#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2019 Purism SPC
# SPDX-License-Identifier: GPL-3.0+
# Author: David Boddie <david.boddie@puri.sm>
#
# Provides a GUI for a tester to toggle kill switches and push buttons,
# showing a summary of which states have been visited by the system.

import evdev
from evdev.ecodes import KEY_WIMAX, KEY_WLAN, KEY_CAMERA, KEY_VOLUMEUP, \
    KEY_VOLUMEDOWN
import sys
import gi

gi.require_version('Gtk', '3.0')
from gi.repository import Gdk, GdkPixbuf, GLib, Gtk

FontSize = 40

Unchecked = '<span font="%i">\u2610</span>' % FontSize
PartChecked = '<span font="%i">\u2612</span>' % FontSize
Checked = '<span font="%i">\u2611</span>' % FontSize

uncheck_svg = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg" height="40" width="40"
     version="1.1" viewBox="0 0 40 40">
<rect x="0" y="0" width="40" height="40" stroke-width="1" stroke="black"
fill="none" />
</svg>
"""

check_svg = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg" height="40" width="40"
     version="1.1" viewBox="0 0 40 40">
<rect x="0" y="0" width="40" height="40" stroke-width="1" stroke="black"
fill="none" />
<path d="M 5,5 L 35,35 M 35,5 L 5,35" stroke-width="5" stroke="green"
fill="none" />
</svg>
"""


class CheckLabels(Gtk.Box):

    def __init__(self, active):
        super().__init__(orientation='vertical')

        self.check_pixbuf = self.load(check_svg)
        self.uncheck_pixbuf = self.load(uncheck_svg)

        if active:
            off_pixbuf = self.uncheck_pixbuf
            on_pixbuf = self.check_pixbuf
        else:
            on_pixbuf = self.uncheck_pixbuf
            off_pixbuf = self.check_pixbuf

        self.off_label = Gtk.Image()
        self.off_label.set_from_pixbuf(off_pixbuf)
        self.on_label = Gtk.Image()
        self.on_label.set_from_pixbuf(on_pixbuf)
        self.add(self.on_label)
        self.add(self.off_label)

    def load(self, svg):
        loader = GdkPixbuf.PixbufLoader()
        loader.write(svg.encode())
        loader.set_size(40, 40)
        loader.close()

        return loader.get_pixbuf()

    def check(self, state):
        if state:
            self.on_label.set_from_pixbuf(self.check_pixbuf)
        else:
            self.off_label.set_from_pixbuf(self.check_pixbuf)


class Application(Gtk.Application):

    def __init__(self):
        super().__init__(application_id='com.example.read_buttons')
        GLib.set_application_name('Test Librem 5')
        GLib.set_prgname('sm.puri.test-librem5')

    def do_startup(self):
        Gtk.Application.do_startup(self)

    def do_activate(self):
        window = Gtk.Window(application=self)
        window.set_default_size(360, 720)

        self.stack = Gtk.Stack()
        self.stack.add_named(ButtonsPage(window), 'buttons')
        self.stack.add_named(FinishedPage(window), 'finished')
        self.current_page = 0

        self.pages = ['buttons', 'finished']

        self.next_button = Gtk.Button.new_from_icon_name('go-next-symbolic',
                                                         Gtk.IconSize.DIALOG)
        self.next_button.connect('clicked', self.show_next_page)

        layout = Gtk.Box(orientation='vertical', vexpand=True)
        layout.add(self.stack)
        layout.pack_end(self.next_button, False, False, 16)

        self.update_next_button()

        window.add(layout)
        window.show_all()

    def show_next_page(self, button):

        if self.current_page < len(self.pages) - 1:
            self.current_page += 1
            self.stack.set_visible_child_name(self.pages[self.current_page])

        self.update_next_button()

    def update_next_button(self):

        self.next_button.set_sensitive(self.current_page < len(self.pages) - 1)
        if self.current_page == len(self.pages) - 1:
            self.next_button.hide()


