"""
Code generator for enum bindings in the Python extension. 
Reads the sls_detector_defs.h and enums_in.cpp then outputs 
enums.cpp

"""


import re
import subprocess

from parse import remove_comments

allow_bitwise_op = ["M3_GainCaps"]
allow_bitwise_op = ["streamingInterface"]

op_key = {"operator|": "__or__", 
          "operator&" : "__and__"}

def single_line_enum(line):
    sub = line[line.find('{')+1:line.find('}')]
    return sub.strip().split(',')

def extract_enums(lines):

    # deal with enum class streamingInterface : int32_t
    # and normal enum burstMode {

    line_iter = iter(lines)
    enums = {}
    for line in line_iter:
        #Hack away class enum defs
        if "class" in line:
            line = line.replace("class", "")
            line = line.replace(line[line.find(':'):line.find('{')], "")
            line = line.replace("  ", " ")

        m = re.search("(?<=enum )\w+(?= {)", line)
        if m:
            enum_name = m.group()
            print(enum_name)
            # print(line)
            fields = []

            #deal with single line enums
            if '};' in line:
                fields = single_line_enum(line)
            else:
                #deal with multi line enums
                while True:
                    l  = next(line_iter)
                    if '};' in l:
                        break
                    m = re.search("\w+", l)
                    try:
                        # print('\t', m.group())
                        fields.append(m.group())

                    except:
                        pass
            fields = [f.strip() for f in fields]
            enums[enum_name] = fields


    #Loop again to find operators
    for key in enums:
        for line in lines:
            if key in line and "operator" in line:
                pos = line.find("operator")
                op_type = line[pos:pos+9]
                enums[key].append(op_type)
    return enums


def generate_enum_string(enums):
    data = []
    for key, value in enums.items():
        if key in allow_bitwise_op:
            tag=", py::arithmetic()"
        else:
            tag=""
        data.append(f'py::enum_<slsDetectorDefs::{key}>(Defs, "{key}"{tag})\n')
        operators = []
        for v in value:
            if "operator" not in v:
                data.append(f'\t.value("{v}", slsDetectorDefs::{key}::{v})\n')
            else:
                operators.append(v)
        data.append('\t.export_values()')

        #Here add the operators 
        for op in operators:
            data.append(f"\n\t.def(\"{op_key[op]}\", py::overload_cast< const slsDetectorDefs::streamingInterface&,  const slsDetectorDefs::streamingInterface&>(&{op}))")


        data.append(';\n\n')
    return ''.join(data)


def remove_ifdefs(lines):
    """Keeps C++ version of the code"""
    out = []
    it = iter(lines)
    skip = False
    for line in it:
        
        if "#ifdef __cplusplus" in line:
            line = next(it)

        if "#else" in line:
            skip = True

        if "#endif" in line:
            skip = False

        if not skip and "#endif" not in line:    
            out.append(line)
    return out
    

with open('../../slsSupportLib/include/sls/sls_detector_defs.h') as f:
    data = f.read()

data = remove_comments(data)
data = data.splitlines()
data = remove_ifdefs(data)
enums = extract_enums(data)


s = generate_enum_string(enums)

# print(s)
# for i, line in enumerate(data):
#     print(i, line)


with open('../src/enums_in.cpp') as f:
    data = f.read()

text = data.replace('[[ENUMS]]', s)
warning = '/* WARINING This file is auto generated any edits might be overwritten without warning */\n\n'
with open('../src/enums.cpp', 'w') as f:
    f.write(warning)
    f.write(text)


# run clang format on the output
subprocess.run(['clang-format', '../src/enums.cpp', '-i'])