# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline 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.
#
# Timeline 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 Timeline.  If not, see <http://www.gnu.org/licenses/>.


import wx


from timelinelib.canvas import TimelineCanvas
from timelinelib.wxgui.cursor import Cursor
from timelinelib.wxgui.keyboard import Keyboard
from timelinelib.wxgui.components.maincanvas.inputhandler import InputHandler
from timelinelib.general.methodcontainer import MethodContainer
from timelinelib.canvas.timelinecanvas import LEFT_RESIZE_HANDLE
from timelinelib.canvas.timelinecanvas import RIGHT_RESIZE_HANDLE
from timelinelib.canvas.timelinecanvas import MOVE_HANDLE


class MainCanvas(TimelineCanvas):

    def __init__(self, parent, main_frame):
        self._main_frame = main_frame
        TimelineCanvas.__init__(self, parent, main_frame.config.debug_enabled, main_frame.DisplayStatus)
        self.SetInputHandler(InputHandler(self))
        self.dragscroll_timer = wx.Timer(self, -1)
        self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down)
        self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_dclick)
        self.Bind(wx.EVT_LEFT_UP, self._on_left_up)
        self.Bind(wx.EVT_MOTION, self._on_motion)
        self.Bind(wx.EVT_TIMER, self._on_dragscroll, self.dragscroll_timer)
        self.Bind(wx.EVT_MIDDLE_DOWN, self._on_middle_down)
        self.Bind(wx.EVT_MOUSEWHEEL, self._on_mousewheel)
        main_frame.config.listen_for('real_time_redraw_interval', self._controller.redraw_time_interval_has_changed)
        main_frame.config.listen_for('real_time_redraw_interval_navigate', self._controller.redraw_time_interval_navigate)

    @property
    def main_frame(self):
        return self._main_frame

    def SetInputHandler(self, input_handler):
        self._input_handler = input_handler

    def StartDragscrollTimer(self, milliseconds=-1, oneShot=False):
        self.dragscroll_timer.Start(milliseconds, oneShot)

    def StopDragscrollTimer(self):
        self.dragscroll_timer.Stop()

    def ToggleEventSelectionState(self, cursor, keyboard):

        def toggle_event_selection_when_event_is_hit(event):
            selected = not self.IsEventSelected(event)
            if keyboard.ctrl:
                self.SetEventSelected(event, selected)
            else:
                self.ClearSelectedEvents()
                self.SetEventSelected(event, selected)
        event = self.GetEventAt(cursor, keyboard.alt)
        if event:
            toggle_event_selection_when_event_is_hit(event)
        else:
            self.ClearSelectedEvents()

    def ToggleBalloonStickyness(self, event_with_balloon):
        stick = not self.EventHasStickyBalloon(event_with_balloon)
        self.SetEventStickyBalloon(event_with_balloon, stick)
        if stick:
            self.Redraw()
        else:
            if self.GetAppearance().get_balloons_visible():
                self.SetHoveredEvent(event_with_balloon)
            else:
                self.SetHoveredEvent(None)

    def HitResizeHandle(self, cursor, keyboard):
        """
        Return the value wx.LEFT if the cursor hits en events left resize point.
        Return the value wx.RIGHT if the cursor hits en events right resize point.
        If the event is not resizeable or if it is a milestone event the function
        returns None.
        """
        try:
            event, hit_info = self.GetEventWithHitInfoAt(cursor)
            if event.get_locked():
                return None
            if event.is_milestone():
                return None
            if not self.IsEventSelected(event):
                return None
            if hit_info == LEFT_RESIZE_HANDLE:
                return wx.LEFT
            if hit_info == RIGHT_RESIZE_HANDLE:
                return wx.RIGHT
            return None
        except:
            return None

    def HitMoveHandle(self, cursor):
        """
        Return the value MOVE_HANDLE if the cursor hits en events move point, and if the event is moveable.
        Otherwise the function returns False.
        """
        event_and_hit_info = self.GetEventWithHitInfoAt(cursor)
        if event_and_hit_info is None:
            return False
        (event, hit_info) = event_and_hit_info
        if event.get_locked():
            return False
        if not self.IsEventSelected(event):
            return False
        return hit_info == MOVE_HANDLE

    def _on_left_down(self, evt):
        self._main_frame.canvas.SaveTimePeriod()
        self.SetFocus()
        self._input_handler.left_mouse_down(self._get_cursor(evt),
                                            self._get_keyboard(evt))

    def _on_left_dclick(self, evt):
        """
        Event handler used when the left mouse button has been double clicked.

        If the timeline is readonly, no action is taken.
        If the mouse hits an event, a dialog opens for editing this event.
        Otherwise a dialog for creating a new event is opened.
        """
        if self.IsReadOnly():
            return
        # Since the event sequence is, 1. EVT_LEFT_DOWN  2. EVT_LEFT_UP
        # 3. EVT_LEFT_DCLICK we must compensate for the toggle_event_selection
        # that occurs in the handling of EVT_LEFT_DOWN, since we still want
        # the event(s) selected or deselected after a left doubleclick
        # It doesn't look too god but I havent found any other way to do it.
        self.ToggleEventSelection(evt)

    def _get_cursor(self, evt):
        return Cursor(evt.GetX(), evt.GetY())

    def _get_keyboard(self, evt):
        return Keyboard(evt.ControlDown(), evt.ShiftDown(), evt.AltDown())

    def _on_left_up(self, event):
        self._input_handler.left_mouse_up()

    def _on_motion(self, evt):
        try:
            self.DisplayBalloons(evt)
            self._main_frame.DisplayStatus(self.GetTimelineInfoText(evt))
            self.SetCursorShape(evt)
            self._input_handler.mouse_moved(self._get_cursor(evt), self._get_keyboard(evt))
        except ValueError as ex:
            self._main_frame.DisplayStatus(str(ex))

    def _on_dragscroll(self, event):
        self._input_handler.dragscroll_timer_fired()

    def _on_middle_down(self, evt):
        self.CenterAtCursor(evt)

    def _on_mousewheel(self, evt):
        self._main_frame.canvas.SaveTimePeriod()

        keyboard = self._get_keyboard(evt)
        methods = MethodContainer(
            [
                (Keyboard.CTRL, self.ZoomHorizontallyOnMouseWheel),
                (Keyboard.SHIFT + Keyboard.CTRL, self.SpecialScrollVerticallyOnMouseWheel),
                (Keyboard.SHIFT, self.ScrollVerticallyOnMouseWheel),
                (Keyboard.ALT, self.ZoomVerticallyOnMouseWheel),
            ], default_method=self.ScrollHorizontallyOnMouseWheel
        )
        methods.select(keyboard.keys_combination)(evt)


def step_function(x_value):
    y_value = 0
    if x_value < 0:
        y_value = -1
    elif x_value > 0:
        y_value = 1
    return y_value