class ButtonsPage(Gtk.Grid):

    Labels = [
        (1, 0, FontSize / 2, "Modem"),
        (1, 1, FontSize / 2, "WLAN"),
        (2, 1, FontSize / 2, "Volume +"),
        (1, 2, FontSize / 2, "Camera"),
        (2, 2, FontSize / 2, "Volume -"),
    ]

    def __init__(self, window):

        super().__init__(hexpand=True, column_spacing=16, row_spacing=8)

        self._reports = set()
        self._previous = None
        self.window = window

        # Find the initial states of the buttons.
        active = self.find_active('gpio-keys')

        # Invert the logic for the kill switches.
        self.modem_box = CheckLabels(KEY_WIMAX not in active)
        self.report('WWAN', KEY_WIMAX not in active, ['off', 'on'])
        self.wlan_box = CheckLabels(KEY_WLAN not in active)
        self.report('WLAN', KEY_WLAN not in active, ['off', 'on'])
        self.camera_box = CheckLabels(KEY_CAMERA not in active)
        self.report('Camera', KEY_CAMERA not in active, ['off', 'on'])

        self.volume_up_box = CheckLabels(KEY_VOLUMEUP in active)
        self.report('Volume+', KEY_VOLUMEUP in active, ['out', 'in'])
        self.volume_down_box = CheckLabels(KEY_VOLUMEDOWN in active)
        self.report('Volume-', KEY_VOLUMEDOWN in active, ['out', 'in'])

        for x, y, font_size, text in self.Labels:
            label = Gtk.Label()
            label.set_markup('<span font="%i">%s</span>' % (font_size, text))
            self.attach(label, x, y, 1, 1)

        self.attach(self.modem_box, 0, 0, 1, 1)
        self.attach(self.wlan_box, 0, 1, 1, 1)
        self.attach(self.camera_box, 0, 2, 1, 1)

        self.attach(self.volume_up_box, 3, 1, 1, 1)
        self.attach(self.volume_down_box, 3, 2, 1, 1)

        self.window.connect('key-press-event', self.handle_key_press)
        self.window.connect('key-release-event', self.handle_key_release)

    def handle_key_press(self, widget, key):

        return self.report_key_event(key, True)

    def handle_key_release(self, widget, key):

        return self.report_key_event(key, False)

    def report_key_event(self, key, value):

        if key.keyval == Gdk.KEY_AudioLowerVolume:
            self.volume_down_box.check(value)
            self.report('Volume-', value, ['out', 'in'])
        elif key.keyval == Gdk.KEY_AudioRaiseVolume:
            self.volume_up_box.check(value)
            self.report('Volume+', value, ['out', 'in'])

        # Invert the logic for the kill switches.
        elif key.keyval == 269025204:       # KEY_WWAN evdev
            value = not value
            self.modem_box.check(value)
            self.report('WWAN', value, ['off', 'on'])
        elif key.keyval == Gdk.KEY_WLAN:    # KEY_WLAN evdev
            value = not value
            self.wlan_box.check(value)
            self.report('WLAN', value, ['off', 'on'])
        elif key.keyval == Gdk.KEY_WebCam:  # KEY_CAMERA evdev
            value = not value
            self.camera_box.check(value)
            self.report('Camera', value, ['off', 'on'])

        return False

    def find_active(self, device_name):

        try:
            for path in evdev.list_devices():
                device = evdev.InputDevice(path)
                if device.name == device_name:
                    return device.active_keys()
        except PermissionError:
            pass

        return []

    def report(self, text, value, text_values):

        if value:
            text_value = text_values[1]
        else:
            text_value = text_values[0]

        if (text, value) not in self._reports:
            self._reports.add((text, value))
            print(text, text_value)

        self._previous = (text, value)


class FinishedPage(Gtk.Button):

    def __init__(self, window):

        super().__init__(hexpand=True, vexpand=True)

        self.window = window

        image = Gtk.Image.new_from_icon_name('object-select-symbolic',
                                             Gtk.IconSize.DIALOG)
        image.set_pixel_size(128)

        self.set_image(image)
        self.connect('clicked', self.close)

    def close(self, button):
        self.window.close()


if __name__ == '__main__':

    app = Application()
    sys.exit(app.run(sys.argv))
