#!/usr/bin/env python
# -*- coding: utf-8 -*-

# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2016 execuc                                             *
# *                                                                         *
# *   This file is part of LCInterlocking module.                           *
# *   LCInterlocking module is free software; you can redistribute it and/or*
# *   modify it under the terms of the GNU Lesser General Public            *
# *   License as published by the Free Software Foundation; either          *
# *   version 2.1 of the License, or (at your option) any later version.    *
# *                                                                         *
# *   This module is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
# *   Lesser General Public License for more details.                       *
# *                                                                         *
# *   You should have received a copy of the GNU Lesser General Public      *
# *   License along with this library; if not, write to the Free Software   *
# *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,            *
# *   MA  02110-1301  USA                                                   *
# *                                                                         *
# ***************************************************************************

import FreeCAD
import FreeCADGui
from FreeCAD import Gui
import os, copy
from PySide import QtCore, QtGui
try:
    from PySide.QtGui import QItemSelectionModel
except ImportError:
    from PySide.QtCore import QItemSelectionModel

from panel.partmat import PartsList, CrossPartWidget, Part
from panel.tab import TabsList
from panel import selection
from lasercut.tabproperties import TabProperties
from panel.treeview import TreeModel, TreeItem
from panel.propertieslist import PropertiesList


PREVIEW_NONE = 0
PREVIEW_NORMAL = 1
PREVIEW_FAST = 2


