# -*- coding: utf-8 -*-
# layout.py, sugar calculator, by:
#   Reinier Heeres <reinier@heeres.eu>
#   Miguel Alvarez <miguel@laptop.org>
#   Aneesh Dogra <lionaneesh@gmail.com>
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

from gettext import gettext as _
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Pango
from sugar3.activity import activity
import sugar3.profile
from sugar3.graphics.combobox import ComboBox
from toolbars import EditToolbar
from toolbars import AlgebraToolbar
from toolbars import TrigonometryToolbar
from toolbars import BooleanToolbar
from toolbars import MiscToolbar

try:
    from sugar3.graphics.toolbarbox import ToolbarButton, ToolbarBox
    from sugar3.activity.widgets import ActivityToolbarButton
    from sugar3.activity.widgets import StopButton
except ImportError:
    pass


class CalcLayout:

    FONT_SMALL_POINTS = 10
    FONT_SMALL = "sans %d" % FONT_SMALL_POINTS
    FONT_SMALL_NARROW = "sans italic %d" % FONT_SMALL_POINTS
    FONT_BIG_POINTS = 14
    FONT_BIG = "sans bold %d" % FONT_BIG_POINTS
    FONT_BIG_NARROW = "sans italic 14"
    FONT_BIGGER_POINTS = 18
    FONT_BIGGER = "sans bold %d" % FONT_BIGGER_POINTS

    def __init__(self, parent):
        self._parent = parent

        self._own_equations = []
        self._other_equations = []
        self._showing_history = True
        self._showing_all_history = True
        self._var_textviews = {}
        self.graph_selected = None

        self.create_dialog()

    def create_color(self, rf, gf, bf):
        return Gdk.Color(int(rf * 0xFFFF), int(gf * 0xFFFF),
                             int(bf * 0xFFFF))

    def create_button_data(self):
        """Create a list with button information. We need to do that here
        because we want to include the lambda functions."""

        mul_sym = self._parent.ml.mul_sym
        div_sym = self._parent.ml.div_sym
        equ_sym = self._parent.ml.equ_sym

        self.button_data = [
            # [x, y, width, label, bgcol, cb]
            [0, 0, 2, 1, u'\u2190', self.col_gray3,
                lambda w: self._parent.move_left()],
            [2, 0, 2, 1, u'\u2192', self.col_gray3,
                lambda w: self._parent.move_right()],
            [4, 0, 2, 1, u'\u232B', self.col_gray3,
                lambda w: self._parent.remove_character(-1)],

            [0, 1, 1, 2, '7', self.col_gray2,
                lambda w: self._parent.add_text('7')],
            [1, 1, 1, 2, '8', self.col_gray2,
                lambda w: self._parent.add_text('8')],
            [2, 1, 1, 2, '9', self.col_gray2,
                lambda w: self._parent.add_text('9')],

            [0, 3, 1, 2, '4', self.col_gray2,
                lambda w: self._parent.add_text('4')],
            [1, 3, 1, 2, '5', self.col_gray2,
                lambda w: self._parent.add_text('5')],
            [2, 3, 1, 2, '6', self.col_gray2,
                lambda w: self._parent.add_text('6')],

            [0, 5, 1, 2, '1', self.col_gray2,
                lambda w: self._parent.add_text('1')],
            [1, 5, 1, 2, '2', self.col_gray2,
                lambda w: self._parent.add_text('2')],
            [2, 5, 1, 2, '3', self.col_gray2,
                lambda w: self._parent.add_text('3')],

            [0, 7, 2, 2, '0', self.col_gray2,
                lambda w: self._parent.add_text('0')],
            [2, 7, 1, 2, '.', self.col_gray2,
                lambda w: self._parent.add_text('.')],

            [3, 1, 3, 2, _('Clear'), self.col_gray1,
             lambda w: self._parent.clear()],

            [3, 3, 1, 2, '+', self.col_gray3,
                lambda w: self._parent.add_text('+')],
            [4, 3, 1, 2, '-', self.col_gray3,
                lambda w: self._parent.add_text('-')],
            [5, 3, 1, 2, '(', self.col_gray3,
             lambda w: self._parent.add_text('(')],
            [3, 5, 1, 2, mul_sym, self.col_gray3,
                lambda w: self._parent.add_text(mul_sym)],
            [4, 5, 1, 2, div_sym, self.col_gray3,
                lambda w: self._parent.add_text(div_sym)],
            [5, 5, 1, 2, ')', self.col_gray3,
                lambda w: self._parent.add_text(')')],

            [3, 7, 3, 2, equ_sym, self.col_gray1,
                lambda w: self._parent.process()],
        ]

    def create_dialog(self):
        """Setup most of the dialog."""

