#!/usr/bin/python3
# -*- coding: utf-8 -*-

from configparser import ConfigParser
import gi, os, subprocess, logging, sys, signal
import shutil, math, re
from sections.gpu_section import GpuSection
import utils
from pathlib import Path
import slimbookamdcontrollerinfo as info

gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')

from gi.repository import Gdk, Gtk, GLib, GdkPixbuf

APPNAME = 'slimbookamdcontroller'
HOMEDIR = str(Path.home())

CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

LAUNCHER_DESKTOP = os.path.join(
    BASE_DIR, "{}-autostart.desktop".format(APPNAME))
AUTOSTART_DESKTOP = "{}/.config/autostart/{}-autostart.desktop".format(HOMEDIR, APPNAME)

_ = utils.load_translation(APPNAME)

iconpath = CURRENT_PATH+'/amd.png'

cpu = utils.get_cpu_info('name')
patron = re.compile(r'[ ](.*)[ ]*([0-9]).*([0-9]{4,})(\w*)')
type = patron.search(cpu).group(1).strip()
gen = patron.search(cpu).group(2)
number = patron.search(cpu).group(3)
line_suffix = patron.search(cpu).group(4)

config = ConfigParser()
config.read(utils.CONFIG_FILE)

db_cpu = ConfigParser()
db_cpu.read(CURRENT_PATH + '/' + utils.DB_FILE)

