import ctypes
import os

class Core:
    """Base class to handle the shared library loading."""
    def __init__(self):
        base_path = os.path.dirname(os.path.abspath(__file__))
        lib_path = os.path.join(base_path, "libcalc.so")
        self.lib = ctypes.CDLL(lib_path)

class Math(Core):
    def __init__(self):
        super().__init__()
        # --- MATH HANDSHAKE ---
        # compile(char*): returns a pointer (void*)
        self.lib.compile.restype = ctypes.c_void_p
        self.lib.compile.argtypes = [ctypes.c_char_p]
        
        # execute_bytecode(void*, double): returns a double
        self.lib.execute_bytecode.restype = ctypes.c_double
        self.lib.execute_bytecode.argtypes = [ctypes.c_void_p, ctypes.c_double]
        
        # execute_bulk(void*, int): returns a double
        self.lib.execute_bulk.restype = ctypes.c_double
        self.lib.execute_bulk.argtypes = [ctypes.c_void_p, ctypes.c_int]

    def compile(self, equation: str):
        """String -> Bytecode Pointer (The 'target' for evaluation)"""
        return self.lib.compile(equation.encode('utf-8'))

    def evaluate(self, target, x=0.0, iterations=1):
        """
        Runs the compiled bytecode. 
        If target is still a string, it compiles it on the fly.
        """
        if isinstance(target, str):
            target = self.compile(target)
        
        if iterations > 1:
            return self.lib.execute_bulk(target, int(iterations))
        
        return self.lib.execute_bytecode(target, float(x))



# Initialize modules
math = Math()