# Toolbar
        self._toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self._parent)
        self._toolbar_box.toolbar.insert(activity_button, 0)
        def append(icon_name, label, page, position):
            toolbar_button = ToolbarButton()
            toolbar_button.props.page = page
            toolbar_button.props.icon_name = icon_name
            toolbar_button.props.label = label
            self._toolbar_box.toolbar.insert(toolbar_button, position)
        append('toolbar-edit',
               _('Edit'),
               EditToolbar(self._parent),
               -1)
        append('toolbar-algebra',
               _('Algebra'),
               AlgebraToolbar(self._parent),
               -1)
        append('toolbar-trigonometry',
               _('Trigonometry'),
               TrigonometryToolbar(self._parent),
               -1)
        append('toolbar-boolean',
               _('Boolean'),
               BooleanToolbar(self._parent),
               -1)
        self._misc_toolbar = MiscToolbar(
            self._parent,
            target_toolbar=self._toolbar_box.toolbar)
        append('toolbar-constants',
               _('Miscellaneous'),
               self._misc_toolbar,
               5)
        self._stop_separator = Gtk.SeparatorToolItem()
        self._stop_separator.props.draw = False
        self._stop_separator.set_expand(True)
        self._stop_separator.show()
        self._toolbar_box.toolbar.insert(self._stop_separator, -1)
        self._stop = StopButton(self._parent)
        self._toolbar_box.toolbar.insert(self._stop, -1)
        self._toolbar_box.show_all()
        self._parent.set_toolbar_box(self._toolbar_box)

# Some layout constants
        self.input_font = Pango.FontDescription('sans bold 12')
        self.button_font = Pango.FontDescription('sans bold 16')
        self.col_white = self.create_color(1.00, 1.00, 1.00)
        self.col_gray1 = self.create_color(0.76, 0.76, 0.76)
        self.col_gray2 = self.create_color(0.50, 0.50, 0.50)
        self.col_gray3 = self.create_color(0.25, 0.25, 0.25)
        self.col_black = self.create_color(0.00, 0.00, 0.00)
        self.col_red = self.create_color(1.00, 0.00, 0.00)

# Big - Table, 16 rows, 10 columns, homogeneously divided
        self.grid = Gtk.Grid()
        self.grid.set_column_homogeneous (True)
        self.grid.set_row_spacing(0)
        self.grid.set_column_spacing(4)

# Left part: container and input
        vc1 = Gtk.VBox(False, 0)
        hc1 = Gtk.HBox(False, 10)
        eb = Gtk.EventBox()
        eb.add(hc1)
        eb.modify_bg(Gtk.StateType.NORMAL, self.col_black)
        eb.set_border_width(12)
        eb2 = Gtk.EventBox()
        eb2.add(eb)
        eb2.modify_bg(Gtk.StateType.NORMAL, self.col_black)
        label1 = Gtk.Label(label=_('Label:'))
        label1.modify_fg(Gtk.StateType.NORMAL, self.col_white)
        label1.set_alignment(1, 0.5)
        hc1.pack_start(label1, expand=False, fill=False, padding=10)
        self.label_entry = Gtk.Entry()
        self.label_entry.modify_bg(Gtk.StateType.INSENSITIVE, self.col_black)
        hc1.pack_start(self.label_entry, expand=True, fill=True, padding=0)
        vc1.pack_start(eb2, False, True, 0)

        self.text_entry = Gtk.Entry()
        try:
            self.text_entry.props.im_module = 'gtk-im-context-simple'
        except AttributeError:
            pass
        self.text_entry.set_size_request(-1, 75)
        self.text_entry.connect('key_press_event', self._parent.ignore_key_cb)
        self.text_entry.modify_font(self.input_font)
        self.text_entry.modify_bg(Gtk.StateType.INSENSITIVE, self.col_black)
        eb = Gtk.EventBox()
        eb.add(self.text_entry)
        eb.modify_bg(Gtk.StateType.NORMAL, self.col_black)
        eb.set_border_width(12)
        eb2 = Gtk.EventBox()
        eb2.add(eb)
        eb2.modify_bg(Gtk.StateType.NORMAL, self.col_black)
        vc1.pack_start(eb2, expand=True, fill=True, padding=0)
        self.grid.attach(vc1, 0, 0, 7, 6)

