# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2018 Yorik van Havre <yorik@uncreated.net>              *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   This program 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 Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************


"""The BIM Box command"""

import os
import FreeCAD
import FreeCADGui

QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP
translate = FreeCAD.Qt.translate


class BIM_Box:
    def GetResources(self):
        return {
            "Pixmap": "BIM_Box",
            "MenuText": QT_TRANSLATE_NOOP("BIM_Box", "Box"),
            "ToolTip": QT_TRANSLATE_NOOP(
                "BIM_Box", "Graphically creates a generic box in the current document"
            ),
        }

    def IsActive(self):
        v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph")
        return v

    def Activated(self):
        import draftguitools.gui_trackers as DraftTrackers

        # here we will store our points
        self.points = []
        # we build a special cube tracker which is a list of 4 rectangle trackers
        self.cubetracker = []
        self.LengthValue = 0
        self.WidthValue = 0
        self.HeightValue = 0
        self.currentpoint = None
        for i in range(4):
            self.cubetracker.append(DraftTrackers.rectangleTracker())
        if hasattr(FreeCADGui, "Snapper"):
            FreeCADGui.Snapper.getPoint(
                callback=self.PointCallback,
                movecallback=self.MoveCallback,
                extradlg=self.taskbox(),
            )

    def MoveCallback(self, point, snapinfo):
        import DraftGeomUtils
        import DraftVecUtils

        self.currentpoint = point
        if len(self.points) == 1:
            # we have the base point already
            self.Length.setText(
                FreeCAD.Units.Quantity(
                    self.points[-1].sub(point).Length,  FreeCAD.Units.Length
                ).UserString
            )
            self.Length.selectAll()
            self.Length.setFocus()
        elif len(self.points) == 2:
            # now we already have our base line, we update the 1st rectangle
            p = point
            v1 = point.sub(self.points[1])
            v4 = v1.cross(self.points[1].sub(self.points[0]))
            if v4 and v4.Length:
                n = (self.points[1].sub(self.points[0])).cross(v4)
                if n and n.Length:
                    n = DraftVecUtils.project(v1, n)
                    p = self.points[1].add(n)
            self.cubetracker[0].p3(p)
            self.Width.setText(
                FreeCAD.Units.Quantity(
                    self.cubetracker[0].getSize()[1], FreeCAD.Units.Length
                ).UserString
            )
            self.Width.selectAll()
            self.Width.setFocus()
        elif len(self.points) == 3:
            h = DraftGeomUtils.distance_to_plane(point, self.cubetracker[0].p3(), self.normal)
            w = self.normal * h
            # then we update all rectangles
            self.cubetracker[1].p3((self.cubetracker[0].p2()).add(w))
            self.cubetracker[2].p3((self.cubetracker[0].p4()).add(w))
            self.cubetracker[3].p1((self.cubetracker[0].p1()).add(w))
            self.cubetracker[3].p3((self.cubetracker[0].p3()).add(w))
            self.Height.setText(FreeCAD.Units.Quantity(h, FreeCAD.Units.Length).UserString)
            self.Height.selectAll()
            self.Height.setFocus()

    def PointCallback(self, point, snapinfo):
        if not point:
            # cancelled
            self._finish()
            return

        if len(self.points) == 0:
            # this is our first clicked point, nothing to do just yet
            self.points.append(point)
            FreeCADGui.Snapper.getPoint(
                last=point,
                callback=self.PointCallback,
                movecallback=self.MoveCallback,
                extradlg=self.taskbox(),
            )
        elif len(self.points) == 1:
            # this is our second point
            baseline = point.sub(self.points[0])
            self.points.append(point)
            self._setupForWidthInput(baseline)
        elif len(self.points) == 2:
            # this is our third point
            self.points.append(point)
            self._setupForHeightInput()
        elif len(self.points) == 3:
            # finally we have all our points. Let's create the actual cube
            self._makeBox()
            self._finish()

    def taskbox(self):
        "sets up a taskbox widget"

        from PySide import QtCore, QtGui

        wid = QtGui.QWidget()
        ui = FreeCADGui.UiLoader()
        wid.setWindowTitle(translate("BIM", "Box dimensions"))
        grid = QtGui.QGridLayout(wid)

        label1 = QtGui.QLabel(translate("BIM", "Length"))
        self.Length = ui.createWidget("Gui::InputField")
        l = FreeCAD.Units.Quantity(self.LengthValue, FreeCAD.Units.Length)
        self.Length.setText(l.UserString)
        grid.addWidget(label1, 0, 0, 1, 1)
        grid.addWidget(self.Length, 0, 1, 1, 1)
        if self.LengthValue:
            self.Length.setEnabled(False)

        label2 = QtGui.QLabel(translate("BIM", "Width"))
        self.Width = ui.createWidget("Gui::InputField")
        l = FreeCAD.Units.Quantity(self.WidthValue, FreeCAD.Units.Length)
        self.Width.setText(l.UserString)
        grid.addWidget(label2, 1, 0, 1, 1)
        grid.addWidget(self.Width, 1, 1, 1, 1)
        if self.WidthValue or (not self.LengthValue):
            self.Width.setEnabled(False)

        label3 = QtGui.QLabel(translate("BIM", "Height"))
        self.Height = ui.createWidget("Gui::InputField")
        l = FreeCAD.Units.Quantity(self.HeightValue, FreeCAD.Units.Length)
        self.Height.setText(l.UserString)
        grid.addWidget(label3, 2, 0, 1, 1)
        grid.addWidget(self.Height, 2, 1, 1, 1)
        if not self.WidthValue:
            self.Height.setEnabled(False)

        self.Length.valueChanged.connect(self.setLength)
        self.Width.valueChanged.connect(self.setWidth)
        self.Height.valueChanged.connect(self.setHeight)
        self.Length.returnPressed.connect(self.setLengthUI)
        self.Width.returnPressed.connect(self.setWidthUI)
        self.Height.returnPressed.connect(self.setHeightUI)
        return wid

    def setLength(self, d):
        self.LengthValue = d

    def setWidth(self, d):
        self.WidthValue = d

    def setHeight(self, d):
        self.HeightValue = d

    def setLengthUI(self):
        if (len(self.points) == 1) and self.currentpoint and self.LengthValue:
            baseline = self.currentpoint.sub(self.points[0])
            baseline.normalize()
            baseline.multiply(self.LengthValue)
            p2 = self.points[0].add(baseline)
            self.points.append(p2)
            self._setupForWidthInput(baseline)

    def setWidthUI(self):
        if (len(self.points) == 2) and self.currentpoint and self.WidthValue:
            self.normal = self.cubetracker[0].getNormal()
            if self.normal:
                n = (self.points[1].sub(self.points[0])).cross(self.normal)
                if n and n.Length:
                    n.normalize()
                    n.multiply(self.WidthValue)
                    p2 = self.points[1].add(n)
            self.cubetracker[0].p3(p2)
            self.points.append(p2)
            self._setupForHeightInput()

    def setHeightUI(self):
        if (len(self.points) == 3) and self.HeightValue:
            self._makeBox()
            self._finish()

    def _setupForWidthInput(self, baseline):
        # we turn on only one of the rectangles
        self.cubetracker[0].setPlane(baseline)
        self.cubetracker[0].p1(self.points[0])
        self.cubetracker[0].on()
        FreeCADGui.Snapper.getPoint(
            last=self.points[-1],
            callback=self.PointCallback,
            movecallback=self.MoveCallback,
            extradlg=self.taskbox(),
        )

    def _setupForHeightInput(self):
        # we can get the cubes Z axis from our first rectangle
        self.normal = self.cubetracker[0].getNormal()
        # we can therefore define the (u,v) planes of all rectangles
        u = self.cubetracker[0].u
        v = self.cubetracker[0].v
        self.cubetracker[1].setPlane(u, self.normal)
        self.cubetracker[2].setPlane(u, self.normal)
        self.cubetracker[3].setPlane(u, v)
        # and the origin points of the vertical rectangles
        self.cubetracker[1].p1(self.cubetracker[0].p1())
        self.cubetracker[2].p1(self.cubetracker[0].p3())
        # finally we turn all rectangles on
        for r in self.cubetracker:
            r.on()
        if hasattr(FreeCAD, "DraftWorkingPlane"):
            FreeCAD.DraftWorkingPlane.save()
            FreeCAD.DraftWorkingPlane.position = self.cubetracker[0].p3()
            FreeCAD.DraftWorkingPlane.u = (
                self.cubetracker[0].p3().sub(self.cubetracker[0].p4())
            ).normalize()
            FreeCAD.DraftWorkingPlane.v = self.normal
            FreeCAD.DraftWorkingPlane.axis = (
                self.cubetracker[0].p2().sub(self.cubetracker[0].p3())
            ).normalize()
            FreeCADGui.Snapper.setGrid()
        FreeCADGui.Snapper.getPoint(
            last=self.cubetracker[0].p3(),
            callback=self.PointCallback,
            movecallback=self.MoveCallback,
            extradlg=self.taskbox(),
        )

    def _makeBox(self):
        import DraftGeomUtils

        p1 = self.cubetracker[0].p1()
        p2 = self.cubetracker[0].p2()
        p3 = self.cubetracker[0].p4()
        pla = DraftGeomUtils.placement_from_points(p1, p2, p3)
        if self.normal.isEqual(pla.Rotation.multVec(FreeCAD.Vector(0, 0, 1)), 1e-6):
            if self.HeightValue < 0.0:
                pla = DraftGeomUtils.placement_from_points(p1, p3, p2)
                self.LengthValue, self.WidthValue = self.WidthValue, self.LengthValue
        else:
            if self.HeightValue > 0.0:
                pla = DraftGeomUtils.placement_from_points(p1, p3, p2)
                self.LengthValue, self.WidthValue = self.WidthValue, self.LengthValue
        doc = FreeCAD.ActiveDocument
        doc.openTransaction(translate("Arch","Create Box"))
        cube = doc.addObject("Part::Box", "Cube")
        cube.Placement = pla
        cube.Length = self.LengthValue
        cube.Width = self.WidthValue
        cube.Height = abs(self.HeightValue)
        doc.commitTransaction()
        doc.recompute()

    def _finish(self):
        FreeCADGui.Snapper.getPoint()
        FreeCADGui.Snapper.off()
        for c in self.cubetracker:
            c.finalize()
        if hasattr(FreeCADGui, "draftToolBar"):
            FreeCADGui.draftToolBar.offUi()
        if hasattr(FreeCAD, "DraftWorkingPlane"):
            FreeCAD.DraftWorkingPlane.restore()
        FreeCADGui.Snapper.setGrid()


FreeCADGui.addCommand("BIM_Box", BIM_Box())
