#!/usr/bin/env python3

import sys
if sys.version_info.minor < 6:
	raise Exception("Execute this script with at least python 3.6 to ensure all dicts are ordered!")

XRL_FUNCTIONS = {
		'AtomicWeight': {'Z': int},
		'ElementDensity': {'Z': int},
		'CS_Total': {'Z': int, 'E': float},
		'CS_Photo': {'Z': int, 'E': float},
		'CS_Rayl': {'Z': int, 'E': float},
		'CS_Compt': {'Z': int, 'E': float},
		'CS_Energy': {'Z': int, 'E': float},
		'CS_KN': {'E': float},
		'CSb_Total': {'Z': int, 'E': float},
		'CSb_Photo': {'Z': int, 'E': float},
		'CSb_Rayl': {'Z': int, 'E': float},
		'CSb_Compt': {'Z': int, 'E': float},
		'DCS_Thoms': {'theta': float},
		'DCS_KN': {'E':float, 'theta': float},
		'DCS_Rayl': {'Z': int, 'E':float, 'theta': float},
		'DCS_Compt': {'Z': int, 'E':float, 'theta': float},
		'DCSb_Rayl': {'Z': int, 'E':float, 'theta': float},
		'DCSb_Compt': {'Z': int, 'E':float, 'theta': float},
		'DCSP_Thoms': {'theta': float, 'phi': float},
		'DCSP_KN': {'E': float, 'theta': float, 'phi': float},
		'DCSP_Rayl': {'Z': int, 'E': float, 'theta': float, 'phi': float},
		'DCSP_Compt': {'Z': int, 'E': float, 'theta': float, 'phi': float},
		'DCSPb_Rayl': {'Z': int, 'E': float, 'theta': float, 'phi': float},
		'DCSPb_Compt': {'Z': int, 'E': float, 'theta': float, 'phi': float},
		'FF_Rayl': {'Z': int, 'q': float},
		'SF_Compt': {'Z': int, 'q': float},
		'MomentTransf': {'E': float, 'theta': float},
		'LineEnergy': {'Z': int, 'line': int},
		'FluorYield': {'Z': int, 'shell': int},
		'CosKronTransProb': {'Z': int, 'trans': int},
		'EdgeEnergy': {'Z': int, 'shell': int},
		'JumpFactor': {'Z': int, 'shell': int},
		'CS_FluorLine': {'Z': int, 'line': int, 'E': float},
		'CSb_FluorLine': {'Z': int, 'line': int, 'E': float},
		'RadRate': {'Z': int, 'line': int},
		'ComptonEnergy': {'E0': float, 'theta': float},
		'Fi': {'Z': int, 'E': float},
		'Fii': {'Z': int, 'E': float},
		'CS_Photo_Total': {'Z': int, 'E': float},
		'CSb_Photo_Total': {'Z': int, 'E': float},
		'CS_Photo_Partial': {'Z': int, 'shell': int, 'E': float},
		'CSb_Photo_Partial': {'Z': int, 'shell': int, 'E': float},
		'CS_Total_Kissel': {'Z': int, 'E': float},
		'CSb_Total_Kissel': {'Z': int, 'E': float},
		'ComptonProfile': {'Z': int, 'pz': float},
		'ComptonProfile_Partial': {'Z': int, 'shell': int, 'pz': float},
		'ElectronConfig': {'Z': int, 'shell': int},
		'ElectronConfig_Biggs': {'Z': int, 'shell': int},
		'AtomicLevelWidth': {'Z': int, 'shell': int},
		'AugerRate': {'Z': int, 'auger_trans': int},
		'AugerYield': {'Z': int, 'shell': int},
		'CS_FluorLine_Kissel': {'Z': int, 'line': int, 'E': float},
		'CSb_FluorLine_Kissel': {'Z': int, 'line': int, 'E': float},
		'CS_FluorLine_Kissel_Cascade': {'Z': int, 'line': int, 'E': float},
		'CSb_FluorLine_Kissel_Cascade': {'Z': int, 'line': int, 'E': float},
		'CS_FluorLine_Kissel_Nonradiative_Cascade': {'Z': int, 'line': int, 'E': float},
		'CSb_FluorLine_Kissel_Nonradiative_Cascade': {'Z': int, 'line': int, 'E': float},
		'CS_FluorLine_Kissel_Radiative_Cascade': {'Z': int, 'line': int, 'E': float},
		'CSb_FluorLine_Kissel_Radiative_Cascade': {'Z': int, 'line': int, 'E': float},
		'CS_FluorLine_Kissel_no_Cascade': {'Z': int, 'line': int, 'E': float},
		'CSb_FluorLine_Kissel_no_Cascade': {'Z': int, 'line': int, 'E': float},
		'PL1_pure_kissel': {'Z': int, 'E': float},
		'PL1_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float},
		'PL1_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float},
		'PL1_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float},
		'PL2_pure_kissel': {'Z': int, 'E': float, 'PL1': float},
		'PL2_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float},
		'PL2_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float},
		'PL2_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float},
		'PL3_pure_kissel': {'Z': int, 'E': float, 'PL1': float, 'PL2': float},
		'PL3_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float},
		'PL3_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float},
		'PL3_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float},
		'PM1_pure_kissel': {'Z': int, 'E': float},
		'PM1_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float},
		'PM1_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float},
		'PM1_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float},
		'PM2_pure_kissel': {'Z': int, 'E': float, 'PM1': float},
		'PM2_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float},
		'PM2_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float},
		'PM2_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float},
		'PM3_pure_kissel': {'Z': int, 'E': float, 'PM1': float, 'PM2': float},
		'PM3_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float},
		'PM3_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float},
		'PM3_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float},
		'PM4_pure_kissel': {'Z': int, 'E': float, 'PM1': float, 'PM2': float, 'PM3': float},
		'PM4_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float, 'PM3': float},
		'PM4_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float, 'PM3': float},
		'PM4_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float, 'PM3': float},
		'PM5_pure_kissel': {'Z': int, 'E': float, 'PM1': float, 'PM2': float, 'PM3': float, 'PM4': float},
		'PM5_rad_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float, 'PM3': float, 'PM4': float},
		'PM5_auger_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float, 'PM3': float, 'PM4': float},
		'PM5_full_cascade_kissel': {'Z': int, 'E': float, 'PK': float, 'PL1': float, 'PL2': float, 'PL3': float, 'PM1': float, 'PM2': float, 'PM3': float, 'PM4': float},

		'CS_Total_CP': {'compound': str, 'E': float},
		'CS_Photo_CP': {'compound': str, 'E': float},
		'CS_Rayl_CP': {'compound': str, 'E': float},
		'CS_Compt_CP': {'compound': str, 'E': float},
		'CS_Energy_CP': {'compound': str, 'E': float},
		'CSb_Total_CP': {'compound': str, 'E': float},
		'CSb_Photo_CP': {'compound': str, 'E': float},
		'CSb_Rayl_CP': {'compound': str, 'E': float},
		'CSb_Compt_CP': {'compound': str, 'E': float},
		'DCS_Rayl_CP': {'compound': str, 'E': float, 'theta': float},
		'DCS_Compt_CP': {'compound': str, 'E': float, 'theta': float},
		'DCSb_Rayl_CP': {'compound': str, 'E': float, 'theta': float},
		'DCSb_Compt_CP': {'compound': str, 'E': float, 'theta': float},
		'DCSP_Rayl_CP': {'compound': str, 'E': float, 'theta': float, 'phi': float},
		'DCSP_Compt_CP': {'compound': str, 'E': float, 'theta': float, 'phi': float},
		'DCSPb_Rayl_CP': {'compound': str, 'E': float, 'theta': float, 'phi': float},
		'DCSPb_Compt_CP': {'compound': str, 'E': float, 'theta': float, 'phi': float},
		'CS_Photo_Total_CP': {'compound': str, 'E': float},
		'CS_Total_Kissel_CP': {'compound': str, 'E': float},
		'CSb_Photo_Total_CP': {'compound': str, 'E': float},
		'CSb_Total_Kissel_CP': {'compound': str, 'E': float},
        'Refractive_Index_Re': {'compound': str, 'E': float, 'density': float},
        'Refractive_Index_Im': {'compound': str, 'E': float, 'density': float}
	}

