import sys
import platform
import os
import pathlib
import tempfile
import subprocess
import time
import ctypes
import fnmatch

# This class is a wrapper for the Host OS specific portions of the t32_multi
# demo. It abstracts things like config files, system directory, loading of
# library files and of TRACE32 itself.
#
# Call with SYS="/path/to/t32_sys", if T32SYS is not in the default location.
# Windows   - C:\T32
# Mac/Linux - /opt/t32
#
# For example:
# osh=OSHelper(SYS="C:\\T32_New")

class OSHelper:
    def __init__(self, **kwargs):
        self.CanLaunch = 1
        self.ICPORT    = 0
        self.APIPORT   = 0
        self.SYSSET    = 0
        self.ConnType  = "SIM"
        self.SysDirSet = 0

        #Over-ride sysdir, if passed in as an argument
        if len(kwargs) >0:
            for key in kwargs:
                if key=="SYS":
                    self.set_sysdir(kwargs[key])
                    self.SYSSET = 1
            if os.path.isdir(self.get_sysdir())==False:
                print("{0} is not a valid directory".format(self.get_sysdir()))
                self.SYSSET = 0
                self.CanLaunch = 0
        else:
            if platform.system() == "Linux":
                self.setsysdir('/opt/t32')
                if platform.machine().endswith('64'):
                    #Linux64
                    self.set_apifile('t32api64.so')
                    self.set_binfile("t32mppc-qt")
                else:
                    #Linux32
                    self.set_apifile('t32api.so')
                    self.set_binfile("t32mppc-qt")
            elif platform.system() == "Darwin":
                # Mac OS
                self.setsysdir('/opt/t32')
                if platform.machine().endswith('64'):
                    #Mac OS64
                    self.set_apifile('libt32api64.so')
                    self.set_binfile("t32mppc-qt")
                else:
                    #Mac OS32
                    print("MacOSX 32bit is currently unsupported")
                    exit()
            elif platform.system() == "Windows":
                self.crt=ctypes.cdll.msvcrt
                self.set_sysdir('C:/T32')
                if platform.machine().endswith('64'):
                    #win64
                    self.set_apifile('t32api64.dll')
                    self.set_bindir("bin/windows64")
                    self.set_binfile("t32mppc.exe")
                else:
                    #win32
                    self.set_apifile('t32api.dll')
                    self.set_bindir("bin/windows")
                    self.set_binfile("t32mppc.exe")
            else:
                print("Unknown OS")
                exit()
            self.SYSSET = 1

    def set_connectiontype(self,conn):
        if not (conn=="SIM" or conn=="USB"):
            print("Illegal value passed for PBI. Please use either \'SIM\' or \'USB\'")
            self.CanLaunch = 0
        else:
            self.ConnType = conn
            self.CanLaunch = 1

    def set_apiport(self,ap):
        # Sanity check values
        if not (ap in range(0,65535)):
            print("Illegal value for APIPORT. Must be an integer between 0 and 65535!")
            self.CanLaunch = 0
        else:
            self.APIPORT = ap
            self.CanLaunch = 1

    def set_icport(self,ic):
        # Sanity check values
        if not (ic in range(0,65535)):
            print("Illegal value for Intercom PORT. Must be an integer between 0 and 65535!")
            self.CanLaunch = 0
        else:
            self.ICPORT = ic
            self.CanLaunch = 1

    def set_sysdir(self,dn):
        self.SYSDIR=dn

    def get_sysdir(self):
        return(self.SYSDIR)

    def set_apifile(self,ap):
        tmp=os.path.join("demo/api/python",ap)
        self.APIFILE=os.path.join(self.get_sysdir(),tmp)

    def get_apifile(self):
        if self.SYSSET==1:
            return(self.APIFILE)
        else:
            return(-1)

    def set_bindir(self,bindir):
        # Set the binary directory for the TRACE32 installation.
        # Check the directory is valid
        # Parse that directory for a list of TRACE32 executables
        self.BINDIR=os.path.join(self.get_sysdir(),bindir)
        if os.path.isdir(self.BINDIR)==False:
            print("{0} is not a valid directory".format(self.BINDIR))
            self.CanLaunch = 0
        else:
            self.EXES= []
            for file in os.listdir(self.BINDIR):
                if fnmatch.fnmatch(file,'t32m*'):
                    self.EXES.append(file)
            if len(self.EXES) < 1:
                print("No TRACE32 executables found in {0}".format(self.get_bindir()))
                self.CanLaunch=0

    def get_bindir(self):
        return(self.BINDIR)

    def set_binfile(self,t32f):
        self.BINFILE=os.path.join(self.get_bindir(),t32f)

    def get_binfile(self):
        return(self.BINFILE)

    def write_config_file(self):
        # First, create ./config-py.t32
        CONFIGDIR= pathlib.Path(__file__).parent
        FNAME="config-%d.t32" % (self.APIPORT)
        CONFIGFILE=os.path.join(CONFIGDIR,FNAME)
        fp = open(CONFIGFILE,"w+")                # Overwrite if file exists
        print(" ",CONFIGDIR)
        print(" ",CONFIGFILE)
        fp.write("OS=\n")
        fp.write("SYS="+self.get_sysdir()+"\n")
        fp.write("TMP="+tempfile.gettempdir()+"\n")
        fp.write("ID=t32_python_%d\n\n" % self.APIPORT)
        if "SIM" in self.ConnType:
            fp.write("PBI=SIM\n\n")
        if "USB" in self.ConnType:
            fp.write("PBI=\nUSB\n\n")
        fp.write("RCL=NETASSIST\n")
        fp.write("PACKLEN=1024\n")
        fp.write("PORT=%d\n\n" % self.APIPORT)
        fp.write("SCREEN=\n")
        fp.write("HEADER=TRACE32 Port %d\n\n" % self.APIPORT)

        # These next lines configure the Intercom to allow TRACE32 instances to
        # talk to each other. Mode details can be found in :
        # ~~/pdf/ide_ref.pdf in the INTERCOM section
        if self.ICPORT != 0:
            fp.write("IC=NETASSIST\n")
            fp.write("PORT=%d\n\n" % self.ICPORT)

        # Un-comment the following lines and configure correctly for your license
        # server (if required for simulator)
        #fp.write("LICENSE=\n")
        #fp.write("RLM_LICENSE=5055@localhost\n\n")
        fp.close()
        return(CONFIGFILE)

    def launch_trace32(self, EXE):

        # EXE - the name of the tRACE32 executable to launch, eg:
        # t32mppc.exe or t32mppc-qt
        #
        # Returns a handle to the new process (instance of TRACE32) or -1 on
        # failure

        # Check that EXE is valid
        ok = 0
        for e in self.EXES:
            if e==EXE:
                ok = 1
        if ok==0:
            print("Unable to launch {0} - not a valid TRACE32 executable".format(EXE))
            return(-1)

        if self.CanLaunch==0:
            print("Unable to launch. Please check configuration")
            return(-1)

        self.set_binfile(EXE)

        CFG=self.write_config_file()
        # Launch TRACE32 as a sub process - this prevents our script
        # from blocking
        t32process=subprocess.Popen([self.get_binfile(),'-c',CFG])
        # Wait a few seconds to give TRACE32 time to initialise - this may
        # be up to 8 seconds depending upon the hardware and connection
        # method. For SIM, we'll use 3 seconds.
        time.sleep(3)

        return(t32process)
