import sys
import re
import time
import random
import math

# --- TOKEN TYPES ---
TT_INT        = 'INT'
TT_STRING     = 'STRING'
TT_IDENTIFIER = 'IDENTIFIER'
TT_COLON      = 'COLON'
TT_LPAREN     = 'LPAREN'
TT_RPAREN     = 'RPAREN'
TT_DOT        = 'DOT'
TT_EQUALS     = 'EQUALS'
TT_COMMA      = 'COMMA'
TT_LBRACKET   = 'LBRACKET'
TT_RBRACKET   = 'RBRACKET'
TT_PLUS       = 'PLUS'
TT_MINUS      = 'MINUS'
TT_BOOL       = 'BOOL'
TT_SEMICOLON  = 'SEMICOLON' # For alt; also; not;

# Comparison
TT_EE         = 'EE'   
TT_LTE        = 'LTE'  
TT_GTE        = 'GTE'  
TT_LT         = 'LT'   
TT_GT         = 'GT'   

class Token:
    def __init__(self, type_, value=None):
        self.type = type_
        self.value = value
    def __repr__(self):
        return f'{self.type}:{self.value}' if self.value is not None else f'{self.type}'

# --- LEXER ---
class Lexer:
    def __init__(self, text):
        text = re.sub(r'~~.*?~~', '', text)
        text = re.sub(r'~~.*', '', text)
        self.text = text
        self.pos = -1
        self.current_char = None
        self.advance()

    def advance(self):
        self.pos += 1
        self.current_char = self.text[self.pos] if self.pos < len(self.text) else None
    
    def peek(self):
        peek_pos = self.pos + 1
        return self.text[peek_pos] if peek_pos < len(self.text) else None

    def make_tokens(self):
        tokens = []
        while self.current_char is not None:
            if self.current_char in ' \t\n\r': self.advance()
            elif self.current_char.isdigit(): tokens.append(self.make_number())
            elif self.current_char.isalpha(): tokens.append(self.make_identifier())
            elif self.current_char in ('"', "'"): tokens.append(self.make_string())
            elif self.current_char == '+': tokens.append(Token(TT_PLUS)); self.advance()
            elif self.current_char == '-': tokens.append(Token(TT_MINUS)); self.advance()
            elif self.current_char == ';': tokens.append(Token(TT_SEMICOLON)); self.advance()
            elif self.current_char == '=':
                if self.peek() == '=':
                    self.advance(); tokens.append(Token(TT_EE)); self.advance()
                elif self.peek() == '>': # Support => for GTE
                    self.advance(); tokens.append(Token(TT_GTE)); self.advance()
                else: tokens.append(Token(TT_EQUALS)); self.advance()
            elif self.current_char == '<':
                if self.peek() == '=':
                    self.advance(); tokens.append(Token(TT_LTE)); self.advance()
                else: tokens.append(Token(TT_LT)); self.advance()
            elif self.current_char == '>':
                if self.peek() == '=':
                    self.advance(); tokens.append(Token(TT_GTE)); self.advance()
                else: tokens.append(Token(TT_GT)); self.advance()
            elif self.current_char == ':': tokens.append(Token(TT_COLON)); self.advance()
            elif self.current_char == '(': tokens.append(Token(TT_LPAREN)); self.advance()
            elif self.current_char == ')': tokens.append(Token(TT_RPAREN)); self.advance()
            elif self.current_char == '[': tokens.append(Token(TT_LBRACKET)); self.advance()
            elif self.current_char == ']': tokens.append(Token(TT_RBRACKET)); self.advance()
            elif self.current_char == '.': tokens.append(Token(TT_DOT)); self.advance()
            elif self.current_char == ',': tokens.append(Token(TT_COMMA)); self.advance()
            else: self.advance()
        return tokens, None
    
    def make_number(self):
        num_str = ''
        while self.current_char is not None and self.current_char.isdigit():
            num_str += self.current_char
            self.advance()
        return Token(TT_INT, int(num_str))

    def make_string(self):
        string = ''
        quote_type = self.current_char
        self.advance()
        while self.current_char is not None and self.current_char != quote_type:
            string += self.current_char
            self.advance()
        self.advance()
        return Token(TT_STRING, string)
    
    def make_identifier(self):
        id_str = ''
        while self.current_char is not None and (self.current_char.isalnum() or self.current_char == '_'):
            id_str += self.current_char
            self.advance()
        if id_str == 'true': return Token(TT_BOOL, True)
        if id_str == 'false': return Token(TT_BOOL, False)
        return Token(TT_IDENTIFIER, id_str)