def generate_iface_arg_for_str(arg_name: str) -> str:
    return f"{arg_name}:string"

def generate_iface_arg_for_float(arg_name: str) -> str:
    return f"{arg_name}:double"

def generate_iface_arg_for_int(arg_name: str) -> str:
    return f"{arg_name}:longint"

GENERATE_IFACE_ARG = {
	str: generate_iface_arg_for_str,
	float: generate_iface_arg_for_float,
	int: generate_iface_arg_for_int
}

def generate_iface_c_arg_for_str(arg_name: str) -> str:
    return f"{arg_name}:PAnsiChar"

GENERATE_IFACE_C_ARG = {
	str: generate_iface_c_arg_for_str,
	float: generate_iface_arg_for_float,
	int: generate_iface_arg_for_int
}

def generate_additional_var_for_str(arg_name: str) -> str:
    return f"{arg_name}_c:PAnsiChar;\n"

GENERATE_ADDITIONAL_VAR = {
        str: generate_additional_var_for_str,        
	float: lambda arg_name: "",
	int: lambda arg_name: ""
}

def generate_preprocess_for_str(arg_name: str) -> str:
    return f"{arg_name}_c := PAnsichar(AnsiString({arg_name}));\n"

GENERATE_PREPROCESS = {
	str: generate_preprocess_for_str,
	float: lambda arg_name: "",
	int: lambda arg_name: ""
}

GENERATE_CALL = {
	str: lambda arg_name: f"{arg_name}_c",
	float: lambda arg_name: f"{arg_name}",
	int: lambda arg_name: f"{arg_name}"
}

def process_function(name: str, iface_f, impl_f) -> str:

    args = XRL_FUNCTIONS[name]
    iface = "function {}({}):double;\n".format(name, '; '.join([GENERATE_IFACE_ARG[arg_type](arg_name) for arg_name, arg_type in args.items()]))
    iface_f.write(iface)
    iface_c = "function {}_C({}; error:PPxrl_error):double;cdecl;external External_library name '{}';\n".format(name, '; '.join([GENERATE_IFACE_C_ARG[arg_type](arg_name) for arg_name, arg_type in args.items()]), name)
    impl_f.write(iface_c)
    additional_vars = ''.join([GENERATE_ADDITIONAL_VAR[arg_type](arg_name) for arg_name, arg_type in args.items()])
    preprocess = ''.join([GENERATE_PREPROCESS[arg_type](arg_name) for arg_name, arg_type in args.items()])
    arg_list =  ', '.join([GENERATE_CALL[arg_type](arg_name) for arg_name, arg_type in args.items()])
    impl = f'''
{iface}
var
    error: Pxrl_error;
    rv: double;
    {additional_vars}
begin
    error := nil;
    {preprocess}
    rv := {name}_C({arg_list}, @error);
    process_error(error);
    result := rv
end;

'''
    impl_f.write(impl)

with open('xraylib_iface.pas', 'w') as iface, open('xraylib_impl.pas', 'w') as impl:
    iface.write('{ This file has been generated automatically using generate-code.py}\n\n')
    impl.write('{ This file has been generated automatically using generate-code.py}\n\n')
    for function in XRL_FUNCTIONS.keys():
        process_function(function, iface, impl)