class SlimbookAMD(Gtk.ApplicationWindow):

    mode = ""
    indicador_actual = ""
    autostart_actual = ""
    parameters = ''
    cpu_ok = False

    switch1 = Gtk.Switch()
    switch1.set_halign(Gtk.Align.END)

    switch2 = Gtk.Switch()
    switch2.set_halign(Gtk.Align.END)

    rbutton1 = Gtk.RadioButton.new_with_label_from_widget(None, (_("Low")))
    rbutton2 = Gtk.RadioButton.new_with_mnemonic_from_widget(rbutton1, (_("Medium")))
    rbutton3 = Gtk.RadioButton.new_with_mnemonic_from_widget(rbutton1, (_("High")))

    def __init__(self):
        Gtk.Window.__init__(self, title="Slimbook AMD Controller")

        self.set_icon()
        self.inicio()

        #self.set_decorated(False)

        self.set_position(Gtk.WindowPosition.CENTER)
        self.get_style_context().add_class("bg-image")

        # Movement
        self.active = True
        self.is_in_drag = False
        self.x_in_drag = 0
        self.y_in_drag = 0
        self.connect('button-press-event', self.on_mouse_button_pressed)
        self.connect('button-release-event', self.on_mouse_button_released)
        self.connect('motion-notify-event', self.on_mouse_moved)

        self.win_grid = Gtk.Grid(column_homogeneous=True,
                                 column_spacing=0,
                                 row_spacing=10)

        self.add(self.win_grid)

        #Content
        
        label1 = Gtk.Label(label=_("Enable app at startup"))
        label1.set_halign(Gtk.Align.START)

        label2 = Gtk.Label(label=_("Show indicator icon"))
        label2.set_halign(Gtk.Align.START)

        button1 = Gtk.Button(label="Button 1")
        button1.set_halign(Gtk.Align.START)
        button1.get_style_context().add_class("button-none")

        button2 = Gtk.Button(label="Button 2")
        button2.set_halign(Gtk.Align.START)
        button2.get_style_context().add_class("button-none")

        separador = Gtk.Image.new_from_file(CURRENT_PATH+'/images/separador.png')
        separador.set_halign(Gtk.Align.CENTER)
        separador2 = Gtk.Image.new_from_file(CURRENT_PATH+'/images/separador.png')
        separador2.set_halign(Gtk.Align.CENTER)
        separador3 = Gtk.Image.new_from_file(CURRENT_PATH+'/images/separador.png')
        separador3.set_halign(Gtk.Align.CENTER)

        pixbuf1 = GdkPixbuf.Pixbuf.new_from_file_at_scale(
            filename=CURRENT_PATH+'/images/cross.png',
            width=20,
            height=20,
            preserve_aspect_ratio=True)

        close = Gtk.Image.new_from_pixbuf(pixbuf1)
        close.get_style_context().add_class("close")

        evnt_close = Gtk.EventBox()
        evnt_close.add(close)
        evnt_close.set_halign(Gtk.Align.END)
        evnt_close.connect("button_press_event", self.on_btnCerrar_clicked)

        # CPU
        cpuGrid = Gtk.Grid(column_homogeneous=True,
                           column_spacing=0,
                           row_spacing=12)
        cpuGrid.set_name('cpuGrid')
        cpuGrid.set_valign(Gtk.Align.CENTER)
        
        # CPU Name
        hbox_consumo = Gtk.HBox(spacing=15)

        cpu_name = Gtk.Label(label=cpu)
        cpu_name.set_halign(Gtk.Align.CENTER)
        hbox_consumo.pack_start(cpu_name, True, True, 0)

        # CPU Temp
        thermal_zones = subprocess.getstatusoutput(
            'ls /sys/class/thermal/ | grep thermal_zone')[1].split('\n')

        cpu_thermal_zone = None
        for thermal_zone in thermal_zones:
            if subprocess.getstatusoutput("cat /sys/class/thermal/"+thermal_zone+"/type | grep acpitz")[0] == 0:
                print('Found thermal zone!')
                cpu_thermal_zone = thermal_zone
                exit

        if cpu_thermal_zone != None:
            cpu_temp = subprocess.getstatusoutput(
                "cat /sys/class/thermal/"+cpu_thermal_zone+r"/temp | sed 's/\(.\)..$/ °C/'")
            if cpu_temp[0] == 0:
                label = Gtk.Label(label=cpu_temp[1])

                def _update_label(label: Gtk.Label):
                    label.set_label(subprocess.getoutput(
                        "cat /sys/class/thermal/"+cpu_thermal_zone+r"/temp | sed 's/\(.\)..$/ °C/'"))
                    return True

                GLib.timeout_add_seconds(2, _update_label, label)
                hbox_consumo.pack_start(label, True, True, 0)
            else:
                print('Cpu_temp 404')
        else:
            print('Thermal_zone 404')

        # consumo = Gtk.Label(label=_('Current consumption: ')+ self.cpu_value('slow-limit')+" - "+self.cpu_value('stapm-limit')+" - "+self.cpu_value('fast-limit')+" mW.")
        # consumo.set_halign(Gtk.Align.END)
        # hbox_consumo.pack_start(consumo, True, True, 0)

        img = ''

        idiomas = utils.get_languages()[0]

        print (idiomas)
        if idiomas.find("es") >= 0:  # Español
            img = 'modos_es.png'
        else:
            img = 'modos_en.png'  # Inglés

        modos = Gtk.Image.new_from_file(CURRENT_PATH+'/images/'+img)
        modos.get_style_context().add_class("cambModos")
        modos.set_halign(Gtk.Align.CENTER)

        # Box 1
        vbox1 = Gtk.VBox()
        self.rbutton1.connect("toggled", self.on_button_toggled, "low")
        self.rbutton1.set_name('radiobutton')
        self.rbutton1.set_halign(Gtk.Align.CENTER)

        rbutton1_img = Gtk.Image.new_from_file(CURRENT_PATH+'/images/modo1.png')
        rbutton1_img.set_halign(Gtk.Align.CENTER)

        vbox1.pack_start(rbutton1_img, False, False, 0)
        vbox1.pack_start(self.rbutton1, False, False, 0)

        # Box 2
        vbox2 = Gtk.VBox()
        self.rbutton2.connect("toggled", self.on_button_toggled, "medium")
        self.rbutton2.set_name('radiobutton')
        self.rbutton2.set_halign(Gtk.Align.CENTER)

        rbutton2_img = Gtk.Image.new_from_file(CURRENT_PATH+'/images/modo2.png')
        rbutton2_img.set_halign(Gtk.Align.CENTER)

        vbox2.pack_start(rbutton2_img, False, False, 0)
        vbox2.pack_start(self.rbutton2, False, False, 0)

        # Box 3
        vbox3 = Gtk.VBox()
        self.rbutton3.connect("toggled", self.on_button_toggled, "high")
        self.rbutton3.set_name('radiobutton')
        self.rbutton3.set_halign(Gtk.Align.CENTER)

        rbutton3_img = Gtk.Image.new_from_file(CURRENT_PATH+'/images/modo3.png')
        rbutton3_img.set_halign(Gtk.Align.CENTER)

        vbox3.pack_start(rbutton3_img, False, False, 0)
        vbox3.pack_start(self.rbutton3, False, False, 0)

        hbox_radios = Gtk.HBox(spacing=70)
        hbox_radios.set_valign(Gtk.Align.CENTER)
        hbox_radios.add(vbox1)
        hbox_radios.add(vbox2)
        hbox_radios.add(vbox3)

        cpuGrid.attach(hbox_consumo, 4, 8, 5, 1)
        cpuGrid.attach(modos, 5, 10, 3, 1)
        cpuGrid.attach(hbox_radios, 4, 11, 5, 3)

        # Buttons
        botonesBox = Gtk.Box(spacing=10)
        botonesBox.set_name('botonesBox')
        botonesBox.set_halign(Gtk.Align.CENTER)
        botonesBox.set_name('buttons')

        # Accept
        btnAceptar = Gtk.ToggleButton(label=_("ACCEPT"))
        btnAceptar.set_size_request(125, 30)
        btnAceptar.connect("toggled", self.on_btnAceptar_clicked)
        botonesBox.pack_start(btnAceptar, True, True, 0)

        # Close
        btnCancelar = Gtk.ToggleButton(label=_("CANCEL"))
        btnCancelar.set_size_request(125, 30)
        btnCancelar.connect("toggled", self.on_btnCerrar_clicked, 'x')
        botonesBox.pack_start(btnCancelar, True, True, 0)

        # Notebook
        notebook = Gtk.Notebook()

        page1 = Gtk.Box()
        page1.set_orientation(Gtk.Orientation.HORIZONTAL)
        page1.set_border_width(0)
        page1.set_halign(Gtk.Align.CENTER)
        page1.add(cpuGrid)
        notebook.append_page(page1, Gtk.Label(label="CPU"))

        # GPU
        notebook = GpuSection(notebook).add()

        # About us 
        pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
            filename=CURRENT_PATH+'/images/question.png',
            width=23,
            height=23,
            preserve_aspect_ratio=True)

        iconApp = Gtk.Image.new_from_pixbuf(pixbuf)
        iconApp.get_style_context().add_class("help")

        evnt_box = Gtk.EventBox()
        evnt_box.set_halign(Gtk.Align.END)
        evnt_box.add(iconApp)

        evnt_box.connect("button_press_event", self.about_us)

        pixbuf1 = GdkPixbuf.Pixbuf.new_from_file_at_scale(
            filename = CURRENT_PATH+'/images/settings.png',
            width = 20,
            height = 20,
            preserve_aspect_ratio=True)

        settings = Gtk.Image.new_from_pixbuf(pixbuf1)
        evnt_settings = Gtk.EventBox()
        evnt_settings.set_valign(Gtk.Align.CENTER)
        evnt_settings.set_halign(Gtk.Align.END)
        evnt_settings.add(settings)
        evnt_settings.connect("button_press_event", self.settings)

        hbox_close = Gtk.HBox()
        hbox_close.set_valign(Gtk.Align.START)
        hbox_close.set_halign(Gtk.Align.END)
        hbox_close.add(evnt_settings)
        hbox_close.add(evnt_close)

        version_tag = Gtk.Label(label='')
        version_tag.set_halign(Gtk.Align.START)
        version_tag.set_valign(Gtk.Align.END)
        version_tag.set_name('version')
        version_line = subprocess.getstatusoutput(
            "cat "+CURRENT_PATH+r"/changelog |head -n1| egrep -o '\(.*\)'")
        if version_line[0] == 0:
            version = version_line[1]
            version_tag.set_markup(
                '<span font="10">Version '+version[1:len(version)-1]+'</span>')
        version_tag.set_justify(Gtk.Justification.CENTER)

        # Grid 
        grid = Gtk.Grid(column_homogeneous=False,
                        column_spacing=0,
                        row_spacing=10)

        grid.set_name('label')

        grid.attach(button1, 1, 1, 2, 1)
        grid.attach(button2, 9, 1, 2, 1)

        grid.attach(label1, 4, 4, 3, 1)
        grid.attach(self.switch1, 6, 4, 2, 1)
        grid.attach(separador, 2, 5, 8, 1)
        grid.attach(label2, 4, 6, 3, 1)
        grid.attach(self.switch2, 6, 6, 2, 1)
        grid.attach(separador2, 2, 7, 8, 1)

        """ grid.attach(hbox_consumo, 2, 8, 8, 1)
        grid.attach(modos, 2, 10, 8, 1)
        grid.attach(hbox_radios, 3, 11, 6, 2) """

        grid.attach(notebook, 2, 8, 8, 1)

        self.win_grid.attach(grid, 1, 1, 5, 5)
        self.win_grid.attach(botonesBox, 1, 7, 5, 1)
        self.win_grid.attach(version_tag, 1, 7, 5, 1)
        self.win_grid.attach(evnt_box, 5, 7, 1, 1)
        self.win_grid.attach(hbox_close, 5, 0, 1, 1)
        self.show_all()

        self.set_cpu()

        try:
            params = config.get('USER-CPU', 'cpu-parameters').split('/')
            self.parameters = params
        except:
            print('CPU not added')


    def settings(self, widget=None, x=None):
        import settings
        self.active = False
        dialog = settings.Dialog()

    def set_icon(self):
        if str(Path(__file__).parent.absolute()).startswith('/usr'):
            SHAREDIR = os.path.join('/usr', 'share')
            ICONDIR = os.path.join(
                SHAREDIR, 'icons', 'hicolor', 'scalable', 'apps')
        else:
            ROOTDIR = os.path.dirname(__file__)
            ICONDIR = os.path.normpath(
                os.path.join(ROOTDIR, 'images'))

        ICON = os.path.join(ICONDIR, 'slimbookamdcontroller.svg')

        try:
            self.set_icon_from_file(ICON)
        except:
            print("Icon not found")

    def on_realize(self, widget):
        monitor = Gdk.Display.get_primary_monitor(Gdk.Display.get_default())
        scale = monitor.get_scale_factor()
        monitor_width = monitor.get_geometry().width / scale
        monitor_height = monitor.get_geometry().height / scale
        width = self.get_preferred_width()[0]
        height = self.get_preferred_height()[0]
        self.move((monitor_width - width)/2, (monitor_height - height)/2)

    def on_mouse_moved(self, widget, event):
        if self.is_in_drag:
            xi, yi = self.get_position()
            xf = int(xi + event.x_root - self.x_in_drag)
            yf = int(yi + event.y_root - self.y_in_drag)
            if math.sqrt(math.pow(xf-xi, 2) + math.pow(yf-yi, 2)) > 10:
                self.x_in_drag = event.x_root
                self.y_in_drag = event.y_root
                self.move(xf, yf)
        return False
        
    def on_mouse_button_released(self, widget, event):
        if event.button == 1:
            self.is_in_drag = False
            self.x_in_drag = event.x_root
            self.y_in_drag = event.y_root
        return False
        
    def on_mouse_button_pressed(self, widget, event):

        if event.button == 1 and self.active == True:
            self.is_in_drag = True
            self.x_in_drag, self.y_in_drag = self.get_position()
            self.x_in_drag = event.x_root
            self.y_in_drag = event.y_root
            #return True
        return False

    def on_button_toggled(self, button, name):
        self.mode = name

    def on_btnAceptar_clicked(self, widget):

        # Check secureboot

        if utils.get_secureboot_status():
            self.dialog(_("Secureboot Warning"),
                        _("This computer has Secureboot enabled, which does not allow kernel to manage CPU parameters."))
            sys.exit(1)

        modes = {"low":0, "medium":1, "high":2}
        nmode = modes[self.mode]

        try:
            # This throws an exception if cpu is not found
            set_parameters = self.parameters[nmode].split('-')
            print(set_parameters)

            print('Updating '+self.mode+' to : ' +
                  set_parameters[0]+' '+set_parameters[1]+' '+set_parameters[2]+'.\n')

            returncode = subprocess.call('pkexec slimbookamdcontroller-pkexec {}'.format(utils.CONFIG_FILE), shell=True, stdout=subprocess.PIPE)
            print("return code:",returncode)
            if (returncode == 0):
                os.system("notify-send 'Slimbook AMD Controller' '" + _("Changes have been executed correctly.") +                          "' -i '" + CURRENT_PATH+'/images/slimbookamdcontroller.svg' + "'")
                

                # Check switches
                self._inicio_automatico(self.switch1, self.switch1.get_state())

                self._show_indicator(self.switch2, self.switch2.get_state())

                # Update vars
                self.update_config_file("mode", self.mode)
                self.update_config_file("autostart", self.autostart_actual)
                self.update_config_file("show-icon", self.indicador_actual)

                #self.reboot_indicator()

            else:
                os.system("notify-send 'Slimbook AMD Controller' '" + _("Your CPU is not avalible, this software might not work.") +
                          "' -i '" + CURRENT_PATH+'/images/slimbookamdcontroller.png' + "'")
        except Exception as e:
            print(e)
            os.system("notify-send 'Slimbook AMD Controller' '" + _("Your CPU is not avalible, this software might not work.") +
                      "' -i '" + CURRENT_PATH+'/images/slimbookamdcontroller.png' + "'")

        # CERRAMOS PROGRAMA
        Gtk.main_quit()

    def dialog(self, title, message, link=None):
        dialog = Gtk.MessageDialog(
            transient_for=self,
            flags=0,
            message_type=Gtk.MessageType.WARNING,
            buttons=Gtk.ButtonsType.CLOSE,
            text=title,
        )

        dialog.format_secondary_text(
            message
        )

        dialog.set_position(Gtk.WindowPosition.CENTER)

        response = dialog.run()

        if response == Gtk.ResponseType.CLOSE:

            print("WARN dialog closed")

        dialog.destroy()

    def inicio(self):
        print('Loading configuration:\n')
        
        if not Path(utils.CONFIG_FILE).exists():
            os.makedirs(os.path.expanduser("~/.config/slimbookamdcontroller"),exist_ok = True)
            self.update_config_file("autostart","off","CONFIGURATION")
            self.update_config_file("show-icon","off","CONFIGURATION")
            self.update_config_file("mode","medium","CONFIGURATION")
        
        config.read(utils.CONFIG_FILE)
        
        if config.get('CONFIGURATION', 'autostart') == 'on':
            self.autostart_actual = 'on'
            self.switch1.set_active(True)
            print('- Autostart enabled')
        else:
            self.autostart_actual = 'off'
            self.switch1.set_active(False)
            print('- Autostart disabled')

        if config.get('CONFIGURATION', 'show-icon') == 'on':
            self.indicador_actual = 'on'
            self.switch2.set_active(True)
            print('- Indicator enabled')
        else:
            self.indicador_actual = 'off'
            self.switch2.set_active(False)
            print('- Indicator disabled')

        self.mode = config.get('CONFIGURATION', 'mode')
        
        self.rbutton1.set_active(self.mode == "low")
        self.rbutton2.set_active(self.mode == "medium")
        self.rbutton3.set_active(self.mode == "high")
        

    # Read RyzenAdj output
    def cpu_value(self, parameter):

        call = subprocess.getoutput(
            '/usr/bin/ryzenadj --info')
        salida = str(call)

        index = str(salida.find(parameter))
        #print('Indice: '+index)

        # Que encuentre cuatro digitos juntos seguidos de 1 o 2 letras mayusculas
        patron = re.compile(r"([0-9]{1,2}\.[0-9]{3,})[ ]\|[ ]("+parameter+")")
        value = patron.search(call).group(1)
        param = patron.search(call).group(2)

        print('Value: '+str(value))
        print('Param: '+param)

        return value

    def reboot_indicator(self):
        pid = utils.get_pid_from_file("slimbook.amd.controller.indicator.pid")

        if (pid>0):
            if (utils.is_pid_alive(pid)):
                print("Killing indicator process...")
                os.kill(pid,9)

        if self.indicador_actual:
            print("Starting indicator...")
            os.system("python3 " + CURRENT_PATH + "/slimbookamdcontrollerindicator.py")

    # Copies autostart file in directory
    def _inicio_automatico(self, switch, state):

        if not os.path.exists(HOMEDIR+'/.config/autostart'):
            os.system('mkdir ~/.config/autostart')
            print('Dir autostart created.')

        if switch.get_active() is True:
            print("\nAutostart Enabled")
            if not os.path.isfile(AUTOSTART_DESKTOP):
                shutil.copy(LAUNCHER_DESKTOP, AUTOSTART_DESKTOP)
                print("File .conf has been copied!.")
            self.autostart_actual = 'on'
        else:
            print("\nAutostart Disabled")
            if os.path.isfile(AUTOSTART_DESKTOP):
                os.remove(AUTOSTART_DESKTOP)
                print("File .conf has been deleted.")
            self.autostart_actual = 'off'

        print('Autostart now: ' + self.autostart_actual+'')

    def set_cpu(self):
        if not config.has_option('USER-CPU', 'cpu-parameters') or len(config.get('USER-CPU', 'cpu-parameters')) <= 1:
            print('Setting cpu TDP values')
            cpu_codename = type+'-'+gen+'-'+number+line_suffix

            params = db_cpu['PROCESSORS'][cpu_codename]
            self.update_config_file('cpu-parameters', params, 'USER-CPU')
            
            """
            if config.has_option('PROCESSORS',cpu_codename):
                print('Found processor in list')
                params = config['PROCESSORS'][cpu_codename]
                self.update_config_file('cpu-parameters', params, 'USER-CPU')
            else:
                print('Could not find your proc in .conf')
                self.settings()
                config.read(utils.CONFIG_FILE)
            """

    def _show_indicator(self, switch, state):

        if switch.get_active() is True:
            print("\nIndicator Enabled")
            # --> Luego esta variable será guardada y cargada desde el programa indicador
            self.indicador_actual = 'on'
        else:
            print("\nIndicator Disabled")
            self.indicador_actual = 'off'

        print('Indicador now: ' + self.indicador_actual)
        print()

    def about_us(self, widget, x):
        self.active = False
        print('\nINFO:')
        #os.system('sudo /usr/share/slimbookamdcontroller/ryzenadj --info')
        print('\n')
        # Abre la ventana de info

        dialog = info.PreferencesDialog()
        dialog.connect("destroy", self.close_dialog)
        dialog.show_all()

        #os.system('python3 '+currpath+'/slimbookamdcontrollerinfo.py')

    def close_dialog(self, dialog):
        dialog.close()

        self.active = True

    def on_btnCerrar_clicked(self, widget, x):
        Gtk.main_quit()

    def update_config_file(self, variable, value, section='CONFIGURATION'):

        config.read(utils.CONFIG_FILE)

        # We change our variable: config.set(section, variable, value)
        if not config.has_section(section):
            config.add_section(section)
        
        config.set(section, str(variable), str(value))

        # Writing our configuration file
        with open(utils.CONFIG_FILE, 'w') as configfile:
            config.write(configfile)

        print("\n- Variable |"+variable +
              "| updated in .conf, actual value: "+value)

def main():
    pid_name = "slimbook.amd.controller.application.pid"
    utils.application_lock(pid_name)
    
    win = SlimbookAMD()
    win.connect("destroy", Gtk.main_quit)
    Gtk.main()
    
    utils.application_release(pid_name)
    
    sys.exit(0)

if __name__=="__main__":
    main()
