from collections import defaultdict

import binary_c
from binaryc_python_utils.custom_logging_functions import create_and_load_logging_function

def create_arg_string(arg_dict):
    """
    Function that creates the arg string
    """
    arg_string = '' 
    for key in arg_dict.keys():
        arg_string += "{key} {value} ".format(key=key, value=arg_dict[key])
    arg_string = arg_string.strip()
    return arg_string

def get_defaults():
    """
    Function that calls the binaryc get args function and cast it into a dictionary
    All the values are strings
    """
    default_output = binary_c.return_arglines()
    default_dict = {}

    for default in default_output.split('\n'):
        if not default  in ['__ARG_BEGIN', '__ARG_END', '']:
            key, value = default.split(' = ')

            # Filter out NULLS (not compiled anyway)
            if not value in ['NULL', 'Function']:
                if not value=='':
                    default_dict[key] = value
    return default_dict

def get_arg_keys():
    """
    Function that return the list of possible keys to give in the arg string
    """

    return get_defaults().keys()

def run_system(**kwargs):
    """
    Wrapper to run a system with settings 
    
    This function determines which underlying python-c api function will be called based upon the arguments that are passed via kwargs.

    - if custom_logging_code or custom_logging_dict is included in the kwargs then it will     
    - if 

    """

    # Load default args
    args = get_defaults()
    if 'custom_logging_code' in kwargs:
        # Use kwarg value to override defaults and add new args
        for key in kwargs.keys():
            if not key=='custom_logging_code':
                args[key] = kwargs[key]

        # Generate library and get memaddr
        func_memaddr = create_and_load_logging_function(kwargs['custom_logging_code'])

        # Construct arguments string and final execution string
        arg_string = create_arg_string(args)
        arg_string = 'binary_c {}'.format(arg_string)

        # Run it and get output
        output = binary_c.run_binary_custom_logging(arg_string, func_memaddr)
        return output

    elif 'log_filename' in kwargs:
        # Use kwarg value to override defaults and add new args
        for key in kwargs.keys():
            args[key] = kwargs[key]

        # Construct arguments string and final execution string
        arg_string = create_arg_string(args)
        arg_string = 'binary_c {}'.format(arg_string)

        # Run it and get output
        output = binary_c.run_binary_with_logfile(arg_string)
        return output

    else: # run the plain basic type

        # Use kwarg value to override defaults and add new args
        for key in kwargs.keys():
            args[key] = kwargs[key]

        # Construct arguments string and final execution string
        arg_string = create_arg_string(args)
        arg_string = 'binary_c {}'.format(arg_string)

        # Run it and get output
        output = binary_c.run_binary(arg_string)

        return output

def run_system_with_log(**kwargs):
    """
    Wrapper to run a system with settings AND logs the files to a designated place defined by the log_filename parameter.
    """

    # Load default args
    args = get_defaults()
    # args = {}

    # For example
    # physics_args['M_1'] = 20
    # physics_args['separation'] = 0 # 0 = ignored, use period
    # physics_args['orbital_period'] = 100000000000 # To make it single

    # Use kwarg value to override defaults and add new args
    for key in kwargs.keys():
        args[key] = kwargs[key]

    # Construct arguments string and final execution string
    arg_string = create_arg_string(args)
    arg_string = 'binary_c {}'.format(arg_string) 
    
    # print(arg_string)

    # Run it and get output
    buffer = ""
    output = binary_c.run_binary_with_log(arg_string)

    return output

def parse_output(output, selected_header):
    """
    Function that parses output of binaryc when it is construction like this:
    DAVID_SINGLE_ANALYSIS t=0 mass=20 

    You can give a 'selected_header' to catch any line that starts with that. 
    Then the values will be put into a dictionary.
    TODO: Think about exporting to numpy array or pandas

    """
    value_dicts = []
    val_lists = []

    # split output on newlines
    for i, line in enumerate(output.split('\n')):
        # Skip any blank lines
        if not line=='':
            split_line = line.split()

            # Select parts
            header = split_line[0]
            value_array = split_line[1:]

            # Catch line starting with selected header
            if header==selected_header:
                # print(value_array)
                # Make a dict 
                value_dict = {}
                for el in value_array:
                    key, val = el.split('=')
                    value_dict[key] = val
                value_dicts.append(value_dict)

    if len(value_dicts)==0:
        print('Sorry, didnt find any line matching your header {}'.format(selected_header))
        return None

    keys = value_dicts[0].keys()

    # Construct final dict.
    final_values_dict = defaultdict(list)
    for value_dict in value_dicts:
        for key in keys:
            final_values_dict[key].append(value_dict[key])

    return final_values_dict