from __future__ import print_function
import os
import json
import sys
import subprocess
import util 
import platform

def stdout_check_call(*args, **kwargs):
    print('calling: '+args[0])
    subprocess.check_call(*args, shell = True, stdout = sys.stdout, stderr = sys.stdout, **kwargs)

def find_VS_compiler(compilers, bits, ver):
    for c in compilers:
        if int(c['bits']) == bits and ver == c['ver']:
            return c
    raise ValueError('no VS compiler found')
    
def newest_VS_compiler(compilers, bits):
    joined = [(float(c['ver']),c) for c in compilers if int(c['bits']) == bits]
    joined = sorted(joined)
    joined.reverse()
    return joined[0][1]
        
def build_target(target, project_root, cpp_sources, project_name = 'Java'):
    """
    
    """
    if 'name_suffix' in target:
        suffix = '-' + target['name_suffix']
    else:
        suffix = ''
    build_name = platform.system() + '-' + target['compiler'] + '-' + str(target['bitness'])+'bit' + suffix
    build_path = os.path.join(project_name, build_name)
    try: 
        os.makedirs(build_path) 
    except: 
        pass
    dist_path = os.path.join(project_name, build_name)
    
    # Add any other sources needed
    if 'sources' in target:
        cpp_sources += target['sources']
        
    # Replace macros in sources
    for i,source in enumerate(cpp_sources):
        cpp_sources[i] = source.replace("%PACKAGE_HOME%", project_root)
    
    # Check that the platform agrees with this platform
    if platform.system() not in target['platform']:
        print('skipped this run since this platform is invalid:'+str(target))
    
    # Do any sort of preliminary processing needed based on the pre key
    if 'pre' in target:
        if not isinstance(target['pre'], basestring): 
            raise ValueError("target['pre'] is not a string: "+str(target['pre']))
        if target['pre'] != '':
            stdout_check_call(target['pre'])
    
    # Get compiler name
    compiler = target['compiler']
    if not isinstance(compiler, basestring): 
        raise ValueError("compiler is not a string: "+str(compiler))
        
    # Do the compilation step
    VS_keys = ['VS+','VS8.0','VS9.0','VS10.0','VS11.0','VS12.0','VS13.0','VS14.0','VS15.0']
    
    if compiler in VS_keys:
        vs_installs = util.find_VS_installations()
        if compiler == 'VS+':
            selected_compiler = newest_VS_compiler(vs_installs, int(target['bitness']))
        else:
            selected_compiler = find_VS_compiler(vs_installs, int(target['bitness']), compiler.lstrip('VS'))
            
        ### *************** COMPILE ********************
        call_dict = dict(c_flags = target['c_flags'], 
                         cpp_sources = ' '.join(cpp_sources),
                         bin_path = '"' + selected_compiler['bin_path'] + '"'
                         )
        
        for file in cpp_sources:
            call_dict['file'] = file
            
            path,ofile = os.path.split(file)
            
            # Get the output file name in the build folder
            call_dict['ofile'] = os.path.join(build_path, ofile.rsplit('.',1)[0]+'.obj')
            # Build the string to be run
            compile_string = 'cl /nologo /c {c_flags} /Fo{ofile} {file}'.format(**call_dict)
            # Actually run the build command using the environment for the selected compiler
            stdout_check_call(compile_string, env = selected_compiler['env'])
        
        if 'link_type' in target:
            if target['link_type'] == 'DLL':
                ### *************************** LINK ***************************
                call_dict = dict(link_flags = target['link_flags'], 
                                 bin_path = '"' + selected_compiler['bin_path'] + '"',
                                 build_path = build_path,
                                 link_fname = target['link_fname']
                                 )
                link_string = 'link /DLL /nologo {build_path}/*.obj {link_flags} /OUT:{build_path}/{link_fname}'.format(**call_dict)
                # Actually run the link command using the environment for the selected compiler
                stdout_check_call(link_string, env = selected_compiler['env'])
        
    elif compiler == 'GCC':
        pass
    elif compiler == 'clang':
        pass
    else:
        raise ValueError()
    
    # Do the post step
    if 'post' in target:
        if not isinstance(target['post'], basestring): 
            raise ValueError("target['post'] is not a string: "+str(target['post']))
        if target['post'] != '':
            stdout_check_call(target['post'])

def build_all_targets(project_root, project_name):
    json_file_name = os.path.relpath(os.path.join(project_root,'build.json'))
    
    if not os.path.exists(json_file_name): 
        raise OSError('File [{json_file_name}] does not exist'.format(**vars()))
        
    # Load the JSON file
    project = json.load(open(json_file_name,'r'))
    
    # Find all the C++ sources that will need to be compiled for sure
    cpp_sources = util.find_cpp_sources(os.path.join('..','..','src'))
    
    for target in project:
        build_target(target, project_root = project_root, cpp_sources = cpp_sources, project_name = project_name)

if __name__=='__main__':
    build_all_targets(project_root = '../../wrappers/EES', project_name = 'EES')
#     build_all_targets(project_root = '../../wrappers/C#', project_name = 'Csharp')
#     build_all_targets(project_root = '../../wrappers/Java', project_name = 'Java')
#     build_all_targets(project_root = '../../wrappers/SharedLibrary', project_name = 'SharedLibrary')