class TreePanel(object):

    def __init__(self, title, obj_join = None): #none to be removed
        self.form = []
        self.obj_join = obj_join
        self.parts = self.obj_join.parts
        if hasattr(self.obj_join, "faces"):
            self.faces = self.obj_join.faces
        else:
            self.faces = PropertiesList()

        if title != "Crosspiece":
            self.partsList = PartsList(Part, self.parts)
        else:
            self.partsList = PartsList(CrossPartWidget, self.parts)
        self.tabsList = TabsList(self.faces)

        self.params_widget = None
        if self.params_widget:
            self.form.append(self.params_widget)
        self.hide_button = None
        self.show_button = None
        self.reset_transparency_button = None
        self.set_transparency_button = None
        self.active_document = self.obj_join.Document#FreeCAD.ActiveDocument
        self.tree_widget = QtGui.QWidget()
        self.tree_widget.setObjectName("TreePanel")
        self.tree_widget.setWindowTitle(title)
        self.tree_vbox = QtGui.QVBoxLayout(self.tree_widget)
        self.form.append(self.tree_widget)
        self.model = TreeModel()
        self.tree_view_widget = QtGui.QTreeView()
        self.tree_view_widget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.tree_view_widget.setModel(self.model)
        self.tree_view_widget.setFixedHeight(250)
        self.selection_model = None
        self.tab_type_box = None
        self.edited_items = []
        self.edit_items_layout = None
        self.init_tree_widget()
        self.preview_doc = None
        self.show_other_state_checkbox = None
        self.other_object_list = []
        self.save_initial_objects()

        for item in self.parts:
            self.model.append_part(item.name, item.label, bool(item.link_name))

        for item in self.faces:
            self.model.append_tab(item.freecad_obj_name, item.tab_name, item.face_name, bool(item.link_name))

    def getStandardButtons(self):
        return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)

    def accept(self):
        raise ValueError("Must overloaded")

    def reject(self):
        raise ValueError("Must overloaded")

    def init_tree_widget(self):
        v_box = QtGui.QVBoxLayout(self.tree_widget)
        preview_button = QtGui.QPushButton('Preview', self.tree_widget)
        preview_button.clicked.connect(self.abs_preview)
        self.fast_preview = QtGui.QCheckBox("Fast preview", self.tree_widget)
        line = QtGui.QFrame(self.tree_widget)
        line.setFrameShape(QtGui.QFrame.HLine);
        line.setFrameShadow(QtGui.QFrame.Sunken);
        h_box = QtGui.QHBoxLayout(self.tree_widget)
        h_box.addWidget(preview_button)
        h_box.addWidget(self.fast_preview)
        v_box.addLayout(h_box)
        v_box.addWidget(line)
        self.tree_vbox.addLayout(v_box)

        # Add part buttons
        h_box = QtGui.QHBoxLayout(self.tree_widget)
        add_parts_button = QtGui.QPushButton('Add parts', self.tree_widget)
        add_parts_button.clicked.connect(self.add_parts)
        add_same_part_button = QtGui.QPushButton('Add same parts', self.tree_widget)
        add_same_part_button.clicked.connect(self.add_same_parts)
        h_box.addWidget(add_parts_button)
        h_box.addWidget(add_same_part_button)
        self.tree_vbox.addLayout(h_box)
        # Add faces buttons
        h_box = QtGui.QHBoxLayout(self.tree_widget)
        self.tab_type_box = QtGui.QComboBox(self.tree_widget)
        self.tab_type_box.addItems([TabProperties.TYPE_TAB, TabProperties.TYPE_T_SLOT,
                                    TabProperties.TYPE_CONTINUOUS])#, TabProperties.TYPE_FLEX])
        h_box.addWidget(self.tab_type_box)
        add_faces_button = QtGui.QPushButton('Add faces', self.tree_widget)
        add_faces_button.clicked.connect(self.add_tabs)
        add_same_faces_button = QtGui.QPushButton('Add same faces', self.tree_widget)
        add_same_faces_button.clicked.connect(self.add_same_tabs)
        h_box.addWidget(add_faces_button)
        h_box.addWidget(add_same_faces_button)
        self.tree_vbox.addLayout(h_box)
        # tree
        self.selection_model = self.tree_view_widget.selectionModel()
        self.selection_model.selectionChanged.connect(self.selection_changed)
        self.tree_vbox.addWidget(self.tree_view_widget)
        remove_item_button = QtGui.QPushButton('Remove item', self.tree_widget)
        remove_item_button.clicked.connect(self.remove_items)
        self.tree_vbox.addWidget(remove_item_button)
        line = QtGui.QFrame(self.tree_widget)
        line.setFrameShape(QtGui.QFrame.HLine)
        line.setFrameShadow(QtGui.QFrame.Sunken)
        self.tree_vbox.addWidget(line)
        # test layout
        self.edit_items_layout = QtGui.QVBoxLayout(self.tree_widget)
        self.tree_vbox.addLayout(self.edit_items_layout)

    def check_parts(self, parts):
        for part in parts:
            if self.partsList.exist(part.Name):
                FreeCAD.Console.PrintError("Part %s is already configured" % part.Name)
                return False
        return True

    def add_parts(self):
        self.check_is_in_active_view()
        parts = selection.get_freecad_objects_list()
        last_index = None
        if not self.check_parts(parts):
            return
        for part in parts:
            try:
                item = self.partsList.append(part)
                last_index = self.model.append_part(item.name, item.label)
            except ValueError as e:
                FreeCAD.Console.PrintError(e)
                return
        self.force_selection(last_index)

    def add_same_parts(self):
        self.check_is_in_active_view()
        freecad_parts = selection.get_freecad_objects_list()
        index = None
        if len(freecad_parts) == 0 or not self.check_parts(freecad_parts):
            return
        try:
            item = self.partsList.append(freecad_parts[0])
            index = self.model.append_part(item.name, item.label)
            for part in freecad_parts[1:]:
                sub_item = self.partsList.append_link(part, freecad_parts[0])
                self.model.append_part(sub_item.name, sub_item.label, True)
        except ValueError as e:
            FreeCAD.Console.PrintError(e)
            return
        self.force_selection(index)
        return

    def remove_items(self):
        indexes = self.tree_view_widget.selectionModel().selectedIndexes()
        if len(indexes) == 0:
            FreeCAD.Console.PrintWarning("Nothing to remove\n")
            return
        parent_test_name = indexes[0].internalPointer().parent().get_name()
        for index in indexes:#[1:]:
            if index.internalPointer().parent().get_name() != parent_test_name:
                FreeCAD.Console.PrintError("No same level delete")
                return False
            elif index.internalPointer().child_count() > 0:
                FreeCAD.Console.PrintError("%s has children" % index.internalPointer().get_name())
                return False
        for index in indexes:
            item = index.internalPointer()
            if item.type == TreeItem.PART and len(self.partsList.get_linked_parts(item.get_name())) > 0:
                FreeCAD.Console.PrintError('Some parts are linked to this part %s\n' % item.get_name())
                return False
            elif item.type == TreeItem.TAB and len(self.tabsList.get_linked_tabs(item.get_name())) > 0:
                FreeCAD.Console.PrintError('Some tabs are linked to this tab %s\n' % item.get_name())
                return False

        for index in indexes:
            item = index.internalPointer()
            if item.type == TreeItem.PART or item.type == TreeItem.PART_LINK:
                self.partsList.remove(item.get_name())
            elif item.type == TreeItem.TAB or item.type == TreeItem.TAB_LINK:
                self.tabsList.remove(item.get_name())
            else:
                FreeCAD.Console.PrintError("Unknown deleter item")
        rows = sorted(set(index.row() for index in indexes))
        for row in reversed(rows):
            self.model.removeRow(row, indexes[0].parent())

        return

    def check_faces(self, faces):
        for face in faces:
            if not self.partsList.exist(face['freecad_object'].Name):
                FreeCAD.Console.PrintError("Part of face %s is not configured" % face['name'])
                return False
            elif self.tabsList.exist(face['name']):
                FreeCAD.Console.PrintError("Face %s already present" % face['name'])
                return False
        return True

    def add_tabs(self):
        self.check_is_in_active_view()
        faces = selection.get_freecad_faces_objects_list()
        last_index = None
        if self.check_faces(faces) is False:
            return
        for face in faces:
            try:
                item = self.tabsList.append(face, self.tab_type_box.currentText())
                last_index = self.model.append_tab(item.freecad_obj_name, item.tab_name, item.face_name)
            except ValueError as e:
                FreeCAD.Console.PrintError(e)
                return
        self.force_selection(last_index)
        return

    def add_same_tabs(self):
        self.check_is_in_active_view()
        faces = selection.get_freecad_faces_objects_list()
        index = None
        if self.check_faces(faces) is False or len(faces) == 0:
            return
        try:
            face = faces[0]
            item = self.tabsList.append(face, self.tab_type_box.currentText())
            index = self.model.append_tab(item.freecad_obj_name, item.tab_name, item.face_name)
            for face in faces[1:]:
                sub_item = self.tabsList.append_link(face, item.tab_name)
                self.model.append_tab(sub_item.freecad_obj_name, sub_item.tab_name, sub_item.face_name, True)

        except ValueError as e:
            FreeCAD.Console.PrintError(e)
            return
        self.force_selection(index)
        return

    def force_selection(self, index):
        self.selection_model.clearSelection()
        self.selection_model.select(index, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)

    def selection_changed(self, selected, deselected):
        FreeCADGui.Selection.clearSelection()
        self.save_items_properties()
        self.edited_items = []
        self.remove_items_widgets()
        indexes = self.tree_view_widget.selectedIndexes()
        tab_indexes = []
        for index in indexes:
            if index.column() > 0:
                continue
            item = index.internalPointer()
            if item.type == TreeItem.TAB or item.type == TreeItem.TAB_LINK:
                tab_indexes.append(index)
                continue
            elif item.type == TreeItem.PART or item.type == TreeItem.PART_LINK :
                part, widget = self.partsList.get(item.get_name())
                fobj = self.active_document.getObject(part.name)
                FreeCADGui.Selection.addSelection(fobj)

                self.edited_items.append(widget)
                groupx_box, grid = widget.get_group_box(self.tree_widget)
                self.edit_items_layout.addWidget(groupx_box)

        for index in tab_indexes:
            if index.column() > 0:
                continue
            item = index.internalPointer()
            tab, widget = self.tabsList.get(item.get_name())
            if tab is None:
                raise ValueError("No tab named %s", item.get_name())
            if widget is None:
                raise ValueError("No widget named %s", item.get_name())
            fobj = self.active_document.getObject(tab.freecad_obj_name)
            FreeCADGui.Selection.addSelection(fobj, tab.face_name)

            self.edited_items.append(widget)
            groupx_box, grid = widget.get_group_box(self.tree_widget)
            self.edit_items_layout.addWidget(groupx_box)

        for index in tab_indexes:
            parent_index = index.parent()
            if parent_index.isValid():
                self.tree_view_widget.setExpanded(parent_index, True)

    def remove_items_widgets(self):
        for cnt in reversed(range(self.edit_items_layout.count())):
            # takeAt does both the jobs of itemAt and removeWidget
            # namely it removes an item and returns it
            widget = self.edit_items_layout.takeAt(cnt).widget()

            if widget is not None:
                # widget will be None if the item is a layout
                widget.deleteLater()

    def save_items_properties(self):
        for item in self.edited_items:
            item.get_properties()

    def init_params(self):
        self.params_widget = QtGui.QWidget()
        self.params_widget.setObjectName("ParamsPanel")
        self.params_widget.setWindowTitle("Parameters")
        self.params_vbox = QtGui.QVBoxLayout(self.params_widget)
        QtGui.QWidget().setLayout(self.params_vbox)
        parts_vbox = QtGui.QGridLayout(self.params_widget)
        self.hide_button = QtGui.QPushButton('Hide others', self.params_widget)
        parts_vbox.addWidget(self.hide_button, 0, 0)
        self.hide_button.clicked.connect(self.hide_others)
        self.show_button = QtGui.QPushButton('Show all', self.params_widget)
        parts_vbox.addWidget(self.show_button, 0, 1)
        self.show_button.clicked.connect(self.show_initial_objects)

        self.set_transparency_button = QtGui.QPushButton('Set transparent', self.params_widget)
        parts_vbox.addWidget(self.set_transparency_button, 1, 0)
        self.set_transparency_button.clicked.connect(self.set_transparency)
        self.reset_transparency_button = QtGui.QPushButton('Restore transparent', self.params_widget)
        parts_vbox.addWidget(self.reset_transparency_button, 1, 1)
        self.reset_transparency_button.clicked.connect(self.restore_transparency)

        preview_button = QtGui.QPushButton('Preview', self.params_widget)
        parts_vbox.addWidget(preview_button,2,0,1,2)
        preview_button.clicked.connect(self.preview)

    def abs_preview(self):
        self.preview(self.fast_preview.isChecked())

    def compute_parts(self):
        raise ValueError("Must overloaded")

    def preview(self, fast=False):
        raise ValueError("Must overloaded")

    def save_initial_objects(self):
        self.other_object_list = []
        objs = self.active_document.Objects
        for obj in objs:
            if obj.ViewObject.isVisible() and hasattr(obj.ViewObject,"Transparency"):
                self.other_object_list.append({'obj':obj, 'transparency':copy.copy(obj.ViewObject.Transparency)})

    def show_initial_objects(self):
        for obj in self.other_object_list:
            freecad_object = obj['obj']
            freecad_object.ViewObject.show()
        return

    def hide_others(self):
        current_obj_list = selection.get_freecad_objects_list()
        object_list = []
        if current_obj_list is None or len(current_obj_list) == 0:
            FreeCAD.Console.PrintError("No object selectionned\n")
            return
        objects = self.active_document.Objects
        for obj in objects:
            freecad_object = obj
            if freecad_object.ViewObject.isVisible() and freecad_object not in current_obj_list:
                freecad_object.ViewObject.hide()

    def restore_transparency(self):
        for obj in self.other_object_list:
            freecad_object = obj['obj']
            freecad_object.ViewObject.Transparency = obj['transparency']

    def set_transparency(self):
        for obj in self.other_object_list:
            freecad_object = obj['obj']
            freecad_object.ViewObject.Transparency = 90

    def check_is_in_active_view(self):
        if self.active_document != FreeCAD.ActiveDocument:
            raise ValueError("You have to select original document")
        return True

    def save_link_properties(self):
        self.partsList.get_parts_properties()
        self.tabsList.get_tabs_properties()