# vim: ts=4 et sts=4 sw=4 autoindent

import gtk
import Pyro.core

# Searching function
from Application import treeview_visible_function
from sndcs_client.Config import config
from sndcs_common.TrueAndFalseMixin import TrueAndFalseMixin
from sndcs_common.PyroProxyMixin import PyroProxyMixin
from sndcs_common.SndcsExceptions import *

from sndcs_common.Logger import logger
log = logger.getLogger("sndcs_gtk")

class ActiveEmployeesTreeView(TrueAndFalseMixin, PyroProxyMixin):

    def __init__(self, tree_view, namespace=None):
        self.view = tree_view
        tree_column_1 = gtk.TreeViewColumn("Employee Name")
        tree_column_2 = gtk.TreeViewColumn("Activity")
        tree_column_1.set_sort_column_id(2)
        tree_column_2.set_sort_column_id(3)
        self.view.append_column(tree_column_1)
        self.view.append_column(tree_column_2)

        cell = gtk.CellRendererText()
        tree_column_1.pack_start(cell, True)
        tree_column_1.add_attribute(cell, "text", 2)
        tree_column_1.add_attribute(cell, "foreground", 4)
        tree_column_2.pack_start(cell, True)
        tree_column_2.add_attribute(cell, "text", 3)
        tree_column_2.add_attribute(cell, "foreground", 4)

        self.model = gtk.TreeStore(long, long, str, str, str, "gboolean")
        self.view.set_model(self.model)

        self.column_number = 0
        self.search_criteria = ""

        if namespace:
            self.namespace = namespace
        else:
            # Get the Pyro namespace from the config
            self.namespace = config.get("pyro", "namespace", "sndcs")


    def getActivityColor(self, type_name):
        if type_name == "idle":
            return "#ff0000"
        elif type_name == "production":
            return "#000000"
        elif type_name == "indirect":
            return "#0000ff"
        elif type_name == "setup":
            return "#00ff00"


    def populate(self, model=False):
        """Populate the model"""
        if model:
            self.model = model
        else:
            self.model = gtk.TreeStore(long, long, str, str, str, "gboolean") # employee_id, labordtl_id, name, activity, color, isClockedIn (for the display clocked-in employee filter)
            active_employee_list = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Employee"]))


            if not hasattr(self,"display_departments"):
                terminal_id = config.get("gtk","terminal_id", None) 
                if terminal_id:
                    terminal = self.selectTerminal( terminal_id )
                    if terminal:
                        self.display_departments = terminal.getTerminalDepartments()
                        terminal.disconnect()
                    else:
                        self.display_departments = None
                else:
                    self.display_departments = None

            for employee in active_employee_list.getActiveEmployeeAndActivityData():
                departments = employee["departments"]

                if self.display_departments is None:
                    display_employee = True
                else:
                    display_employee = False
                    for x in self.display_departments:
                        if departments.count(x):
                            display_employee = True

                # Misc. departments displayed for now.
                if not display_employee and not len(departments)==0: continue
                            
                activities = employee["activities"]
                if not activities:
                    self.model.append(None, (employee["serialNum"], 0, employee["properName"], "", "#aaaaaa", False))
                else:
                    if len(activities) > 1:
                        parent = self.model.append(None, (employee["serialNum"], 0, employee["properName"], "Ganged", "#000000", True))
                    else:
                        parent = None
                    for activity in activities:
                        if activity["serialNum"]:
                            labordtl_id = activity["serialNum"]
                        else:
                            labordtl_id = 0
                        self.model.append(parent, (employee["serialNum"], labordtl_id, employee["properName"], activity["description"], self.getActivityColor(activity["type"]), True))

        if not self.true(config.get("gtk","show_clocked_out_employees")): # If we don't want to see clocked out employees we need to filter them out
            self.clocked_out_employee_filter = self.model.filter_new()
            self.clocked_out_employee_filter.set_visible_column(5)
            self.clocked_out_employee_filter.refilter()
        else:
            self.clocked_out_employee_filter = self.model

        self.modelfilter = self.clocked_out_employee_filter.filter_new()
        self.modelfilter.set_visible_func(treeview_visible_function,self)

        self.modelsort = gtk.TreeModelSort(self.modelfilter)

        self.view.set_model(self.modelsort)
        self.modelsort.set_sort_column_id(2, gtk.SORT_ASCENDING) # Start off sorting by the first column ASCENDING
        self.view.expand_all()
        

    def find_employee(self, employee_id, model=None):
        if not model:
            model = self.model
        p = None # (p)arent
        i = 0
        for x in model:
            if x[0] == employee_id: # We found them!
                p = i
                break
            i += 1
        return p

    def scroll_to_employee(self, employee_id):
        """ Scroll the active employee list so the employee is visible and
        highlight the record.  It is possible that the path may not be found if
        the model is filtered. We will return the path if it found or None if
        it is not found so the calling code can decide what to do with it.
        (i.e. If it is a clock in and clocked-out employees are filtered out we
        may want to unfilter that employee and try to scroll_to_employee again)
        """
        p = self.find_employee(employee_id, model=self.modelsort)
        if p is not None:
            self.view.set_cursor(p)
            self.view.grab_focus()
            self.view.scroll_to_cell((p), use_align=True, row_align=0.5)
            return p
        return None

    def add_activity(self, employee_id, labordtl_id, proper_name, activity_description, color, clockedIn):
        """
        Add an activity to the active employee tree view. If labordtl_id is 0
        it's not an actual labordtl record we are adding. It is something like
        clock in or idle.
        """
        # Try to find employee in the tree model
        p = self.find_employee(employee_id)

        if p is not None: # Employee already in the model
            if not labordtl_id: # If no labordtl_id passed (or 0) then just update the main record
                self.model[p] = [employee_id, labordtl_id, proper_name, activity_description, color, clockedIn]
            else:
                parent_iter = self.model.get_iter(p)
                c = self.model.iter_children(parent_iter)
                if c: # The user is ganged
                    self.model.append(parent_iter, (employee_id, labordtl_id, proper_name, activity_description, color, clockedIn))
                else: # The user is NOT ganged
                    # We need to check if we on a single activity... if so we need to convert into gang
                    if self.model[p][1]: # need to convert to gang
                        self.model.append(parent_iter, (self.model[p]))
                        self.model[p] = [employee_id, 0, proper_name, "Ganged", "#000000", clockedIn]
                        # And add the new activity
                        self.model.append(parent_iter, (employee_id, labordtl_id, proper_name, activity_description, color, clockedIn))
                        # Expand the treeview
                        self.view.expand_row((p), True)
                    else: # Not on activity, no need to convert to gang
                        self.model[p] = [employee_id, labordtl_id, proper_name, activity_description, color, clockedIn]
        else: # Employee not in the model yet
            print "not in model"

    def remove_activity(self, employee_id, labordtl_id, proper_name):
        # Try to find employee in the tree model
        p = self.find_employee(employee_id)

        if p is not None: # Employee is in the model
            if labordtl_id:
                parent_iter = self.model.get_iter(p)
                c = self.model.iter_children(parent_iter)
                if c: # The user is ganged
                    # Check the child records for a matching labordtl_id
                    while c:
                        if self.model.get_value(c, 1) ==  labordtl_id: # Found it
                            self.model.remove(c)
                            # If this leaves the user with only a single activity we need to colapse the gang
                            c = self.model.iter_children(parent_iter)
                            i =  0
                            while c:
                                i += 1
                                c = self.model.iter_next(c)
                            if i == 1:
                                self.model[p] = self.model[(p,0)]
                                del self.model[(p,0)]
                            return
                        c = self.model.iter_next(c)
                else: # The user is NOT ganged
                    if self.model[p][1] == labordtl_id: # If the single activity labordtl_id matches, remove it... otherwise don't do anything
                        self.model[p] = [employee_id, 0, proper_name, "*** IDLE ***", self.getActivityColor("idle"), True]
        else: # Employee not in the model yet
            print "not in model"

    def remove_employee(self, employee_id, proper_name=""):
        # Try to find employee in the tree model
        p = self.find_employee(employee_id)
        if p is not None:
            parent_iter = self.model.get_iter(p)
            self.model.remove(parent_iter)

    def add_employee(self, employee_id, proper_name, departments=False, actionType="New"):
        if departments and self.display_departments is not None:
            for x in departments:
                if x in self.display_departments:
                    self.model.append(None, (employee_id, 0, proper_name, "*** %s Employee ***" % actionType, "#aaaaaa", True))
                    break
        else:
            self.model.append(None, (employee_id, 0, proper_name, "*** %s Employee ***" % actionType, "#aaaaaa", True))

    def edit_employee(self, employee_id, proper_name, departments=False):
        self.remove_employee(employee_id)
        self.add_employee(employee_id, proper_name, departments, "Edited")

    def clock_in_employee(self, employee_id):
        """Sets the employee's isClockedIn column to True. Originally created
        to be used when an employee clocks in (w/ barcode) but the employee is
        filtered out of the model (i.e. only showing clocked in employees). If
        that is the only filter that applies this will make it unfilterd so
        that the treeview can scroll to it."""
        p = self.find_employee(employee_id)
        if p is not None:
            self.model[p][5] = True

    def update(self, event=False):
        if event:
            employee_id = event.msg["serialNum"]
            emp_num = event.msg["empId"]
            proper_name = event.msg["properName"]

            if event.subject == self.namespace + "_clock_out":
                self.add_activity(employee_id, 0, proper_name, "", "#aaaaaa", False)
                log.info("%s [%s] clocked out", proper_name, emp_num)
            elif event.subject == self.namespace + "_clock_in":
                self.add_activity(employee_id, 0, proper_name, "*** IDLE ***", self.getActivityColor("idle"), True)
                log.info("%s [%s] clocked in", proper_name, emp_num)
            elif event.subject == self.namespace + "_lunch_in":
                activity = event.msg["activity"]
                self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("indirect"), True)
                log.info("%s [%s] lunch in", proper_name, emp_num)
            elif event.subject == self.namespace + "_break_in":
                activity = event.msg["activity"]
                self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("indirect"), True)
                log.info("%s [%s] break in", proper_name, emp_num)
            elif event.subject == self.namespace + "_lunch_out":
                activity = event.msg["activity"]
                self.remove_activity(employee_id, activity["serialNum"], proper_name)
                log.info("%s [%s] lunch out", proper_name, emp_num)
            elif event.subject == self.namespace + "_break_out":
                activity = event.msg["activity"]
                self.remove_activity(employee_id, activity["serialNum"], proper_name)
                log.info("%s [%s] break out", proper_name, emp_num)
            elif event.subject == self.namespace + "_indirect_start" or event.subject == self.namespace + "_indirect_resume":
                activity = event.msg["activity"]
                self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("indirect"), True)
                log.info("%s [%s] started or resumed indirect activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"])
            elif event.subject == self.namespace + "_indirect_stop" or event.subject == self.namespace + "_indirect_suspend":
                activity = event.msg["activity"]
                self.remove_activity(employee_id, activity["serialNum"], proper_name)
                log.info("%s [%s] stoped or suspended indirect activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"])
            elif event.subject == self.namespace + "_production_start" or event.subject == self.namespace + "_production_resume":
                activity = event.msg["activity"]
                self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("production"), True)
                log.info("%s [%s] started or resumed production activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"])
            elif event.subject == self.namespace + "_production_stop" or event.subject == self.namespace + "_production_suspend":
                activity = event.msg["activity"]
                self.remove_activity(employee_id, activity["serialNum"], proper_name)
                log.info("%s [%s] stoped or suspended production activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"])
            elif event.subject == self.namespace + "_setup_start" or event.subject == self.namespace + "_setup_resume":
                activity = event.msg["activity"]
                self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("setup"), True)
                log.info("%s [%s] started or resumed setup activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"])
            elif event.subject == self.namespace + "_setup_stop" or event.subject == self.namespace + "_setup_suspend":
                activity = event.msg["activity"]
                self.remove_activity(employee_id, activity["serialNum"], proper_name)
                log.info("%s [%s] stoped or suspended setup activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"])
            elif event.subject == self.namespace + "_employee_delete":
                self.remove_employee(employee_id, proper_name)
                log.info("%s [%s] deleted", proper_name, emp_num)
            elif event.subject == self.namespace + "_employee_add":
                departments = event.msg["departments"]
                self.add_employee(employee_id, proper_name, departments)
                log.info("%s [%s] added", proper_name, emp_num)
            elif event.subject == self.namespace + "_employee_edit":
                departments = event.msg["departments"]
                self.edit_employee(employee_id, proper_name, departments)
                log.info("%s [%s] edited", proper_name, emp_num)

        else:
            self.modelfilter.refilter()

        self.view.expand_all()
        
    def selectTerminal(self, terminal_name):
        terminal_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Terminal"]))
        
        try:
            if terminal_name is "": raise ValueError
            terminal = terminal_factory.setTerminalByTerminalName( terminal_name )
            return terminal
        except ValueError:
            return None
        except SndcsTerminalError:
            return None
