#!<python_interpreter_path>

import csv
import json
import sys
import os
import glob
from pathlib import Path


def to_bool(value):
    """
    Converts a string or float value (like "1.000000e+00") to a boolean.
    Returns True if the numeric value is greater than 0, False otherwise.
    """
    try:
        return float(value) > 0
    except (ValueError, TypeError):
        return False


def convert_csv_to_json(csv_file_path: str, json_file_path: str):
    """
    Reads a CSV file, converts its data into the target JSON format, 
    and writes the result to a new JSON file. The conversion logic is 
    simplified to only map the required feature set.
    """
    json_data = []

    # --- 1.  JSON  ---
    #  (int)
    callee_map = {
        'callee_Blocks': 'Blocks',
        'callee_Loops': 'Loops',
        'callee_Calls': 'Calls',
        'callee_ConditionalBranch': 'ConditionalBranch',
    }

    #  (int)
    caller_map = {
        'caller_Blocks': 'Blocks',
        'caller_Loops': 'Loops',
        'caller_Calls': 'Calls',
        'caller_ConditionalBranch': 'ConditionalBranch',
    }

    # : is_indirect (bool), num_loops (int), cost_estimate (float)
    generic_map = {
        'is_indirect': 'is_indirect',
        'num_loops': 'num_loops',
        'cost_estimate': 'cost_estimate',
    }
    # ----------------------------------------------------

    try:
        with open(csv_file_path, mode='r', newline='', encoding='utf-8') as csv_file:
            #  DictReader
            csv_reader = csv.DictReader(csv_file)

            for row in csv_reader:
                #
                entry = {
                    'name': row.get('function_name', 'unknown'),
                    'features': {
                        'callee': {},
                        'caller': {},
                        'generic': {}
                    }
                }

                # --- 2.  callee features () ---
                for csv_key, json_key in callee_map.items():
                    value = row.get(csv_key)
                    if value is not None:
                        #
                        try:
                            entry['features']['callee'][json_key] = int(
                                float(value))
                        except ValueError:
                            entry['features']['callee'][json_key] = 0  # 0

                # --- 3.  caller features () ---
                for csv_key, json_key in caller_map.items():
                    value = row.get(csv_key)
                    if value is not None:
                        #
                        try:
                            entry['features']['caller'][json_key] = int(
                                float(value))
                        except ValueError:
                            entry['features']['caller'][json_key] = 0

                # --- 4.  generic features () ---
                for csv_key, json_key in generic_map.items():
                    value = row.get(csv_key)
                    if value is not None:
                        try:
                            if json_key == 'is_indirect':
                                entry['features']['generic'][json_key] = to_bool(
                                    value)
                            elif json_key == 'num_loops':
                                entry['features']['generic'][json_key] = int(
                                    float(value))
                            elif json_key == 'cost_estimate':
                                entry['features']['generic'][json_key] = float(
                                    value)
                        except ValueError:
                            #
                            if json_key == 'is_indirect':
                                entry['features']['generic'][json_key] = False
                            else:
                                entry['features']['generic'][json_key] = 0.0

                json_data.append(entry)

        #  JSON
        os.makedirs(Path(json_file_path).parent, exist_ok=True)
        with open(json_file_path, 'w', encoding='utf-8') as json_file:
            #  ensure_ascii=False
            json.dump(json_data, json_file, indent=2, ensure_ascii=False)

        print(
            f"Successfully converted CSV data from '{csv_file_path}' to JSON and saved to '{json_file_path}'.")

    except FileNotFoundError:
        print(f"Error: The file '{csv_file_path}' was not found.")
    except Exception as e:
        print(f"An error occurred during conversion: {e}")

# ---  () ---


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python feature_converter.py <directory_path>")
        sys.exit(1)

    DIR_PATH = sys.argv[1]

    # CSV
    csv_files = glob.glob(os.path.join(DIR_PATH, "*.csv"))

    if not csv_files:
        print(f"No CSV files found in {DIR_PATH}")
        sys.exit(1)

    # CSV
    for csv_file_path in csv_files:
        #  JSON
        base_name = os.path.splitext(os.path.basename(csv_file_path))[0]
        json_file_path = os.path.join(
            f"{os.path.dirname(os.path.dirname(csv_file_path))}/jsons", f"{base_name}.json")

        print(f"Processing {csv_file_path} -> {json_file_path}")

        # CSV
        with open(csv_file_path, 'r') as f:
            csv_content = f.read()

        #
        csv_content = csv_content.replace(" ", ",")

        #
        if not csv_content.startswith("function_name,call"):
            header = "function_name,callee_BlockWithMultipleSuccecorsPerLoop,callee_PtrArgs,callee_MaxDomTreeLevel,callee_IsLinkOnceODR,callee_IsLocal,callee_Calls,callee_Blocks,callee_InitialSize,callee_MaxLoopDepth,callee_users,callee_InstructionPerBlock,callee_Loops,callee_conditionally_executed_blocks,callee_IsLinkOnce,callee_basic_block_count,callee_PtrCallee,callee_CallReturnPtr,callee_ConditionalBranch,callee_CBwithArg,callee_CallerHeight,callee_CallUsage,callee_IsRecursive,callee_NumCallsiteInLoop,callee_NumOfCallUsesInLoop,callee_EntryBlockFreq,callee_MaxCallsiteBlockFreq,callee_SuccessorPerBlock,callee_AvgVecInstr,callee_AvgNestedLoopLevel,callee_InstrPerLoop,caller_BlockWithMultipleSuccecorsPerLoop,caller_PtrArgs,caller_MaxDomTreeLevel,caller_IsLinkOnceODR,caller_IsLocal,caller_Calls,caller_Blocks,caller_InitialSize,caller_MaxLoopDepth,caller_users,caller_InstructionPerBlock,caller_Loops,caller_conditionally_executed_blocks,caller_IsLinkOnce,caller_basic_block_count,caller_PtrCallee,caller_CallReturnPtr,caller_ConditionalBranch,caller_CBwithArg,caller_CallerHeight,caller_CallUsage,caller_IsRecursive,caller_NumCallsiteInLoop,caller_NumOfCallUsesInLoop,caller_EntryBlockFreq,caller_MaxCallsiteBlockFreq,caller_SuccessorPerBlock,caller_AvgVecInstr,caller_AvgNestedLoopLevel,caller_InstrPerLoop,is_indirect,num_loops,opt_code,unsimplified_common_instructions,mandatory_only,switch_penalty,mandatory_kind,case_cluster_penalty,loop_level,jump_table_penalty,cost_estimate,indirect_call_penalty,nr_ctant_params,lowered_call_arg_setup,callsite_height,load_relative_intrinsic,block_freq,call_argument_setup,call_penalty,load_elimination,hot_callsite,sroa_losses,cold_callsite,sroa_savings,is_in_inner_loop,dead_blocks,is_must_tail,simplified_instructions,is_tail,constant_args,constant_offset_ptr_args,callsite_cost,cold_cc_penalty,last_call_to_static_bonus,is_multiple_blocks,nested_inlines,nested_inline_cost_estimate,threshold,node_count,edge_count,Classes\n"
            csv_content = header + csv_content

        # CSV
        with open(csv_file_path, 'w', newline='', encoding='utf-8') as f:
            f.write(csv_content)

        #  JSON
        convert_csv_to_json(csv_file_path, json_file_path)

    print("All CSV files processed successfully.")
