#!/usr/bin/python

from gi.repository import Gtk, GObject, GLib
from pzbanzeige import *
import subprocess


class Sim(object):
    def __init__(self, callback, init_time = 0.0):
        self.sim_speed = 0.0
        self.sim_dist = 0.0
        self.sim_time = init_time

        self.callback_update = callback

        self.simproc = subprocess.Popen(["lua", "testharness.lua"],
                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                universal_newlines=True)
        self.proc_in = self.simproc.stdin
        self.proc_out = self.simproc.stdout

        GLib.io_add_watch(self.proc_out, GLib.IO_IN | GLib.IO_ERR | GLib.IO_HUP,
                self.handle_sim_message)

    def reinit(self, speed = 0.0, init_time = 0.0, ttype = "O", start = True):
        self.sim_time = init_time
        self.sim_speed = speed
        self.sim_dist = 0.0

        self.proc_in.write("I T:%f S:%f ZA:%s SP:%d\n" %
                (self.sim_time, self.sim_dist, ttype, start))
        self.proc_in.flush()

    def step(self, current_time, accel, active_magnet, befehl, frei, wachsam):
        delta_time = current_time - self.sim_time

        distance = self.sim_speed / 3.6 * delta_time
        delta_v = accel * 3.6 * delta_time

        new_speed = self.sim_speed + delta_v
        new_speed = max(min(new_speed, 200.0), 0.0)

        self.sim_time = current_time
        self.sim_speed = new_speed
        self.sim_dist += distance

        cmdkeys = ["B", "F", "W"]
        if not befehl:
            cmdkeys[0] = "-"
        if not frei:
            cmdkeys[1] = "-"
        if not wachsam:
            cmdkeys[2] = "-"
        cmdkeys = "".join(cmdkeys)

        self.proc_in.write("S T:%f S:%f V:%f M:%d BT:%s\n" %
                (self.sim_time, self.sim_dist, self.sim_speed, active_magnet, cmdkeys))
        self.proc_in.flush()

    def handle_sim_message(self, f, cond):
        if cond != GLib.IO_IN:
            print("Error!")
            Gtk.main_quit()
            return False

        msg = f.readline().rstrip()
        args = msg.split()
        cmd = args.pop(0)

        blau = BLAU_AUS
        magnet = MAGNET_AUS
        befehl = False
        acoust_signal = False

        monspeed = 999.0
        switchspeed = 0.0
        end_1000 = 0.0
        end_1000r = 0.0
        end_500 = 0.0
        forced_braking = None

        if cmd == "S":
            while args:
                k, v = args.pop(0).split(":", 1)

                if k == "LB":
                    blau_map = {
                            "aus": BLAU_AUS,
                            "55": BLAU_55,
                            "70": BLAU_70,
                            "85": BLAU_85,
                            "blink55": BLAU_55BLINK,
                            "blink70": BLAU_70BLINK,
                            "blink85": BLAU_85BLINK,
                            "wechsel": BLAU_WECHSEL,
                            }
                    blau = blau_map[v]
                elif k == "LM":
                    mag_map = {
                            "aus": MAGNET_AUS,
                            "500": MAGNET_500,
                            "1000": MAGNET_1000,
                            "zwang": MAGNET_ZWANG,
                            "stoer": MAGNET_STOER,
                            }
                    magnet = mag_map[v]
                elif k == "L4":
                    befehl = v != "0"
                elif k == "AS":
                    acoust_signal = v != "0"
                elif k == "PG":
                    monspeed = float(v)
                elif k == "UG":
                    switchspeed = float(v)
                elif k == "E1":
                    end_1000 = float(v) - self.sim_dist
                elif k == "E1R":
                    end_1000r = float(v) - self.sim_dist
                elif k == "E5":
                    end_500 = float(v) - self.sim_dist
                elif k == "ZB":
                    forced_braking = v.replace("_", " ")
                else:
                    print("Unknown key " + k + " with value " + v)
        else:
            print("Unknown from sim: " + msg)

        self.callback_update(blau, magnet, befehl, acoust_signal, monspeed, switchspeed,
                forced_braking, end_1000, end_1000r, end_500)

        return True


