import target
from sccd.runtime.statecharts_core import Event
import threading
import fsaclasses
from fsa_cbd_woven_classes import CBDState
from CBDMultipleOutput.models.HarmonicOscilator import CircleCBDDerivative
from models.bouncingball import BouncingBallFSA
from woven_cbd_fsa_lib import FSACBDLib
from Options import Options
import os

CBD_DELTA = 0.01

def buildModel():
    statea = fsaclasses.State("a")
    stateb = fsaclasses.State("b")
    statec = CBDState("c", CircleCBDDerivative("Harmonic Oscilator", CBD_DELTA))
    #statec = State("c")
    stated = fsaclasses.State("d")
    statef = fsaclasses.State("f", True)
    
    transitionab = fsaclasses.Transition("a2b",statea,stateb)
    transitionab.trigger = fsaclasses.After(fsaclasses.Float(5.0))
    
    transitionbc = fsaclasses.Transition("b2c",stateb,statec)
    transitionbc.trigger = fsaclasses.After(fsaclasses.Float(2.0))

    transitioncd = fsaclasses.Transition("c2d",statec,stated)
    transitioncd.trigger = fsaclasses.Event("c")

    transitiondf = fsaclasses.Transition("d2f",stated,statef)
    transitiondf.trigger = fsaclasses.After(fsaclasses.Float(5.0))

    fsamodel = fsaclasses.FSAModel(
                        [statea,stateb,statec,stated],
                        [transitionab,transitionbc,transitioncd,transitiondf]
                        )
    fsamodel.initialState = statea
    return fsamodel

if __name__ == '__main__':    
    if os.path.exists(FSACBDLib.OUTPUT_EVENTS):
        os.remove(FSACBDLib.OUTPUT_EVENTS)
    if os.path.exists(FSACBDLib.OUTPUT_SIGNALS):
        os.remove(FSACBDLib.OUTPUT_SIGNALS)
    
    options = Options(delta = CBD_DELTA)
    options.setMaxIterations(10000);
    fsamodel = BouncingBallFSA()
    events = fsaclasses.Events([fsaclasses.RunTimeEvent('kick',4.0), fsaclasses.RunTimeEvent('enough',5.0)])
    # events = fsaclasses.Events([fsaclasses.RunTimeEvent('kick',13.0), fsaclasses.RunTimeEvent('kick',23.0), fsaclasses.RunTimeEvent('kick',37.0) ,fsaclasses.RunTimeEvent('enough',60.0)])
    #events = Events([RunTimeEvent('enough',25.0)])
    controller = target.Controller(fsamodel, events, options, keep_running=True)
    
    def set_defaults(inp, defaultlist):
        for i, v in enumerate(defaultlist):
            if len(inp) == i + 1:
                inp.append(v)
                
    def get_bool(val):
        if val.lower() in ["true", "yes", "1"]:
            return True
        else:
            return False

    def raw_inputter():
        while 1:
            inp = raw_input().split(" ")
            action = inp[0]
            if action == "continuous":
                params = []
            elif action == "realtime":
                set_defaults(inp, [1.0])
                params = [inp[1]]
            elif action == "big_step":
                params = []
            elif action == "small_step":
                params = []
            elif action == "step_into":
                params = []
            elif action == "pause":
                params = []
            elif action == "add_breakpoint":
                if len(inp) < 5:
                    print("Adding a breakpoint requires 4 parameters!")
                    continue
                # Merge together the final part again
                params = [inp[1], " ".join(inp[2:-2]).replace("\\n", "\n").replace("\\t", "\t"), get_bool(inp[-2]), get_bool(inp[-1])]
                print("Generated parameters: " + str(params))
            elif action == "del_breakpoint":
                if len(inp) < 2:
                    print("Breakpoint removal requires 1 parameter!")
                    continue
                params = [inp[1]]
            elif action == "toggle_breakpoint":
                params = [inp[1]]
            elif action == "list_breakpoints":
                params = []
            elif action == "god_event":
                if len(inp) != 2:
                    print("God events require 1 parameter!")
                    continue
                params = [inp[1]]
            elif action == "help":
                print("Supported operations:")
                print("  continuous")
                print("   --> Simulate until termination time is reached")
                print("  pause")
                print("   --> Pause the simulation")
                print("  realtime [realtime_scale]")
                print("   --> Simulate in realtime")
                print("       realtime_scale can speed up or slow down the pace")
                print("  big_step")
                print("   --> Simulate a big step")
                print("  small_step")
                print("   --> Simulate a small step")
                print("  add_breakpoint name function enabled disable_on_trigger")
                print("   --> Add a breakpoint that will pause simulation when function returns True")
                print("       the function should contain a definition of the 'breakpoint' function")
                print("       and must take 1 parameter")
                print("       enabled indicates whether or not the breakpoint should be checked")
                print("       disable_on_trigger determines if the breakpoint should auto-disable")
                print("       after being triggered")
                print("  del_breakpoint name")
                print("   --> Remove a breakpoint")
                print("  toggle_breakpoint name")
                print("   --> Toggle the designated breakpoint")
                print("  list_breakpoints")
                print("   --> List all breakpoints")
                print("  god_event new_state")
                print("   --> Updates the current state")
                print("")
                print("Defaults: ")
                print("  realtime_scale   --> 1.0")
                continue
            else:
                print("Command not understood: " + str(inp))
                continue
            controller.addInput(Event(action, "user_input", params))
            
    input_thread = threading.Thread(target=raw_inputter)
    input_thread.daemon = True
    input_thread.start()

    output_listener = controller.addOutputListener(["user_output"])
    def outputter():
        while 1:
            print output_listener.fetch(-1)
    output_thread = threading.Thread(target=outputter)
    output_thread.daemon = True
    output_thread.start()

    controller.start()