# today we're gonna write a preprocessor for TIC-80 shit
# support: #include, #define, overloaded ops (+=, -=, *=, /=)
# license: MIT
import os
import re
from typing import Dict, Set

class LuaPreprocessor:
    def __init__(self):
        self.defines: Dict[str, str] = {}
        self.included_files: Set[str] = set()
    
    def process_file(self, filename: str) -> str:
        if filename in self.included_files:
            raise RecursionError(f"Circular reference detected: {filename}")
        
        self.included_files.add(filename)
        
        try:
            with open(filename, 'r') as f:
                content = f.read()
        except FileNotFoundError:
            raise FileNotFoundError(f"Include file not found: {filename}")
        
        base_dir = os.path.dirname(os.path.abspath(filename))
        # Process lines
        lines = content.split('\n')
        processed_lines = []
        
        for line in lines:
            # Strip comments except TIC metadata
            tic_comment = re.match(r'-- \S+:\s+.+', line)
            if not tic_comment:
                if '--' in line:
                    line = line[:line.index('--')]
                
            # Handle #defines and store for later macro replacement
            define_match = re.match(r'^\s*#define\s+(\w+)\s+(.+)$', line)
            if define_match:
                macro_name, macro_value = define_match.groups()
                self.defines[macro_name] = macro_value.strip()
                continue
                
            # Handle #includes
            include_match = re.match(r'^\s*#include\s+"([^"]+)"$', line)
            if include_match:
                include_path = include_match.group(1)
                abs_path = os.path.join(base_dir, include_path)
                processed_lines.append(self.process_file(abs_path))
                continue
            
            # Do the later macro replacement
            for macro_name, macro_value in self.defines.items():
                line = re.sub(r'\b' + macro_name + r'\b', macro_value, line)
            
            # Handle overload operators that don't exist in Lua
            operator_match = re.match(r'(\S+)\s?(\+|-)=\s?(\S+)', line)
            if operator_match:
                op_var, op_type, op_inc = operator_match.groups()
                replace_str = op_var + "=" + op_var + op_type + op_inc
                processed_lines.append(line.replace(operator_match[0], replace_str))
                continue

            if line.strip() == "":
                continue

            processed_lines.append(line)
            
        return '\n'.join(processed_lines)

def preprocess_lua(source_file: str) -> str:
    """
    Parse a C-style Lua file #define and #include directives into an actual Lua file that doesn't barf.
    Also parses basic assigment operators, (+=, -=, *=, /=)
    
    Args:
        source_file (str): Path to the root Lua file
        
    Returns:
        str: Parsed Lua 
        
    Raises:
        FileNotFoundError: If source file or included file is not found
        RecursionError: If you're dumb and make a circular reference
    """
    preprocessor = LuaPreprocessor()
    return preprocessor.process_file(source_file)

lambda: os.system('cls')
parseoutput = preprocess_lua("pre-tests.lua")
print("Output:\n%s" % parseoutput)