# --- NODES ---
class CondNode:
    def __init__(self, mode, condition_tokens, body):
        self.mode = mode # 'if' or 'during'
        self.condition_tokens = condition_tokens
        self.body = body

class DefineNode:
    def __init__(self, dtype, name, args=None, body=None):
        self.dtype, self.name, self.args, self.body = dtype, name, args, body

class SetNode:
    def __init__(self, library, name, expression):
        self.library, self.name, self.expression = library, name, expression

class LogNode:
    def __init__(self, lib, name, index=None, raw_val=None):
        self.lib, self.name, self.index, self.raw_val = lib, name, index, raw_val

class FuncExecuteNode:
    def __init__(self, name, args):
        self.name, self.args = name, args

# Added for v0.3.0: Delay statement
class DelayNode:
    def __init__(self, seconds_expr):
        self.seconds_expr = seconds_expr

# --- PARSER ---
class Parser:
    def __init__(self, tokens):
        self.tokens, self.tok_idx = tokens, -1
        self.advance()

    def advance(self):
        self.tok_idx += 1
        self.current_tok = self.tokens[self.tok_idx] if self.tok_idx < len(self.tokens) else None

    def get_block(self):
        body = []
        while self.current_tok:
            if self.current_tok.type == TT_COLON:
                if self.tok_idx + 1 < len(self.tokens) and self.tokens[self.tok_idx+1].value == 'end':
                    self.advance(); self.advance()
                    break
            stmt = self.statement()
            if stmt: body.append(stmt)
        return body

    def statement(self):
        if not self.current_tok: return None

        # --- NEW COND() SYNTAX ---
        if self.current_tok.value == 'cond':
            self.advance(); self.advance() # cond (
            mode = self.current_tok.value # 'if' or 'during'
            self.advance()
            expr = []
            while self.current_tok.type != TT_RPAREN:
                expr.append(self.current_tok); self.advance()
            self.advance(); self.advance() # ) :
            return CondNode(mode, expr, self.get_block())

        if self.current_tok.value == 'define':
            self.advance(); self.advance(); self.advance(); self.advance() 
            dtype = self.current_tok.value
            self.advance(); self.advance() 
            name = self.current_tok.value
            self.advance(); self.advance() 
            if dtype == 'Func':
                self.advance(); self.advance() 
                args = []
                while self.current_tok.type != TT_RBRACKET:
                    if self.current_tok.type == TT_IDENTIFIER and self.current_tok.value != 'None':
                        args.append(self.current_tok.value)
                    self.advance()
                self.advance(); self.advance() 
                return DefineNode(dtype, name, args, self.get_block())
            return DefineNode(dtype, name)

        if self.current_tok.value in ('vars', 'arrs', 'funcs'):
            lib = self.current_tok.value
            self.advance(); self.advance() 
            name = self.current_tok.value
            self.advance(); self.advance() 
            action = self.current_tok.value
            self.advance(); self.advance() 
            expr = []
            while self.current_tok and self.current_tok.type != TT_RPAREN:
                expr.append(self.current_tok); self.advance()
                if self.current_tok and self.current_tok.type == TT_COMMA: self.advance()
            if self.current_tok: self.advance()
            if action == 'Set': return SetNode(lib, name, expr)
            if action == 'Execute': return FuncExecuteNode(name, expr)

        if self.current_tok.value == 'log':
            self.advance(); self.advance(); self.advance(); self.advance() 
            if self.current_tok.type == TT_IDENTIFIER and self.current_tok.value in ('vars', 'arrs'):
                lib = self.current_tok.value
                self.advance(); self.advance()
                name = self.current_tok.value
                self.advance()
                idx = None
                if self.current_tok and self.current_tok.type == TT_LBRACKET:
                    self.advance(); idx = self.current_tok.value; self.advance(); self.advance()
                if self.current_tok: self.advance()
                return LogNode(lib, name, index=idx)
            else:
                val = self.current_tok.value; self.advance(); self.advance()
                return LogNode(None, None, raw_val=val)

        # --- v0.3.0: delay statement ---
        if self.current_tok.value == 'delay':
            self.advance()  # consume 'delay'
            # Expect '('
            if self.current_tok.type == TT_LPAREN:
                self.advance()
            # Collect tokens until matching ')'
            expr_tokens = []
            paren_level = 1
            while self.current_tok:
                if self.current_tok.type == TT_LPAREN:
                    paren_level += 1
                elif self.current_tok.type == TT_RPAREN:
                    paren_level -= 1
                    if paren_level == 0:
                        self.advance()  # consume final ')'
                        break
                if paren_level >= 1:
                    expr_tokens.append(self.current_tok)
                self.advance()
            return DelayNode(expr_tokens)

        return None

    def parse(self):
        ast = []
        while self.current_tok is not None:
            stmt = self.statement()
            if stmt: ast.append(stmt)
            else: self.advance()
        return ast

