# -*- coding: utf-8 -*-
"""
$Id$

Copyright 2011 Lars Kruse <devel@sumpfralle.de>

This file is part of PyCAM.

PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PyCAM 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with PyCAM.  If not, see <http://www.gnu.org/licenses/>.
"""

import pycam.Plugins


class Units(pycam.Plugins.PluginBase):

    UI_FILE = "units.ui"
    CATEGORIES = ["System"]

    def setup(self):
        if self.gui:
            self._gtk_handlers = []
            unit_pref_box = self.gui.get_object("UnitPrefBox")
            unit_pref_box.unparent()
            self.core.register_ui("preferences_general", "Units",
                    unit_pref_box, 20)
            # unit control (mm/inch)
            unit_field = self.gui.get_object("unit_control")
            self._gtk_handlers.append((unit_field, "changed",
                    self.change_unit_init))
            def set_unit(text):
                unit_field.set_active(0 if text == "mm" else 1)
                self._last_unit = text
            self.core.add_item("unit", unit_field.get_active_text, set_unit)
            # other plugins should use "unit_string" for human readable output
            self.core.add_item("unit_string", unit_field.get_active_text)
            self._gtk_handlers.extend((
                    (self.gui.get_object("UnitChangeSelectAll"), "clicked",
                        self.change_unit_set_selection, True),
                    (self.gui.get_object("UnitChangeSelectNone"), "clicked",
                        self.change_unit_set_selection, False)))
            # "unit change" window
            self.unit_change_window = self.gui.get_object("UnitChangeDialog")
            self._gtk_handlers.extend((
                    (self.gui.get_object("UnitChangeApply"), "clicked",
                        self.change_unit_apply),
                    (self.unit_change_window, "delete_event",
                        self.change_unit_apply, False)))
            self.register_gtk_handlers(self._gtk_handlers)
        self.register_state_item("settings/unit", lambda: self.core.get("unit"),
                lambda value: self.core.set("unit", value))
        return True

    def teardown(self):
        self.clear_state_items()
        if self.gui:
            self.core.unregister_ui("preferences_general",
                    self.gui.get_object("UnitPrefBox"))
            self.unregister_gtk_handlers(self._gtk_handlers)
            # TODO: reset setting "unit" back to a default value?

    def change_unit_init(self, widget=None):
        new_unit = self.gui.get_object("unit_control").get_active_text()
        if self._last_unit is None:
            # first initialization
            self._last_unit = new_unit
            return
        if self._last_unit == new_unit:
            # don't show the dialog if the conversion would make no sense
            return
        # show a dialog asking for a possible model scaling due to the unit change
        self.unit_change_window.show()

    def change_unit_set_selection(self, widget, state):
        for key in ("UnitChangeModel", "UnitChangeProcesses", "UnitChangeTools",
                "UnitChangeBounds"):
            self.gui.get_object(key).set_active(state)

    def change_unit_apply(self, widget=None, data=None, apply_scale=True):
        # TODO: move tool/process/task related code to these plugins
        new_unit = self.gui.get_object("unit_control").get_active_text()
        factors = {
                ("mm", "inch"): 1 / 25.4,
                ("inch", "mm"): 25.4,
        }
        conversion = (self._last_unit, new_unit)
        if conversion in factors.keys():
            factor = factors[conversion]
            if apply_scale:
                if self.gui.get_object("UnitChangeModel").get_active():
                    # transform the model if it is selected
                    # keep the original center of the model
                    self.core.emit_event("model-change-before")
                    models = self.core.get("models")
                    progress = self.core.get("progress")
                    progress.disable_cancel()
                    progress.set_multiple(len(models), "Scaling model")
                    for model in models:
                        new_x, new_y, new_z = ((model.maxx + model.minx) / 2,
                                (model.maxy + model.miny) / 2,
                                (model.maxz + model.minz) / 2)
                        model.scale(factor, callback=progress.update)
                        cur_x, cur_y, cur_z = self._get_model_center()
                        model.shift(new_x - cur_x, new_y - cur_y,
                                new_z - cur_z,
                                callback=progress.update)
                        progress.update_multiple()
                    progress.finish()
                if self.gui.get_object("UnitChangeProcesses").get_active():
                    # scale the process settings
                    for process in self.core.get("processes"):
                        for key in ("MaterialAllowanceControl",
                                "MaxStepDownControl",
                                "EngraveOffsetControl"):
                            process[key] *= factor
                if self.gui.get_object("UnitChangeBounds").get_active():
                    # scale the boundaries and keep their center
                    for bounds in self.core.get("bounds"):
                        low, high = bounds.get_bounds()
                        if bounds.get_type() == Bounds.TYPE_FIXED_MARGIN:
                            low[0] *= factor
                            high[0] *= factor
                            low[1] *= factor
                            high[1] *= factor
                            low[2] *= factor
                            high[2] *= factor
                            bounds.set_bounds(low, high)
                        elif bounds.get_type() == Bounds.TYPE_CUSTOM:
                            center = [0, 0, 0]
                            for i in range(3):
                                center[i] = (high[i] + low[i]) / 2
                            for i in range(3):
                                low[i] = center[i] + (low[i] - center[i]) * factor
                                high[i] = center[i] + (high[i] - center[i]) * factor
                            bounds.set_bounds(low, high)
                        elif bounds.get_type() == Bounds.TYPE_RELATIVE_MARGIN:
                            # no need to change relative margins
                            pass
                if self.gui.get_object("UnitChangeTools").get_active():
                    # scale all tool dimensions
                    for tool in self.core.get("tools"):
                        for key in ("tool_radius", "torus_radius"):
                            # TODO: fix this invalid access
                            tool[key] *= factor
        self.unit_change_window.hide()
        # store the current unit (for the next run of this function)
        self._last_unit = new_unit
        # update all labels containing the unit size
        self.update_unit_labels()
        # redraw the model
        self.core.emit_event("model-change-after")

    def update_unit_labels(self, widget=None, data=None):
        # don't use the "unit" setting, since we need the plural of "inch"
        if self.core.get("unit") == "mm":
            base_unit = "mm"
        else:
            base_unit = "inches"
        for key in ("SpeedUnit2", ):
            self.gui.get_object(key).set_text("%s/minute" % base_unit)
        for key in ("LengthUnit1", "LengthUnit2", "LengthUnitTouchOffHeight"):
            self.gui.get_object(key).set_text(base_unit)