class App(object):
    def __init__(self):
        self.sim = Sim(self.callback_simupdate, GLib.get_monotonic_time() / 1e6)

        builder = Gtk.Builder()
        builder.add_from_file("TestPZB.glade")
        builder.connect_signals(self)

        pzbbin = builder.get_object("aspectpzb")
        self.pzb_display = PZBAnzeige()
        pzbbin.add(self.pzb_display)

        self.init_speed = builder.get_object("adj_initspeed")
        self.init_zugart = builder.get_object("combo_zugart")
        self.init_zugart.set_active(0)
        self.init_start = builder.get_object("check_startprogramm")

        self.disp_speed = builder.get_object("label_speed")
        self.disp_dist = builder.get_object("label_dist")
        self.disp_monspeed = builder.get_object("label_monspeed")
        self.disp_switchspeed = builder.get_object("label_switchspeed")
        self.disp_end1000 = builder.get_object("label_end1000")
        self.disp_end1000r = builder.get_object("label_end1000r")
        self.disp_end500 = builder.get_object("label_end500")
        self.disp_forcebrake = builder.get_object("label_forcebrake")
        self.disp_sound = builder.get_object("label_sound")

        self.toggle_befehl = builder.get_object("toggle_befehl")
        self.toggle_frei = builder.get_object("toggle_frei")
        self.toggle_wachsam = builder.get_object("toggle_wachsam")

        self.adj_accel = builder.get_object("adj_accel")

        win = builder.get_object("top_window")
        win.show_all()

        GLib.timeout_add(100, self.sim_timer)

    def run(self):
        Gtk.main()

    def update_from_sim(self):
        self.disp_speed.set_text("%.0f" % self.sim.sim_speed)
        self.disp_dist.set_text("%.0f" % self.sim.sim_dist)

    def callback_simupdate(self, blau, magnet, befehl, acoust_signal, monspeed, switchspeed,
            forced_braking, end_1000, end_1000r, end_500):
        self.pzb_display.set_blau(blau)
        self.pzb_display.set_magnet(magnet)
        self.pzb_display.set_befehl(befehl)

        self.disp_monspeed.set_text("%.0f" % monspeed)
        self.disp_switchspeed.set_text("%.0f" % switchspeed)
        self.disp_end1000.set_text("%.0f" % max(end_1000, 0.0))
        self.disp_end1000r.set_text("%.0f" % max(end_1000r, 0.0))
        self.disp_end500.set_text("%.0f" % max(end_500, 0.0))

        if acoust_signal:
            self.disp_sound.set_text("EIN")
        else:
            self.disp_sound.set_text("aus")

        if forced_braking is not None:
            self.adj_accel.set_value(-100)
            self.disp_forcebrake.set_text(forced_braking)
        else:
            self.disp_forcebrake.set_text("-")

    def step_sim(self, active_magnet = 0):
        time = GLib.get_monotonic_time() / 1e6
        accel = self.adj_accel.get_value() / 100.0 * 3.6

        self.sim.step(time, accel, active_magnet, self.toggle_befehl.get_active(),
                self.toggle_frei.get_active(), self.toggle_wachsam.get_active())
        self.update_from_sim()

    def sim_timer(self):
        self.step_sim()
        return True

    def on_quit(self, widget):
        Gtk.main_quit()

    def on_zero_accel(self, widget):
        self.adj_accel.set_value(0)

    def on_reinit(self, widget):
        zugart_map = ("O", "M", "U")

        self.adj_accel.set_value(0)
        self.sim.reinit(init_time=GLib.get_monotonic_time() / 1e6,
                speed=float(self.init_speed.get_value()),
                ttype=zugart_map[self.init_zugart.get_active()],
                start=self.init_start.get_active())
        self.update_from_sim()

    def on_mag_1000(self, widget):
        self.step_sim(1000)

    def on_mag_500(self, widget):
        self.step_sim(500)

    def on_mag_2000(self, widget):
        self.step_sim(2000)

    def on_cmd_toggled(self, widget):
        self.step_sim()


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