# --- INTERPRETER ---
class Interpreter:
    def __init__(self):
        self.vars, self.arrs, self.funcs, self.temp_vars = {}, {}, {}, {}

    # --- Helper methods for function argument extraction (v0.3.0) ---
    def extract_until_matching_paren(self, tokens, start_idx=0):
        """Extract tokens from start_idx up to matching closing parenthesis."""
        extracted = []
        paren_level = 1
        i = start_idx
        while i < len(tokens):
            if tokens[i].type == TT_LPAREN:
                paren_level += 1
            elif tokens[i].type == TT_RPAREN:
                paren_level -= 1
                if paren_level == 0:
                    i += 1
                    break
            if paren_level >= 1:
                extracted.append(tokens[i])
            i += 1
        return extracted, i

    def parse_function_arguments(self, tokens):
        """Parse comma‑separated arguments from a token list starting after '('."""
        args = []
        current_arg = []
        paren_level = 0
        i = 0
        while i < len(tokens):
            tok = tokens[i]
            if tok.type == TT_LPAREN:
                paren_level += 1
                current_arg.append(tok)
            elif tok.type == TT_RPAREN:
                paren_level -= 1
                if paren_level < 0:  # end of arguments
                    if current_arg:
                        args.append(current_arg)
                    break
                current_arg.append(tok)
            elif tok.type == TT_COMMA and paren_level == 0:
                args.append(current_arg)
                current_arg = []
            else:
                current_arg.append(tok)
            i += 1
        if current_arg:
            args.append(current_arg)
        return args

    def resolve_value(self, tokens):
        if not isinstance(tokens, list):
            if tokens.type in (TT_INT, TT_STRING, TT_BOOL): return tokens.value
            if tokens.value == 'input':
                val = input(">> "); return int(val) if val.isdigit() else val
            if tokens.value in self.temp_vars: return self.temp_vars[tokens.value]
            return tokens.value

        # Original library lookup (vars.xxx / arrs.xxx)
        if len(tokens) >= 3 and tokens[1].type == TT_DOT:
            lib, var_name = tokens[0].value, tokens[2].value
            if lib == 'vars': return self.temp_vars.get(var_name, self.vars.get(var_name, 0))
            if lib == 'arrs': return self.arrs.get(var_name, [])

        # --- v0.3.0: math.sqrt, math.abs, math.rand ---
        if (len(tokens) >= 4 and tokens[0].value == 'math' and tokens[1].type == TT_DOT and
            tokens[2].value in ('sqrt','abs','rand') and tokens[3].type == TT_LPAREN):
            func = tokens[2].value
            # Extract argument tokens inside parentheses
            arg_tokens, _ = self.extract_until_matching_paren(tokens, start_idx=4)
            arg_val = self.evaluate_expression(arg_tokens) if arg_tokens else None
            if func == 'sqrt':
                return math.sqrt(arg_val)
            elif func == 'abs':
                return abs(arg_val)
            elif func == 'rand':
                if arg_val is None:
                    return random.random()
                else:
                    # If an argument is given, treat as upper bound (float)
                    return random.random() * arg_val

        # --- v0.3.0: combine function ---
        if len(tokens) >= 2 and tokens[0].value == 'combine' and tokens[1].type == TT_LPAREN:
            # Parse arguments inside parentheses
            inner_tokens = tokens[1:]  # from '(' onward
            args_token_lists = self.parse_function_arguments(inner_tokens)
            if len(args_token_lists) == 2:
                left_val = self.evaluate_expression(args_token_lists[0])
                right_val = self.evaluate_expression(args_token_lists[1])
                # Combine lists or strings; fallback to string concatenation
                if isinstance(left_val, list) and isinstance(right_val, list):
                    return left_val + right_val
                if isinstance(left_val, str) and isinstance(right_val, str):
                    return left_val + right_val
                return str(left_val) + str(right_val)
            return None  # error case, fall through

        # --- v0.3.0: delay as expression (optional) ---
        if len(tokens) >= 2 and tokens[0].value == 'delay' and tokens[1].type == TT_LPAREN:
            arg_tokens, _ = self.extract_until_matching_paren(tokens, start_idx=2)
            arg_val = self.evaluate_expression(arg_tokens)
            time.sleep(float(arg_val))
            return arg_val

        # Fallback: treat the first token as the value (original behaviour)
        return self.resolve_value(tokens[0])

    def peek_token_type(self, idx):
        # Helper for evaluate_complex_logic
        return None # Simplified for this snippet

    def evaluate_expression(self, expr_tokens):
        for i, tok in enumerate(expr_tokens):
            if tok.type in (TT_EE, TT_LTE, TT_GTE, TT_LT, TT_GT, TT_PLUS, TT_MINUS):
                v1 = self.resolve_value(expr_tokens[:i])
                v2 = self.resolve_value(expr_tokens[i+1:])
                if tok.type == TT_EE: return v1 == v2
                if tok.type == TT_LTE: return v1 <= v2
                if tok.type == TT_GTE: return v1 >= v2
                if tok.type == TT_LT: return v1 < v2
                if tok.type == TT_GT: return v1 > v2
                if tok.type == TT_PLUS: return v1 + v2
                if tok.type == TT_MINUS: return v1 - v2
        return self.resolve_value(expr_tokens)

    def evaluate_complex_logic(self, tokens):
        # Handle "not;"
        negate = False
        if tokens[0].value == 'not' and tokens[1].type == TT_SEMICOLON:
            negate = True
            tokens = tokens[2:]

        # Split by "alt;" or "also;"
        # For v0.2.2 we'll do a simple check for these keywords
        for i, tok in enumerate(tokens):
            if tok.value == 'alt' and self.peek_token_type(i+1) == TT_SEMICOLON:
                left = self.evaluate_expression(tokens[:i])
                right = self.evaluate_complex_logic(tokens[i+2:])
                res = left or right
                return not res if negate else res
            if tok.value == 'also' and self.peek_token_type(i+1) == TT_SEMICOLON:
                left = self.evaluate_expression(tokens[:i])
                right = self.evaluate_complex_logic(tokens[i+2:])
                res = left and right
                return not res if negate else res

        res = self.evaluate_expression(tokens)
        return not res if negate else res

    def visit(self, node):
        if isinstance(node, CondNode):
            if node.mode == 'if':
                if self.evaluate_complex_logic(node.condition_tokens):
                    for s in node.body: self.visit(s)
            elif node.mode == 'during':
                while self.evaluate_complex_logic(node.condition_tokens):
                    for s in node.body: self.visit(s)
        elif isinstance(node, DefineNode):
            if node.dtype == 'Var': self.vars[node.name] = None
            elif node.dtype == 'Arr': self.arrs[node.name] = []
            elif node.dtype == 'Func': self.funcs[node.name] = (node.args, node.body)
        elif isinstance(node, SetNode):
            val = self.evaluate_expression(node.expression)
            if node.library == 'vars': self.vars[node.name] = val
            else: self.arrs[node.name] = val
        elif isinstance(node, LogNode):
            if node.raw_val is not None: print(f"[SILICON CONSOLE]: {node.raw_val}")
            else: print(f"[SILICON CONSOLE]: {self.vars.get(node.name, self.arrs.get(node.name))}")
        elif isinstance(node, FuncExecuteNode):
            args_n, body = self.funcs[node.name]
            for s in body: self.visit(s)
        # --- v0.3.0: Delay statement ---
        elif isinstance(node, DelayNode):
            seconds = self.evaluate_expression(node.seconds_expr)
            time.sleep(float(seconds))

