#!/usr/bin/env python3
# vim: sts=4 sw=4 et
# GladeVcp MDI history widget
#
# Copyright (c) 2011  Pavel Shramov <shramov@mexmat.net>
#
# 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.

import os

import gi
gi.require_version("Gtk","3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from gi.repository import Pango

if __name__ == "__main__":
    from hal_widgets import _HalWidgetBase
    from hal_actions import _EMC_ActionBase, ensure_mode
else:
    from .hal_widgets import _HalWidgetBase
    from .hal_actions import _EMC_ActionBase, ensure_mode

import linuxcnc
from hal_glib import GStat
from gladevcp.core import Info

GSTAT = GStat()
INFO = Info()

# Set up logging
from qtvcp import logger
LOG = logger.getLogger(__name__)
# LOG.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL, VERBOSE

# path to TCL for external programs eg. halshow
try:
    TCLPATH = os.environ['LINUXCNC_TCL_DIR']
except:
    pass

import gettext             # to extract the strings to be translated

class EMC_MDIHistory(Gtk.VBox, _EMC_ActionBase):
    '''
    EMC_MDIHistory will store each MDI command to a file on your hard drive
    and display the grabbed commands in a treeview so they can be used again
    without typing the complete command again
    '''

    __gtype_name__ = 'EMC_MDIHistory'
    __gproperties__ = {
        'font_size_tree' : (GObject.TYPE_INT, 'Font Size', 'The font size of the tree view text',
                    8, 96, 10, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT),
        'font_size_entry' : (GObject.TYPE_INT, 'Font Size', 'The font size of the entry text',
                    8, 96, 10, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT),
        'use_double_click' : (GObject.TYPE_BOOLEAN, 'Enable submit a command using a double click', 'A double click on an entry will submit the selected command directly\nIt is not recommended to use this on real machines',
                    False, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT),
    }
    __gproperties = __gproperties__

    __gsignals__ = {
                    'exit': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()),
                   }

    def __init__(self, *a, **kw):
        Gtk.VBox.__init__(self, *a, **kw)

        self.use_double_click = False
        self.filename = os.path.expanduser(INFO.MDI_HISTORY_PATH)

        self.model = Gtk.ListStore(str)

        self.tv = Gtk.TreeView()
        self.default_font = self.tv.get_style().font_desc.to_string()
        self.tv.modify_font(Pango.FontDescription(self.default_font))
        self.tv.set_model(self.model)
        self.cell = Gtk.CellRendererText()

        self.col = Gtk.TreeViewColumn(_("Command"))
        self.col.pack_start(self.cell, True)
        self.col.add_attribute(self.cell, 'text', 0)

        self.tv.append_column(self.col)
        self.tv.set_search_column(0)
        self.tv.set_reorderable(False)
        self.tv.set_headers_visible(True)
        self.tv.get_selection().set_mode(Gtk.SelectionMode.NONE)

        scroll = Gtk.ScrolledWindow()
        scroll.add(self.tv)
        scroll.props.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC
        scroll.props.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC

        self.entry = Gtk.Entry()
        self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "gtk-ok")
        self.entry.modify_font(Pango.FontDescription(self.default_font))

        self.entry.connect('activate', self.submit)
        self.entry.connect('icon-press', self.submit)
        self.tv.connect('cursor-changed', self.select)
        self.tv.connect('key_press_event', self.on_key_press_event)
        self.connect('key_press_event', self.on_key_press_event)
        self.tv.connect('button_press_event', self.on_button_press_event)

        self.pack_start(scroll, True, True, 0)
        self.pack_start(self.entry, False, False, 0)
        GSTAT.connect('state-off', lambda w: self.set_sensitive(False))
        GSTAT.connect('state-estop', lambda w: self.set_sensitive(False))
        GSTAT.connect('interp-idle', lambda w: self.set_sensitive(self.machine_on()))
        GSTAT.connect('interp-run', lambda w: self.set_sensitive(not self.is_auto_mode()))
        GSTAT.connect('all-homed', lambda w: self.set_sensitive(self.machine_on()))
        # this time lambda with two parameters, as not all homed will send also the unhomed joints
        GSTAT.connect('not-all-homed', lambda w,uj: self.set_sensitive(INFO.NO_HOME_REQUIRED) )
        self.reload()
        self.show_all()
        
    def reload(self):
        self.model.clear()

        try:
            fp = open(self.filename)
        except:
            return
        lines = map(str.strip, fp.readlines())
        fp.close()

        lines = filter(bool, lines)
        for l in lines:
            self.model.append((l,))
        #path = (len(list(lines))-1,)
        path = 0 #TODO: breaks the functionality
        self.tv.scroll_to_cell(path)
        self.tv.set_cursor(path)
        self.entry.set_text('')

    def submit(self, *a):
        cmd = self.entry.get_text()
        if cmd == 'HALMETER' or cmd == 'halmeter':
            self.load_halmeter()
            return
        elif cmd == 'STATUS' or cmd == 'status':
            self.load_status()
            return
        elif cmd == 'HALSHOW' or cmd == 'halshow':
            self.load_halshow()
            return
        if not cmd:
            return
        ensure_mode(GSTAT.stat, self.linuxcnc, linuxcnc.MODE_MDI)

        self.linuxcnc.mdi(cmd)
        self.entry.set_text('')
        self.entry.grab_focus()

        add_to_file = True
        # we need to put this in a try, because if the hal mdi history file is
        # empty, the model is empty and we will get an None iter!
        try:
            actual = self.tv.get_cursor()[0]
            iter = self.model.get_iter(actual)
            old_cmd = self.model.get_value(iter,0)

            lastiter = self._get_iter_last(self.model)
            len = int(self.model.get_string_from_iter(lastiter))

            if actual[0] == len and old_cmd == cmd:
                add_to_file = False
        except:
            pass

        if add_to_file:
            try:
                fp = open(self.filename, 'a')
                fp.write(cmd + "\n")
                fp.close()
            except:
                pass

            last = self.model.append((cmd,))
            path = self.model.get_path(last)
            self.tv.scroll_to_cell(path)
            self.tv.set_cursor(path)
            self.entry.set_text('')
            self.entry.grab_focus()
        self.tv.get_selection().set_mode(Gtk.SelectionMode.NONE)

    def select(self, w):
        idx = w.get_cursor()[0]
        if idx is None:
            return True
        self.entry.set_text(self.model[idx][0])
        self.entry.grab_focus()
        self.entry.set_position(-1)

    def on_key_press_event(self,w,event):
        # get the keyname
        keyname = Gdk.keyval_name(event.keyval)