# Left part: buttons
        self.pad = Gtk.Grid()
        self.pad.set_column_homogeneous (True)
        self.pad.set_row_spacing(6)
        self.pad.set_column_spacing(6)
        self.create_button_data()
        self.buttons = {}
        for x, y, w, h, cap, bgcol, cb in self.button_data:
            button = self.create_button(
                _(cap), cb, self.col_white, bgcol, w, h)
            self.buttons[cap] = button
            self.pad.attach(button, x, y, w, h)

        eb = Gtk.EventBox()
        eb.add(self.pad)
        eb.modify_bg(Gtk.StateType.NORMAL, self.col_black)
        self.grid.attach(eb, 0, 6, 7, 20)

# Right part: container and equation button
        hc2 = Gtk.HBox()
        combo = ComboBox()
        combo.append_item(0, _('All equations'))
        combo.append_item(1, _('My equations'))
        combo.append_item(2, _('Show variables'))
        combo.set_active(0)
        combo.connect('changed', self._history_filter_cb)
        hc2.pack_start(combo, True, True, 0)
        hc2.set_border_width(6)
        self.grid.attach(hc2, 7, 0, 4, 2)

# Right part: last equation
        self.last_eq = Gtk.TextView()
        self.last_eq.set_editable(False)
        self.last_eq.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self.last_eq.connect('realize', self._textview_realize_cb)
        self.last_eq.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse(
                                 sugar3.profile.get_color().get_fill_color()))
        self.last_eq.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(
                               sugar3.profile.get_color().get_stroke_color()))
        self.last_eq.set_border_window_size(Gtk.TextWindowType.LEFT, 4)
        self.last_eq.set_border_window_size(Gtk.TextWindowType.RIGHT, 4)
        self.last_eq.set_border_window_size(Gtk.TextWindowType.TOP, 4)
        self.last_eq.set_border_window_size(Gtk.TextWindowType.BOTTOM, 4)

        # TODO Fix for old Sugar 0.82 builds, red_float not available
        xo_color = sugar3.profile.get_color()
        bright = (
            Gdk.color_parse(xo_color.get_fill_color()).red_float +
            Gdk.color_parse(xo_color.get_fill_color()).green_float +
            Gdk.color_parse(xo_color.get_fill_color()).blue_float) / 3.0
        if bright < 0.5:
            self.last_eq.modify_text(Gtk.StateType.NORMAL, self.col_white)
        else:
            self.last_eq.modify_text(Gtk.StateType.NORMAL, self.col_black)

        self.grid.attach(self.last_eq, 7, 2, 4, 5)

