import unittest
import sys
import os

import sys
import time
import json
import urllib
import urllib2
import subprocess
import signal

username = "test_user"

def serialize(value):
    if isinstance(value, str):
        return '"%s"' % value
    else:
        return str(value)

def execute(scriptname, parameters=[], wait=False):
    if os.name == "nt":
        command = ["%s.bat" % scriptname] + parameters
    elif os.name == "posix":
        command = ["./%s.sh" % scriptname] + parameters
    else:
        raise Exception("Unknown OS: " + str(os.name))

    if wait:
        return subprocess.call(command, shell=False)
    else:
        return subprocess.Popen(command, shell=False)

def kill(process):
    if os.name == "nt":
        subprocess.call(["taskkill", "/F", "/T", "/PID", "%i" % process.pid])
    elif os.name == "posix":
        subprocess.call(["pkill", "-P", "%i" % process.pid])

def flush_data(data):
    if data:
        urllib2.urlopen(urllib2.Request("http://localhost:8001/", urllib.urlencode({"op": "set_input", "data": json.dumps(data), "username": username})), timeout=10).read()
    return []

def run_file(files, parameters, expected, mode):
    # Resolve file
    import os.path

    time.sleep(0.01)
    try:
        # Run Modelverse server
        proc = execute("run_local_modelverse", ["bootstrap/bootstrap.m"], wait=False)

        try:
            for filename in files:
                if filename == "--fast":
                    continue
                if os.path.isfile("integration/code/%s" % filename):
                    mod_filename = "integration/code/%s" % filename
                elif os.path.isfile("bootstrap/%s" % filename):
                    mod_filename = "bootstrap/%s" % filename
                else:
                    raise Exception("File not found: %s" % filename)
                print("Found file " + str(mod_filename))

                # Load in the file required
                timeout_val = 120
                while 1:
                    proc2 = execute("compile", [mod_filename, username, filename, mode], wait=False)

                    while proc2.returncode is None:
                        time.sleep(0.01)
                        proc2.poll()
                        timeout_val -= 0.01
                        if timeout_val < 0:
                            kill(proc2)
                            print("Compilation timeout expired!")
                            return False
                    if proc2.returncode != 7:
                        break

                # Make sure everything stopped correctly
                assert proc2.returncode == 0
                if proc2.returncode != 0:
                    return False

            if mode[-1] == "O":
                # Fire up the linker
                val = execute("link_and_load", [username] + files, wait=True)
                if val != 0:
                    raise Exception("Linking error")

            # Send in the actual request and wait for replies
            data = []
            for p in parameters:
                if isinstance(p, tuple):
                    for v in p:
                        data.append(["V", serialize(v)])
                else:
                    data.append(["V", serialize(p)])
            flush_data(data)
            
            for e in expected:
                c = len(e) if isinstance(e, set) else 1
                for _ in range(c):
                    val = urllib2.urlopen(urllib2.Request("http://localhost:8001/", urllib.urlencode({"op": "get_output", "username": username})), timeout=10).read()
                    val = val.split("=", 2)[2]
                    print("Got %s, expect %s" % (val, e))
                    if isinstance(e, set):
                        assert str(val) in e
                        if str(val) not in e:
                            return False
                    else:
                        assert str(val) == e
                        if str(val) != e:
                            return False

            # All passed!
            return True
        except:
            raise
        finally:
            try:
                kill(proc2)
            except UnboundLocalError:
                pass
    except:
        raise
    finally:
        try:
            kill(proc)
        except UnboundLocalError:
            pass
            
def run_barebone(parameters, expected, interface="0", timeout=False, wait=False, link=None):
    try:
        # Run Modelverse server
        proc = execute("run_local_modelverse", ["bootstrap/bootstrap.m"], wait=False)

        # Create user and set interface
        timeout_val = 15
        start = time.time()
        while 1:
            try:
                urllib2.urlopen(urllib2.Request("http://localhost:8001/", urllib.urlencode({"op": "set_input", "element_type": "V", "value": '"%s"' % username, "username": "user_manager"})), timeout=1).read()
                if interface is not None:
                    urllib2.urlopen(urllib2.Request("http://localhost:8001/", urllib.urlencode({"op": "set_input", "element_type": "V", "value": interface, "username": username})), timeout=1).read()
                break
            except:
                time.sleep(0.01)
                if time.time() - start > timeout_val:
                    raise

        # Send in the actual request and wait for replies
        var_list = {}
        data = []
        for p in parameters:
            if isinstance(p, int):
                if p not in var_list:
                    data = flush_data(data)
                    val = urllib2.urlopen(urllib2.Request("http://localhost:8001/", urllib.urlencode({"op": "get_output", "username": username})), timeout=10).read()
                    val = val.split("=", 2)[1].split("&", 1)[0]
                    var_list[p] = val
                    continue
                else:
                    val = var_list[p]
                    t = "R"
            else:
                val = p
                t = "V"
            data.append([t, val])
        data = flush_data(data)

        # Now do linking and loading
        if link is not None:
            # Execute linker
            timeout_val = 10
            proc2 = execute("link_and_load", [username] + link, wait=False)

            while proc2.returncode is None:
                time.sleep(0.01)
                proc2.poll()
                timeout_val -= 0.01
                if timeout_val < 0:
                    kill(proc2)
                    print("Linking timeout expired!")
                    return False

        counter = 0
        for e in expected:
            print("Expect " + str(e))
            c = len(e) if isinstance(e, set) else 1
            for _ in range(c):
                try:
                    val = urllib2.urlopen(urllib2.Request("http://localhost:8001/", urllib.urlencode({"op": "get_output", "username": username})), timeout=15).read()
                except:
                    if timeout:
                        return True
                    else:
                        raise
                val = val.split("=", 2)[2]

                print("Got %s, expect %s" % (val, e))
                if isinstance(e, set):
                    assert str(val) in e
                    if str(val) not in e:
                        return False
                else:
                    assert str(val) == e
                    if str(val) != e:
                        return False

        # All passed!
        return not timeout
    finally:
        kill(proc)