def run_file(filename):
    with open(filename, 'r') as f: script = f.read()
    lexer = Lexer(script)
    tokens, _ = lexer.make_tokens()
    parser = Parser(tokens)
    ast = parser.parse()
    interpreter = Interpreter()
    for node in ast: interpreter.visit(node)

if __name__ == "__main__":
    if len(sys.argv) > 1: run_file(sys.argv[1])

def main_cli():
    # Updated to v0.3.0
    VERSION = "0.3.0"
    
    if len(sys.argv) > 1:
        arg = sys.argv[1]
        
        # Check for Flags/Commands
        if arg in ["--version", "-v"]:
            print(f"Silicon v{VERSION}")
            return
            
        if arg in ["--help", "-h"]:
            print("Silicon Programming Language")
            print("Usage:")
            print("  sili <filename>.sil    Run a script")
            print("  sili --version         Show version info")
            print("  sili --help            Show this menu")
            return

        # If it's not a flag, treat it as a file
        filename = arg
        if filename.endswith('.sil'):
            try:
                run_file(filename) 
            except FileNotFoundError:
                print(f"Silicon Error: File '{filename}' not found.")
            except Exception as e:
                print(f"Silicon Runtime Error: {e}")
        else:
            print(f"Silicon Error: '{filename}' is not a valid .sil file.")
    else:
        # No arguments provided
        print(f"Silicon v{VERSION}")
        print("Usage: sili <filename>.sil")

if __name__ == "__main__":
    main_cli()