# Right part: history
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)

        self.history_vbox = Gtk.VBox()
        self.history_vbox.set_homogeneous(False)
        self.history_vbox.set_border_width(0)
        self.history_vbox.set_spacing(4)

        self.variable_vbox = Gtk.VBox()
        self.variable_vbox.set_homogeneous(False)
        self.variable_vbox.set_border_width(0)
        self.variable_vbox.set_spacing(4)

        vbox = Gtk.VBox()
        vbox.pack_start(self.history_vbox, True, True, 0)
        vbox.pack_start(self.variable_vbox, True, True, 0)
        scrolled_window.add_with_viewport(vbox)
        self.grid.attach(scrolled_window, 7, 7, 4, 19)

        Gdk.Screen.get_default().connect('size-changed',
                                             self._configure_cb)

    def _configure_cb(self, event):
        # Maybe redo layout
        self._toolbar_box.toolbar.remove(self._stop)
        self._toolbar_box.toolbar.remove(self._stop_separator)
        self._misc_toolbar.update_layout()
        self._toolbar_box.toolbar.insert(self._stop_separator, -1)
        self._toolbar_box.toolbar.insert(self._stop, -1)

    def show_it(self):
        """Show the dialog."""
        self._parent.set_canvas(self.grid)
        self._parent.show_all()
        self.show_history()
        self.text_entry.grab_focus()

    def showing_history(self):
        """Return whether we're currently showing the history (or otherwise
        the list of variables)."""
        return self._showing_history

    def show_history(self):
        """Show the history VBox."""
        self._showing_history = True
        self.variable_vbox.hide()
        self.history_vbox.show()

    def toggle_select_graph(self, widget, host=None):
        # if we have a graph already selected, we must deselect it first
        if self.graph_selected and self.graph_selected != widget:
            self.toggle_select_graph(self.graph_selected)

        if not self.graph_selected:
            widget.set_visible_window(True)
            widget.set_above_child(True)
            self.graph_selected = widget
            white = Gdk.color_parse('white')
            widget.modify_bg(Gtk.StateType.NORMAL, white)
        else:
            widget.set_visible_window(False)
            self.graph_selected = False

    def add_equation(self, textview, own, prepend=False):
        """Add a Gtk.TextView of an equation to the history_vbox."""

        GraphEventBox = None
        if isinstance(textview, Gtk.Image):
            # Add the image inside the eventBox
            GraphEventBox = Gtk.EventBox()
            GraphEventBox.add(textview)
            GraphEventBox.set_visible_window(False)
            GraphEventBox.connect(
                'button_press_event', self.toggle_select_graph)
            GraphEventBox.show()

        if prepend:
            if GraphEventBox:
                self.history_vbox.pack_start(GraphEventBox, False, True, 0)
                self.history_vbox.reorder_child(GraphEventBox, 0)
            else:
                self.history_vbox.pack_start(textview, False, True, 0)
                self.history_vbox.reorder_child(textview, 0)
        else:
            if GraphEventBox:
                self.history_vbox.pack_end(GraphEventBox, False, True)
            else:
                self.history_vbox.pack_end(textview, False, True)

        if own:
            if GraphEventBox:
                self._own_equations.append(GraphEventBox)
                GraphEventBox.get_child().show()
            else:
                self._own_equations.append(textview)
                textview.show()
        else:
            if self._showing_all_history:
                if GraphEventBox:
                    self._other_equations.append(GraphEventBox)
                    GraphEventBox.get_child().show()
                else:
                    self._other_equations.append(textview)
                    textview.show()

    def show_all_history(self):
        """Show both owned and other equations."""
        self._showing_all_history = True
        for key in self._other_equations:
            if isinstance(key, Gtk.EventBox):
                key.get_child().show()
            else:
                key.show()

    def show_own_history(self):
        """Show only owned equations."""
        self._showing_all_history = False
        for key in self._other_equations:
            if isinstance(key, Gtk.EventBox):
                key.get_child().hide()
            else:
                key.hide()

    def add_variable(self, varname, textview):
        """Add a Gtk.TextView of a variable to the variable_vbox."""

        if varname in self._var_textviews:
            self.variable_vbox.remove(self._var_textviews[varname])
            del self._var_textviews[varname]

        self._var_textviews[varname] = textview
        self.variable_vbox.pack_start(textview, False, True, 0)

        # Reorder textviews for a sorted list
        names = self._var_textviews.keys()
        names.sort()
        for i in range(len(names)):
            self.variable_vbox.reorder_child(self._var_textviews[names[i]], i)

        textview.show()

    def show_variables(self):
        """Show the variables VBox."""
        self._showing_history = False
        self.history_vbox.hide()
        self.variable_vbox.show()

    def create_button(self, cap, cb, fgcol, bgcol, width, height):
        """Create a button that is set up properly."""
        button = Gtk.Button(_(cap))
        self.modify_button_appearance(button, fgcol, bgcol, width, height)
        button.connect("clicked", cb)
        button.connect("key_press_event", self._parent.ignore_key_cb)
        return button

    def modify_button_appearance(self, button, fgcol, bgcol, width, height):
        """Modify button style."""
        width = 50 * width
        height = 50 * height
        button.get_child().set_size_request(width, height)
        button.get_child().modify_font(self.button_font)
        button.get_child().modify_fg(Gtk.StateType.NORMAL, fgcol)
        button.modify_bg(Gtk.StateType.NORMAL, bgcol)
        button.modify_bg(Gtk.StateType.PRELIGHT, bgcol)

    def _history_filter_cb(self, combo):
        selection = combo.get_active()
        if selection == 0:
            self.show_history()
            self.show_all_history()
        elif selection == 1:
            self.show_history()
            self.show_own_history()
        elif selection == 2:
            self.show_variables()

    def _textview_realize_cb(self, widget):
        '''Change textview properties once window is created.'''
        win = widget.get_window(Gtk.TextWindowType.TEXT)
        win.set_cursor(Gdk.Cursor.new(Gdk.CursorType.HAND1))
        return False
