#!/usr/bin/env python
import os
import sys
import ast

from schainpy.controller import Project
from schainpy.cli import cli
from schainpy.utils import log

try:
    import kivy
    from kivy.app import App
    from kivy.uix.label import Label
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.textinput import TextInput
    from kivy.uix.button import Button
    from kivy.uix.dropdown import DropDown
    from kivy.uix.togglebutton import ToggleButton
    from kivy.uix.popup import Popup
    from kivy.uix.filechooser import FileChooserListView
except:
    log.error(
        'You should install kivy module in order to run the GUI.')
    sys.exit()


DEFAULTS = {
    'path': os.path.expanduser('~'),
    'startDate': '2018/01/01',
    'endDate': '2020/01/01',
    'startTime': '00:00:00',
    'endTime': '23:59:59',
    'online': '1',
    'delay': '30',
    'walk': '1',
    'show': '1',
    'zmin': '10',
    'zmax': '40',
}


class MainLayout(BoxLayout):
    def __init__(self, **kwargs):
        super(MainLayout, self).__init__(**kwargs)
        
        self.workspace = os.path.join(os.path.expanduser('~'), 'workspace/scripts')
        self.current_unit_id = None
        self._units = []
        self.project = Project()
        self.project.setup(id='1', name='test', description='')

        self.sidebar_left = BoxLayout(orientation='vertical', size_hint_x=0.4, spacing=5)
        self.body = BoxLayout(orientation='vertical', spacing=5)
        self.sidebar_right = BoxLayout(orientation='vertical', size_hint_x=0.6, spacing=5)
        
        bt_prj = Button(text='Project')
        bt_prj.bind(on_press=self.show_project)
        self.sidebar_left.add_widget(bt_prj)

        bt_add_unit = Button(text='Add Unit')
        bt_add_unit.bind(on_press=self.select_unit)
        self.sidebar_left.add_widget(bt_add_unit)

        bt_add_operation = Button(text='Add Operation')
        bt_add_operation.bind(on_press=self.select_operation)
        self.sidebar_left.add_widget(bt_add_operation)

        bt_import = Button(text='Import')
        bt_import.bind(on_press=self.load)
        self.sidebar_left.add_widget(bt_import)

        bt_export = Button(text='Export')
        bt_export.bind(on_press=self.export)
        self.sidebar_left.add_widget(bt_export)

        bt_run = Button(text='Run')
        bt_run.bind(on_press=self.run)
        self.sidebar_left.add_widget(bt_run)

        bt_stop = Button(text='Stop')
        bt_stop.bind(on_press = self.stop)
        self.sidebar_left.add_widget(bt_stop)

        bt_exit = Button(text = 'Exit', height = 40, size_hint_y = None, background_color=(1, 0, 0, 1))
        bt_exit.bind(on_press=App.get_running_app().stop)
        self.sidebar_left.add_widget(bt_exit)

        self.add_widget(self.sidebar_left)
        self.add_widget(self.body)
        self.add_widget(self.sidebar_right)

    def update_body(self):

        self._units = []
        self.body.clear_widgets()
        self.sidebar_right.clear_widgets()
        
        for unit in self.project.getUnits():
            box = GridLayout(cols=3)
            bt = ToggleButton(text=unit.name, group='units')
            bt._obj = unit
            bt.bind(on_press=self.show_parameters)
            box.add_widget(bt)
            self._units.append(bt)

            for operation in unit.operations:
                bt_op = Button(text = operation.name, background_color=(1, 0.5, 0, 1))
                bt_op._id = unit.id
                bt_op._obj = operation
                bt_op.bind(on_press=self.show_parameters)
                box.add_widget(bt_op)
            
            self.body.add_widget(box)
        
        print(self.project)
    
    def show_parameters(self, instance):
        
        obj = instance._obj
        self.current_unit_id = obj.id
        self.sidebar_right.clear_widgets()
        
        if obj and obj.parameters:
            self._params = {}
            
            for key, value in obj.getParameters().items():
                self.sidebar_right.add_widget(Label(text=key))
                text = TextInput(text=value, multiline=False)
                self._params[key] = text
                self.sidebar_right.add_widget(text)
            
            bt_save = Button(text = 'Save', height = 40, size_hint_y = None, background_color=(0, 1, 0, 1))
            bt_save._obj = obj
            if hasattr(instance, '_id'):
                bt_save._id = instance._id
                self.current_unit_id = None
            bt_save.bind(on_press=self.save_parameters)
            self.sidebar_right.add_widget(bt_save)

        bt_delete = Button(text = 'Delete', height = 40, size_hint_y = None, background_color=(1, 0, 0, 1))
        bt_delete._obj = obj
        if hasattr(instance, '_id'):
            bt_delete._id = instance._id
            self.current_unit_id = obj.id
        bt_delete.bind(on_press=self.delete_object)
        self.sidebar_right.add_widget(bt_delete)

    def save_parameters(self, instance):
        
        obj = instance._obj
        params = {}
        for key in self._params:
            if self._params[key]:
                params[key] = self._params[key].text
        
        if hasattr(instance, '_id'):
            unit = self.project.getProcUnit(instance._id)
            op = unit.getOperation(obj.id)
            op.update(**params)
        else:
            unit = self.project.getProcUnit(obj.id)
            unit.update(**params)
    
    def delete_object(self, instance):
        
        obj = instance._obj
        
        if hasattr(instance, '_id'):
            unit = self.project.getProcUnit(instance._id)
            unit.removeOperation(obj.id)
        else:
            self.project.removeProcUnit(obj.id)

        self.project.updateId(self.project.id)
        self.update_body()

    def show_project(self, instance):

        self.sidebar_right.clear_widgets()
        self._params = {}
        for label in ['Id', 'Name', 'Description']:
            self.sidebar_right.add_widget(Label(text=label))
            text = TextInput(text=getattr(self.project, label.lower()), multiline=False)
            self._params[label] = text
            self.sidebar_right.add_widget(text)

        self.sidebar_right.add_widget(Label(text='Workspace'))
        text = TextInput(text=getattr(self, 'workspace'), multiline=False)
        self._params['Workspace'] = text
        self.sidebar_right.add_widget(text)

        bt_save = Button(text = 'Save', height = 40, size_hint_y = None, background_color=(0, 1, 0, 1))
        bt_save.bind(on_press = self.save_project_parameters)
        self.sidebar_right.add_widget(bt_save)

    def save_project_parameters(self, instance):

        for label in ['Id', 'Name', 'Description']:
            setattr(self.project, label.lower(), self._params[label].text)
        
        setattr(self, 'workspace', self._params['Workspace'].text)

    def select_unit(self, instance):
        
        self.sidebar_right.clear_widgets()
        bt_main = Button(text = 'Select Unit', height = 40, size_hint_y = None)
        dropdown = DropDown()

        for unit in cli.getProcs():
    
            btn = Button(text = unit, size_hint_y = None, height = 40) 
            btn.bind(on_release = lambda btn: dropdown.select(btn.text)) 
            dropdown.add_widget(btn)

        bt_main.bind(on_release = dropdown.open)
        dropdown.bind(on_select = lambda instance, x: setattr(bt_main, 'text', x)) 

        bt_add = Button(text = 'Add', height = 40, size_hint_y = None, background_color=(0, 1, 0, 1))
        bt_add.bind(on_press = lambda instance: self.add_unit(bt_main.text))

        self.sidebar_right.add_widget(bt_main)
        self.sidebar_right.add_widget(bt_add)

    def add_unit(self, s):
        
        if s:
            if 'Reader' in s:
                unit = self.project.addReadUnit(name=s)
            else:
                *_, last = self.project.getUnits()
                unit = self.project.addProcUnit(name=s, inputId=last.id)
            
            keys = cli.getArgs(unit.name)
            values = [DEFAULTS[key] if key in DEFAULTS else '' for key in keys]
            unit.update(**dict(zip(keys, values)))
            self.update_body()
    
    def select_operation(self, instance):
        
        self.sidebar_right.clear_widgets()
        btns = [bt.state == 'down' for bt in self._units]
        if True in btns:
            bt_main = Button(text = 'Select Operation', height = 40, size_hint_y = None)
            dropdown = DropDown()

            for unit in cli.getOperations():
        
                btn = Button(text = unit, size_hint_y = None, height = 40) 
                btn.bind(on_release = lambda btn: dropdown.select(btn.text)) 
                dropdown.add_widget(btn)

            bt_main.bind(on_release = dropdown.open)
            dropdown.bind(on_select = lambda instance, x: setattr(bt_main, 'text', x)) 

            bt_add = Button(text = 'Add', height = 40, size_hint_y = None, background_color=(0, 1, 0, 1))
            bt_add.bind(on_press = lambda instance: self.add_operation(bt_main.text))

            self.sidebar_right.add_widget(bt_main)
            self.sidebar_right.add_widget(bt_add)
        else:
            self.sidebar_right.add_widget(Label(text='Select Unit'))

    def add_operation(self, s):
        
        if s:
            unit = self.project.getProcUnit(self.current_unit_id)
            op = unit.addOperation(name=s)
            keys = cli.getArgs(op.name)
            values = [DEFAULTS[key] if key in DEFAULTS else '' for key in keys]
            op.update(**dict(zip(keys, values)))
            self.update_body()

    def run(self, instance):

        if self.project and self.project.is_alive():
            self.sidebar_right.clear_widgets()
            self.sidebar_right.add_widget(Label(text='Project running'))
        else:
            if self.project.exitcode is None:
                self.project.start()
            else:
                self.project = self.project.clone()
                self.project.start()

    def stop(self, instance):

        if self.project and self.project.is_alive():
            self.project.kill()
            log.error('Project Stopped by user', 'GUI')
        else:
            self.sidebar_right.clear_widgets()
            self.sidebar_right.add_widget(Label(text='Project not running'))

    def load(self, instance):

        self.sidebar_right.clear_widgets()
        textinput = FileChooserListView(
            path=self.workspace, size_hint=(1, 1), dirselect=False, filters=['*.xml'])

        self.sidebar_right.add_widget(textinput)
        bt_open = Button(text = 'Open', height = 40, size_hint_y = None, background_color=(0, 1, 0, 1))
        bt_open.textinput = textinput
        bt_open.bind(on_press = self.load_file)
        self.sidebar_right.add_widget(bt_open)
    
    def load_file(self, instance):

        self.project.readXml(instance.textinput.selection[0])
        self.update_body()

    def export(self, instance):

        filename = os.path.join(self.workspace, '{}.xml'.format(self.project.name))
        self.project.writeXml(filename)
        log.success('File created: {}'.format(filename), 'GUI')


class SignalChainApp(App):
    def build(self):
        return MainLayout(spacing=10)


if __name__ == "__main__":
    SignalChainApp().run()