from __future__ import print_function
import subprocess, sys, six, os, re
import numpy

def generate_interface_file(REFPROP_FORTRAN_path, interface_file_path, verbose = False):
    """
    Use f2py to parse PASS_FTN.FOR to generate a python-directed header file
    """
    # Call f2py to generate .pyf file
    from subprocess import Popen, PIPE
    print('Writing the .pyf file with numpy.f2py, please be patient...')
    p = Popen(['python','-m','numpy.f2py','--quiet','--no-lower','-h',interface_file_path,REFPROP_FORTRAN_path], stdin=PIPE, stdout=PIPE, stderr=PIPE)
    output, err = p.communicate()
    rc = p.returncode
    # If the REFPROP.pyf file was not generated successfully, that's an error
    if not os.path.exists(interface_file_path):
        if verbose:
            print(output)
        raise ValueError('Unable to call f2py successfully: '+str(err))
        
def find_subroutine(lines, lineno):
    istart = -1; iend = -1
    for i in range(lineno, len(lines)):
        if 'subroutine' in lines[i]:
            if istart < 0 and iend < 0:
                istart = i
            elif istart >= 0 and iend < 0:
                iend = i
            else:
                break
                
    if istart < 0 and iend < 0:
        return None
    
    # Example: "subroutine pureflddll(icomp) ! in c:\Program Files (x86)\REFPROP\fortran\PASS_FTN.FOR"
    # Example: "subroutine unsetagadll ! in c:\Program Files (x86)\REFPROP\fortran\PASS_FTN.FOR"
    splitted = lines[istart].split(' ')
    sub, name_arguments = splitted[0], splitted[1]
    if '(' in name_arguments:
        name, arguments = name_arguments.split('(')
        arguments = arguments.rstrip(')').split(',')
    else:
        name, arguments = name_arguments, []    
    
    argument_list, string_arguments = [], []
    for offset, argument in enumerate(arguments):
        type, argname = lines[istart + 1 + offset].split(' :: ')

        """
        Examples:
        character*10000 intent(in) :: hfiles
        integer intent(out) :: ierr
        double precision dimension(20),intent(in) :: x0
        """
        # Split out the dimension and intent annotations
        matches = re.search(r'intent|dimension', type)
        intent_dimension = ''
        if matches:
            intent_dimension = type[matches.start(0)::]
            type = type[0:matches.start(0)]

        if 'character' in type:
            # Example: "     character*10000 :: hfld"
            if type.strip() == 'character*(*)':
                string_length = -1
            else:
                string_length = int(type.split('*')[1])
            string_arguments.append((argname.strip()+'_length', string_length))
            argument_list.append((argname.strip(), 'char *'))
        elif 'integer' in type:
            L = 0
            if 'dimension' in intent_dimension:
                L = re.findall(r'dimension\(([0-9]*)\)', intent_dimension)[0]
            argument_list.append((argname.strip(), 'int *', L))
        elif 'double' in type:
            L = 0
            if 'dimension' in intent_dimension:
                L = re.findall(r'dimension\(([0-9]*)\)', intent_dimension)[0]
            argument_list.append((argname.strip(), 'double *', L))
        else:
            raise ValueError(lines[istart + 1 + offset].strip())
        
    return dict(istart = istart,
                iend = iend,
                name = name,
                argument_list = argument_list,
                string_arguments = string_arguments
                )
        
def correct_name_case(name):
    """ Previously we had to modify the case; this function is a no-op now """
    return name
    
def arguments_to_string(args, string_arguments):
    outs = []
    for arg in args:
        if len(arg) == 2:
            outs.append(arg[1]+arg[0])
        elif len(arg) == 3:
            if arg[2] == 0:
                outs.append(arg[1]+arg[0])
            else:
                outs.append(arg[1]+arg[0]+'/* ' + arg[2] + " */")
        else:
            print(arg)
    for arg in string_arguments:
        outs.append('int '+arg[0]+'/* ' + str(arg[1]) + " */")
        
    return ', '.join(outs)
        
def generate_function_dict(pyf_file_path):
    """
    Returns JSON data structure for the functions
    """
    istart = 0
    with open(pyf_file_path, 'r') as fp:
        lines = fp.readlines()
        funcs = {}
        output = find_subroutine(lines, istart)
        while output is not None:
            output = find_subroutine(lines, output['iend']+1)
            if output is not None:
                funcname = correct_name_case(output['name'])
                funcs[funcname] = output
    return funcs

header = """
/*
This file was generated by a script in http://github.com/CoolProp/REFPROP-headers
originally written by Ian H. Bell, January 2016

This header should work on windows and linux
*/
"""
def write_header(function_dict, output_header_file):
    """
    Use python to parse the generated interface file
    """
    istart = 0
    ostring = header
    for function in sorted(function_dict.keys()):
        ostring += 'void ' + function + '(' + arguments_to_string(function_dict[function]['argument_list'],function_dict[function]['string_arguments']) + ');\n'

    with open(output_header_file,'w') as fp:
        fp.write(ostring)
    
if __name__=='__main__':
    # Uncomment for local testing
    # sys.argv += ['--FORTRAN-path', r'/Users/ihb/Downloads/911FILES']

    # Parse args first
    import argparse
    parser = argparse.ArgumentParser(description='Run the generator for .')
    parser.add_argument('--FORTRAN-path', nargs=1, required =True, default="", help="The directory containing the FORTRAN source files")
    parser.add_argument('--keep-pyf', nargs='?', const=True, default=False, help="If defined, the intermediate pyf file will be kept, otherwise it will be deleted")
    args = parser.parse_args()

    # Change these paths as needed
    generate_interface_file(os.path.join(args.FORTRAN_path[0],'PASS_FTN.FOR'), 'REFPROP.pyf')
    dd = generate_function_dict('REFPROP.pyf')

    write_header(dd,'REFPROP.h')
    if not args.keep_pyf:
        print('Deleting REFPROP.pyf')
        os.remove('REFPROP.pyf')

@@ -167,16 +167,39 @@
  = False
+, python_exe = 'python'
 ):%0A    %22
@@ -451,24 +451,26 @@
  Popen(%5B
-'
 python
-'
+_exe
 ,'-m','n
@@ -5902,16 +5902,140 @@
 leted%22)%0A
+    parser.add_argument('--python-exe', nargs=1, default=%22python%22, help=%22The python exe to be used to build the interface%22)%0A
     args
@@ -6181,24 +6181,57 @@
 REFPROP.pyf'
+, python_exe = args.python_exe%5B0%5D
 )%0A    dd = g

Allow python exe to be directly specified