#        print(keyname)
        idx = self.tv.get_cursor()[0]
        if idx is None:
            return True

        lastiter = self._get_iter_last(self.model)
        len = int(self.model.get_string_from_iter(lastiter))

        #if nothing is selected, we need to activate the last one on up key
        selection = self.tv.get_selection()
        _, selected = selection.get_selected()


        if keyname == 'Up':
            self.tv.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
            if not selected:
                self.tv.set_cursor(len)
            else:
                if idx[0] > 0:
                    self.tv.set_cursor(idx[0] - 1)
                else:
                    self.tv.set_cursor(idx[0])
            return True

        if keyname == 'Down':
            if not selected:
                return True
            self.tv.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
            if idx[0] < len:
                self.tv.set_cursor(idx[0] + 1)
            else:
                self.tv.set_cursor(idx[0])
                self.entry.set_text('')
                self.entry.grab_focus()
                self.tv.get_selection().set_mode(Gtk.SelectionMode.NONE)
            return True

        if keyname == 'Escape':
            self.entry.set_text('')
            self.entry.grab_focus()
            self.tv.get_selection().set_mode(Gtk.SelectionMode.NONE)

    def on_button_press_event(self,w,event):
        idx = w.get_cursor()[0]
        if idx is None:
            return True
        self.tv.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
        self.entry.set_text(self.model[idx][0])
        self.entry.grab_focus()
        self.entry.set_position(-1)
        if event.type == Gdk.EventType._2BUTTON_PRESS:
            print("Double Click", self.use_double_click)
            if self.use_double_click:
                self.submit()

    def load_halmeter(self):
        p = os.popen("halmeter &")
    def load_status(self):
        p = os.popen("linuxcnctop  > /dev/null &","w")
    def load_halshow(self):
        try:
            p = os.popen("tclsh %s/bin/halshow.tcl &" % (TCLPATH))
        except:
            self.entry.set_text(_("ERROR loading halshow"))

    def _get_iter_last(self, model):
        itr = model.get_iter_first()
        last = None
        while itr:
            last = itr
            itr = model.iter_next(itr)
        return last

    def _change_font_entry(self, value):
        font = self.default_font.split()[0]
        new_font = font +" " + str(value)
        self.entry.modify_font(Pango.FontDescription(new_font))

    def _change_font_tree(self, value):
        font = self.default_font.split()[0]
        new_font = font +" " + str(value)
        self.tv.modify_font(Pango.FontDescription(new_font))

    # Get property
    def do_get_property(self, property):
        name = property.name.replace('-', '_')
        if name in self.__gproperties.keys():
            return getattr(self, name)
        else:
            raise AttributeError('unknown property %s' % property.name)

    # Set property
    def do_set_property(self, property, value):
        try:
            name = property.name.replace('-', '_')
            if name in self.__gproperties.keys():
                setattr(self, name, value)
                self.queue_draw()
                if name == "font_size_tree":
                    self._change_font_tree(value)
                if name == "font_size_entry":
                    self._change_font_entry(value)
                if name == "use_double_click":
                    self.use_double_click = value
            else:
                raise AttributeError('unknown property %s' % property.name)
        except:
            pass

# for testing without glade editor or LinuxCNC not running:
def main():
    window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
    mdi = EMC_MDIHistory()
    mdi.set_property("font_size_tree", 12)
    mdi.set_property("font_size_entry", 20)
    mdi.set_property("use_double_click", True)
    window.add(mdi)
    window.connect("destroy", Gtk.main_quit)
    window.set_size_request(250, 400)
    window.show_all()
    Gtk.main()

if __name__ == "__main__":
    main()
