# MIT License
#
# Copyright (c) 2018 Lotus Engineering, LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

#
# Utility for converting qutest tcl scripts to pythong
#
import sys
import os
from qspypy.qspy import FILTER, QS_OBJ_KIND


def usage():
    print("qutest_convert.py:")
    print(" Converts one or more qutest Tcl scripts to Python.")
    print(" Example Usage:")
    print("     qutest_tcl2py test_example1.tcl test_example2.tcl")

def write_header(file, tcl_script):
    file.write('#\n')
    file.write(f'# Autogenerated Python Script from {tcl_script}\n')
    file.write('#\n')
    file.write(
        """import sys
import pytest
import struct
from qspypy.qspy import FILTER, QS_OBJ_KIND\n\n""")

def write_footer(file):
    file.write(
        """if __name__ == "__main__":
    options = ['-x', '-v', '--tb=short']
    options.extend(sys.argv)
    pytest.main(options)
""")

def convert(tcl_script):
    base=os.path.basename(tcl_script)
    name, ext = os.path.splitext(base)
    py_script = name + ".py"
    print("Generating file {} from {} ....\n".format(py_script, tcl_script))
    
    obj_kind_set = set([name for name, member in QS_OBJ_KIND.__members__.items()])
    filter_set = set([name for name, member in FILTER.__members__.items()])

    with open(tcl_script, 'r') as tcl:
        with open(py_script, 'w') as py:
            write_header(py, tcl_script)
            for line in tcl:
                if not line in ['\n', '\r\n']:
                    tokens = line.split()
                    if line.startswith('#'):                    
                        continue # Skip comment lines
                    elif line.startswith('}') or line.startswith('end'):
                        py.write('\n')
                    elif line.startswith('proc'):
                        proc_name = tokens[1]
                        py.write(f'def {proc_name}(qutest):\n')
                    elif line.startswith('test'):
                        name_tokens = line.split('"')
                        test_name = name_tokens[1].replace('->', '_')
                        test_name = test_name.replace(' (', '_')
                        test_name = test_name.replace(' ', '_')
                        test_name = test_name.replace('/', '_')
                        test_name = test_name.replace('(', '_')
                        test_name = test_name.rstrip(')')
                        if len(name_tokens) > 2 and '-noreset' in name_tokens[2]:
                            py.write(f'def test_{test_name}(qutest_noreset):\n')
                            py.write(f'    qutest = qutest_noreset # name change\n')
                        else:
                            py.write(f'def test_{test_name}(qutest):\n')
                    else:
                        line = line.lstrip()

                        def argmap(arg):
                            if not arg.isdigit() and not arg.startswith('0x'):
                                if arg in obj_kind_set:
                                    return f'QS_OBJ_KIND.{arg}'
                                else:
                                    return f'"{arg}"'
                            else:
                                return arg

                        def argmap_filter(arg):
                            if not arg.isdigit() and not arg.startswith('0x'):
                                if arg in filter_set:
                                    return f'FILTER.{arg}'
                                else:
                                    return f'"{arg}"'
                            else:
                                return arg

                        method = tokens[0]
                        if method == 'expect':
                            _, _, match = line.partition(" ")
                            arguments = match.rstrip() 
                        elif method == 'continue':
                            method = 'Continue'
                            arguments = ""
                        else:
                            if method == 'glb_filter':
                                arguments = ",".join(map(argmap_filter, tokens[1:]))
                            else:
                                arguments = ",".join(map(argmap, tokens[1:]))

                        py.write(f'    qutest.{method}({arguments})\n')
                else:
                    py.write(line)     
            write_footer(py)    

def create_conftest():
    fname = 'conftest.py'
    if not os.path.isfile(fname):
        with open(fname, 'w') as conf:
            conf.write('#\n') 
            conf.write('# Default qutest pytest configuration file\n')
            conf.write('#\n')             
            conf.write('\n') 
            conf.write('# Load common fixtures used throughout testing\n')
            conf.write('from qspypy.fixtures import session, reset, module, qutest, qutest_noreset\n')
            conf.write('\n') 


def main():   
    # Check number of arguments
    if len(sys.argv) < 2:
        usage()
        sys.exit()

    create_conftest()

    input_files = sys.argv[1:]
    for file in input_files:
        convert(file)

if __name__ == "__main__":
    main()