#!/usr/bin/env python3

from __future__ import print_function

import os
import sys
import threading
import traceback

import numpy

# By default, Gdk loads the OpenGL 3.2 Core profile. However, PyCAM's rendering
# code uses the fixed function pipeline, which was removed in the Core profile.
# So we have to resort to this semi-public API to ask Gdk to use a Compatibility
# profile instead.
os.environ['GDK_GL'] = 'legacy'
# stolen from https://github.com/SebKuzminsky/pycam/pull/140/files

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

from pyutils.pyscope import pyscope
import links_and_nodes as ln

class ln_topic_scope(object):
    def __init__(self, args):
        self.clnt = ln.client("ln_topic_scope", args)
        args = self.clnt.get_remaining_args()
        
        self.topic = None
        self.value_name = None
        self.value_access = None

        for arg in args[1:]:
            if self.topic is None:
                self.topic = arg
                continue
            self.value_name = arg
        if self.topic is None or self.value_name is None:
            raise Exception("usage: ln_topic_scope [ln args...] topic_name value_name")

        self.port = self.clnt.subscribe(self.topic, buffers=10)
        self.request_read = threading.Event()
        self.waiter = threading.Thread(target=self._subscriber_thread)
        self.waiter.daemon = True
        self.waiter.start()

        # register present_window
        self.present_window = self.clnt.get_service_provider(
            "ln.scopes.%s.%s.present_window" % (self.topic, self.value_name), 
            "ln/present_window")
        self.present_window.set_handler(self.on_present_window)
        self.present_window.do_register()
        self.clnt.handle_service_group_in_thread_pool(None, "main")

        self.scope = pyscope(
            fps=30, 
            #draw_timescale=False
        )

        self.w = w = Gtk.Window()
        w.set_size_request(300, 200)
        w.add(self.scope)
        w.set_title("pyscope %s/%s" % (self.topic, self.value_name))
        w.connect("delete-event", self.on_delete)

        print("scope ready")
        self.w.show_all()
        self.request_read.set()

    def on_present_window(self, svc, req, resp):
        def call_present():
            self.w.present()
            return False
        GLib.idle_add(call_present)
        svc.respond()
        return 0

    def _subscriber_thread(self):
        while True:
            self.request_read.wait()
            self.request_read.clear()
            self.port.read()
            GLib.idle_add(self.on_data)

    def on_data(self):
        try:
            if self.value_access is None:
                names = self.value_name.split(".")
                part = self.port.packet

                while True:
                    name = names.pop(0)
                    item_no = None
                    if name.endswith("]"):
                        name, item_no = name.rsplit("[", 1)
                        item_no = int(item_no[:-1])
                    self.value_access = part, name
                    self.value_item_no = item_no
                    if not names:
                        break # done
                    part = getattr(part, name)
                    if item_no is not None:
                        part = part[item_no]
                value = getattr(*self.value_access)
                if isinstance(value, ln.ln_wrappers.ln_packet_part):
                    value = numpy.array([(getattr(value, f)) for f in value._fields])
                    labels = value._fields
                if type(value) == numpy.ndarray:
                    self.scope.expect_array(value.shape)
                    labels = ["%s[%d]" % (self.value_name, i) for i in range(len(value))]
                else:
                    labels = ["%s" % self.value_name]
                self.scope.set_legends(labels)

            value = getattr(*self.value_access)
            if self.value_item_no is not None:
                value = value[self.value_item_no]

            if isinstance(value, ln.ln_wrappers.ln_packet_part):
                value = [(getattr(value, f)) for f in value._fields]
            
            self.scope.push_data(value, self.port.timestamp)
            self.request_read.set()
        except Exception:
            traceback.print_exc()
            Gtk.main_quit()
        return False

    def on_delete(self, win, ev):
        sys.exit(0)

if __name__ == "__main__":
    s = ln_topic_scope(sys.argv)
    Gtk.main()
