"""
CHILL to C Code Generator
Generates C code from CHILL AST

Uses pthreads for process/concurrency support.
"""

from typing import List, Dict, Optional, Set
from io import StringIO

from .ast_nodes import *
from .semantic import Type, TypeKind, SemanticAnalyzer


class CodeGenerator:
    """
    Generates C code from CHILL AST

    Maps CHILL constructs to C equivalents:
    - MODULE -> separate .c/.h files
    - PROC -> C function
    - PROCESS -> pthread wrapper
    - SIGNAL -> message queue or condition variable
    - BUFFER -> thread-safe queue
    - EVENT -> pthread_cond_t
    """

    def __init__(self):
        self.output = StringIO()
        self.indent_level = 0
        self.current_module = ""
        self.processes: List[str] = []
        self.signals: Dict[str, SignalNode] = {}
        self.buffers: Set[str] = set()

    def generate(self, program: Program) -> str:
        """Generate C code for a complete program"""
        self._emit_header()

        # Forward declarations
        for module in program.modules:
            self._emit_forward_decls(module)

        # Type definitions
        for module in program.modules:
            self._emit_types(module)

        # Global variables
        for module in program.modules:
            self._emit_globals(module)

        # Function/process implementations
        for module in program.modules:
            self._emit_module(module)

        # Main function
        self._emit_main()

        return self.output.getvalue()

    def _emit(self, text: str):
        """Emit text with current indentation"""
        indent = "    " * self.indent_level
        for line in text.split('\n'):
            if line.strip():
                self.output.write(indent + line + '\n')
            else:
                self.output.write('\n')

    def _emit_raw(self, text: str):
        """Emit text without indentation"""
        self.output.write(text)

    def _emit_header(self):
        """Emit C file header with includes"""
        self._emit_raw('''/*
 * Generated by CHILL Compiler
 * Source: ITU-T Z.200 (1999) CHILL to C transpiler
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>
#include <time.h>

/* CHILL runtime types */
typedef int32_t chill_int;
typedef bool chill_bool;
typedef char chill_char;
typedef struct timespec chill_time;
typedef int64_t chill_duration;

/* String operations */
static inline int chill_strlen(const char *s) { return (int)strlen(s); }

/* Time operations */
static inline chill_time chill_abstime(void) {
    chill_time t;
    clock_gettime(CLOCK_REALTIME, &t);
    return t;
}

/* Event type */
typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int signaled;
} chill_event;

static inline void chill_event_init(chill_event *e) {
    pthread_mutex_init(&e->mutex, NULL);
    pthread_cond_init(&e->cond, NULL);
    e->signaled = 0;
}

static inline void chill_event_signal(chill_event *e) {
    pthread_mutex_lock(&e->mutex);
    e->signaled = 1;
    pthread_cond_signal(&e->cond);
    pthread_mutex_unlock(&e->mutex);
}

static inline void chill_event_wait(chill_event *e) {
    pthread_mutex_lock(&e->mutex);
    while (!e->signaled) {
        pthread_cond_wait(&e->cond, &e->mutex);
    }
    e->signaled = 0;
    pthread_mutex_unlock(&e->mutex);
}

/* Buffer (bounded queue) for IPC */
typedef struct chill_buffer {
    void **data;
    int capacity;
    int head, tail, count;
    pthread_mutex_t mutex;
    pthread_cond_t not_empty;
    pthread_cond_t not_full;
} chill_buffer;

static inline void chill_buffer_init(chill_buffer *b, int capacity) {
    b->data = malloc(capacity * sizeof(void*));
    b->capacity = capacity;
    b->head = b->tail = b->count = 0;
    pthread_mutex_init(&b->mutex, NULL);
    pthread_cond_init(&b->not_empty, NULL);
    pthread_cond_init(&b->not_full, NULL);
}

static inline void chill_buffer_send(chill_buffer *b, void *item) {
    pthread_mutex_lock(&b->mutex);
    while (b->count == b->capacity) {
        pthread_cond_wait(&b->not_full, &b->mutex);
    }
    b->data[b->tail] = item;
    b->tail = (b->tail + 1) % b->capacity;
    b->count++;
    pthread_cond_signal(&b->not_empty);
    pthread_mutex_unlock(&b->mutex);
}

static inline void *chill_buffer_receive(chill_buffer *b) {
    pthread_mutex_lock(&b->mutex);
    while (b->count == 0) {
        pthread_cond_wait(&b->not_empty, &b->mutex);
    }
    void *item = b->data[b->head];
    b->head = (b->head + 1) % b->capacity;
    b->count--;
    pthread_cond_signal(&b->not_full);
    pthread_mutex_unlock(&b->mutex);
    return item;
}

/* Process tracking */
static pthread_t *process_threads = NULL;
static int process_count = 0;
static int process_capacity = 0;

static void register_process(pthread_t thread) {
    if (process_count >= process_capacity) {
        process_capacity = process_capacity ? process_capacity * 2 : 8;
        process_threads = realloc(process_threads, process_capacity * sizeof(pthread_t));
    }
    process_threads[process_count++] = thread;
}

static void wait_all_processes(void) {
    for (int i = 0; i < process_count; i++) {
        pthread_join(process_threads[i], NULL);
    }
    free(process_threads);
}

/* Delay implementation */
static inline void chill_delay(chill_duration ms) {
    struct timespec ts;
    ts.tv_sec = ms / 1000;
    ts.tv_nsec = (ms % 1000) * 1000000;
    nanosleep(&ts, NULL);
}

''')

    def _emit_forward_decls(self, module: ModuleDef):
        """Emit forward declarations"""
        self._emit(f"\n/* Forward declarations for {module.name} */")

        # Procedures
        for proc in module.procs:
            ret = self._c_type(proc.returns) if proc.returns else "void"
            params = self._format_params(proc.parameters)
            self._emit(f"{ret} {module.name}_{proc.name}({params});")

        # Processes
        for process in module.processes:
            self._emit(f"void *{module.name}_{process.name}_thread(void *arg);")

    def _emit_types(self, module: ModuleDef):
        """Emit type definitions"""
        self._emit(f"\n/* Types for {module.name} */")

        for decl in module.declarations:
            if isinstance(decl, NewmodeNode):
                self._emit_type_def(decl, module.name)
            elif isinstance(decl, SynmodeNode):
                self._emit_type_def(decl, module.name)

    def _emit_type_def(self, decl, prefix: str):
        """Emit a type definition"""
        if isinstance(decl.mode, SetMode):
            # Enumeration
            elements = ", ".join(f"{prefix}_{e}" for e in decl.mode.elements)
            self._emit(f"typedef enum {{ {elements} }} {prefix}_{decl.name};")

        elif isinstance(decl.mode, StructMode):
            # Structure
            self._emit(f"typedef struct {{")
            self.indent_level += 1
            for field in decl.mode.fields:
                c_type = self._c_type(field.mode)
                for name in field.names:
                    self._emit(f"{c_type} {name};")
            self.indent_level -= 1
            self._emit(f"}} {prefix}_{decl.name};")

        elif isinstance(decl.mode, RangeMode):
            # Integer subrange -> typedef to int
            self._emit(f"typedef chill_int {prefix}_{decl.name};")

        elif isinstance(decl.mode, CharsMode):
            # Fixed string
            length = self._eval_expr(decl.mode.length)
            self._emit(f"typedef char {prefix}_{decl.name}[{length}+1];")

        elif isinstance(decl.mode, ArrayMode):
            # Array type
            elem_type = self._c_type(decl.mode.element_mode)
            # Get array bounds
            if isinstance(decl.mode.index_mode, RangeMode):
                lower = self._eval_expr(decl.mode.index_mode.lower)
                upper = self._eval_expr(decl.mode.index_mode.upper)
                size = f"{upper} - {lower} + 1" if lower != "0" else upper
            else:
                size = "100"  # Default size
            self._emit(f"typedef {elem_type} {prefix}_{decl.name}[{size}];")

        else:
            # Generic typedef
            c_type = self._c_type(decl.mode)
            self._emit(f"typedef {c_type} {prefix}_{decl.name};")

    def _emit_globals(self, module: ModuleDef):
        """Emit global variable declarations"""
        self._emit(f"\n/* Globals for {module.name} */")

        for decl in module.declarations:
            if isinstance(decl, DclNode):
                c_type = self._c_type(decl.mode)
                for name in decl.names:
                    init = ""
                    if decl.init:
                        init = f" = {self._gen_expr(decl.init)}"
                    self._emit(f"{c_type} {module.name}_{name}{init};")

            elif isinstance(decl, SignalNode):
                # Signals implemented as message queues
                self.signals[decl.name] = decl
                self._emit(f"chill_buffer {module.name}_{decl.name}_queue;")

    def _emit_module(self, module: ModuleDef):
        """Emit module implementation"""
        self.current_module = module.name

        self._emit(f"\n/* Module {module.name} */")

        # Procedures
        for proc in module.procs:
            self._emit_proc(proc, module.name)

        # Processes
        for process in module.processes:
            self._emit_process(process, module.name)
            self.processes.append(f"{module.name}_{process.name}")

    def _emit_proc(self, proc: ProcDef, prefix: str):
        """Emit a procedure"""
        ret = self._c_type(proc.returns) if proc.returns else "void"
        params = self._format_params(proc.parameters)

        self._emit(f"\n{ret} {prefix}_{proc.name}({params}) {{")
        self.indent_level += 1

        # Local declarations
        for decl in proc.declarations:
            if isinstance(decl, DclNode):
                c_type = self._c_type(decl.mode)
                for name in decl.names:
                    init = ""
                    if decl.init:
                        init = f" = {self._gen_expr(decl.init)}"
                    self._emit(f"{c_type} {name}{init};")

        # Statements
        for stmt in proc.statements:
            self._emit_stmt(stmt)

        self.indent_level -= 1
        self._emit("}")

    def _emit_process(self, process: ProcessDef, prefix: str):
        """Emit a process as pthread wrapper"""
        # Parameter structure for thread
        if process.parameters:
            self._emit(f"\ntypedef struct {{")
            self.indent_level += 1
            for param in process.parameters:
                c_type = self._c_type(param.mode)
                self._emit(f"{c_type} {param.name};")
            self.indent_level -= 1
            self._emit(f"}} {prefix}_{process.name}_args;")

        # Thread function
        self._emit(f"\nvoid *{prefix}_{process.name}_thread(void *arg) {{")
        self.indent_level += 1

        # Unpack parameters
        if process.parameters:
            self._emit(f"{prefix}_{process.name}_args *_args = ({prefix}_{process.name}_args *)arg;")
            for param in process.parameters:
                c_type = self._c_type(param.mode)
                self._emit(f"{c_type} {param.name} = _args->{param.name};")
            self._emit("free(_args);")

        # Local declarations
        for decl in process.declarations:
            if isinstance(decl, DclNode):
                c_type = self._c_type(decl.mode)
                for name in decl.names:
                    init = ""
                    if decl.init:
                        init = f" = {self._gen_expr(decl.init)}"
                    self._emit(f"{c_type} {name}{init};")

        # Statements
        for stmt in process.statements:
            self._emit_stmt(stmt)

        self._emit("return NULL;")
        self.indent_level -= 1
        self._emit("}")

        # Start function
        self._emit(f"\nvoid {prefix}_{process.name}_start({self._format_params(process.parameters)}) {{")
        self.indent_level += 1

        if process.parameters:
            self._emit(f"{prefix}_{process.name}_args *_args = malloc(sizeof({prefix}_{process.name}_args));")
            for param in process.parameters:
                self._emit(f"_args->{param.name} = {param.name};")

        self._emit("pthread_t thread;")
        arg = "_args" if process.parameters else "NULL"
        self._emit(f"pthread_create(&thread, NULL, {prefix}_{process.name}_thread, {arg});")
        self._emit("register_process(thread);")

        self.indent_level -= 1
        self._emit("}")

    def _emit_stmt(self, stmt: Statement):
        """Emit a statement"""
        # Label
        if stmt.label:
            self._emit(f"{stmt.label}:;")

        if isinstance(stmt, AssignStmt):
            for target in stmt.targets:
                target_c = self._gen_expr(target)
                value_c = self._gen_expr(stmt.value)
                # Handle string assignment
                if self._is_string_type(target) and not target_c.startswith('*'):
                    self._emit(f"strcpy({target_c}, {value_c});")
                else:
                    self._emit(f"{target_c} = {value_c};")

        elif isinstance(stmt, IfStmt):
            cond = self._gen_expr(stmt.condition)
            self._emit(f"if ({cond}) {{")
            self.indent_level += 1
            for s in stmt.then_stmts:
                self._emit_stmt(s)
            self.indent_level -= 1

            for elsif in stmt.elsif_parts:
                cond = self._gen_expr(elsif.condition)
                self._emit(f"}} else if ({cond}) {{")
                self.indent_level += 1
                for s in elsif.statements:
                    self._emit_stmt(s)
                self.indent_level -= 1

            if stmt.else_stmts:
                self._emit("} else {")
                self.indent_level += 1
                for s in stmt.else_stmts:
                    self._emit_stmt(s)
                self.indent_level -= 1

            self._emit("}")

        elif isinstance(stmt, CaseStmt):
            selector = self._gen_expr(stmt.selector)
            self._emit(f"switch ({selector}) {{")
            for alt in stmt.alternatives:
                for val in alt.values:
                    val_c = self._gen_expr(val)
                    self._emit(f"case {val_c}:")
                self.indent_level += 1
                for s in alt.statements:
                    self._emit_stmt(s)
                self._emit("break;")
                self.indent_level -= 1

            if stmt.else_stmts:
                self._emit("default:")
                self.indent_level += 1
                for s in stmt.else_stmts:
                    self._emit_stmt(s)
                self.indent_level -= 1

            self._emit("}")

        elif isinstance(stmt, DoWhileStmt):
            cond = self._gen_expr(stmt.condition)
            self._emit(f"while ({cond}) {{")
            self.indent_level += 1
            for s in stmt.statements:
                self._emit_stmt(s)
            self.indent_level -= 1
            self._emit("}")

        elif isinstance(stmt, DoForStmt):
            start = self._gen_expr(stmt.start)
            end = self._gen_expr(stmt.end)
            step = self._gen_expr(stmt.step) if stmt.step else "1"
            op = ">=" if stmt.down else "<="
            inc = "-=" if stmt.down else "+="

            self._emit(f"for (int {stmt.var} = {start}; {stmt.var} {op} {end}; {stmt.var} {inc} {step}) {{")
            self.indent_level += 1
            for s in stmt.statements:
                self._emit_stmt(s)
            self.indent_level -= 1
            self._emit("}")

        elif isinstance(stmt, DoEverStmt):
            self._emit("while (1) {")
            self.indent_level += 1
            for s in stmt.statements:
                self._emit_stmt(s)
            self.indent_level -= 1
            self._emit("}")

        elif isinstance(stmt, ExitStmt):
            if stmt.target:
                self._emit(f"goto {stmt.target}_exit;")
            else:
                self._emit("break;")

        elif isinstance(stmt, ReturnStmt):
            if stmt.value:
                val = self._gen_expr(stmt.value)
                self._emit(f"return {val};")
            else:
                self._emit("return;")

        elif isinstance(stmt, GotoStmt):
            self._emit(f"goto {stmt.target};")

        elif isinstance(stmt, CallStmt):
            call = self._gen_expr(ProcCall(proc=stmt.proc, arguments=stmt.arguments))
            self._emit(f"{call};")

        elif isinstance(stmt, StartStmt):
            args = ", ".join(self._gen_expr(arg) for arg in stmt.arguments)
            self._emit(f"{self.current_module}_{stmt.process}_start({args});")

        elif isinstance(stmt, StopStmt):
            self._emit("pthread_exit(NULL);")

        elif isinstance(stmt, SendStmt):
            # Send to signal queue
            if stmt.arguments:
                # Pack arguments into struct
                self._emit("{")
                self.indent_level += 1
                self._emit("/* SEND signal - would pack arguments */")
                self._emit(f"/* chill_buffer_send(&{self.current_module}_{stmt.signal}_queue, ...); */")
                self.indent_level -= 1
                self._emit("}")
            else:
                self._emit(f"chill_buffer_send(&{self.current_module}_{stmt.signal}_queue, NULL);")

        elif isinstance(stmt, ReceiveCaseStmt):
            # Simplified - just handle first alternative
            self._emit("/* RECEIVE CASE - simplified */")
            self._emit("{")
            self.indent_level += 1
            for alt in stmt.alternatives:
                self._emit(f"/* Receive {alt.signal} */")
                for s in alt.statements:
                    self._emit_stmt(s)
                break  # Only handle first for now
            self.indent_level -= 1
            self._emit("}")

        elif isinstance(stmt, DelayStmt):
            if stmt.duration:
                dur = self._gen_expr(stmt.duration)
                self._emit(f"chill_delay({dur});")

        elif isinstance(stmt, ContinueStmt):
            evt = self._gen_expr(stmt.event)
            self._emit(f"chill_event_signal(&{evt});")

        elif isinstance(stmt, AssertStmt):
            cond = self._gen_expr(stmt.condition)
            self._emit(f'if (!({cond})) {{ fprintf(stderr, "Assertion failed\\n"); abort(); }}')

        elif isinstance(stmt, BeginEndBlock):
            self._emit("{")
            self.indent_level += 1
            for d in stmt.declarations:
                if isinstance(d, DclNode):
                    c_type = self._c_type(d.mode)
                    for name in d.names:
                        init = ""
                        if d.init:
                            init = f" = {self._gen_expr(d.init)}"
                        self._emit(f"{c_type} {name}{init};")
            for s in stmt.statements:
                self._emit_stmt(s)
            self.indent_level -= 1
            self._emit("}")

        elif isinstance(stmt, EmptyStmt):
            pass

    def _gen_expr(self, expr: Expression) -> str:
        """Generate C expression"""
        if expr is None:
            return "0"

        if isinstance(expr, Literal):
            if expr.kind == 'int':
                return str(expr.value)
            elif expr.kind == 'bool':
                return "true" if expr.value else "false"
            elif expr.kind == 'char':
                return f"'{expr.value}'"
            elif expr.kind == 'string':
                return f'"{expr.value}"'
            elif expr.kind == 'null':
                return "NULL"
            elif expr.kind == 'float':
                return str(expr.value)
            else:
                return "0"

        elif isinstance(expr, Identifier):
            # Check if it's a module-qualified name
            name = expr.name
            # Simple heuristic: if uppercase, might be enum value
            if name.upper() == name:
                return f"{self.current_module}_{name}"
            return name

        elif isinstance(expr, BinaryOp):
            left = self._gen_expr(expr.left)
            right = self._gen_expr(expr.right)

            op_map = {
                '+': '+', '-': '-', '*': '*', '/': '/',
                '=': '==', '/=': '!=',
                '<': '<', '>': '>', '<=': '<=', '>=': '>=',
                'AND': '&&', 'OR': '||', 'XOR': '^',
                'MOD': '%', 'REM': '%',
                '**': '/* pow */',
            }

            if expr.op == '//':
                # String concatenation - need helper
                return f'/* concat({left}, {right}) */'
            elif expr.op in op_map:
                c_op = op_map[expr.op]
                return f"({left} {c_op} {right})"
            else:
                return f"({left} /* {expr.op} */ {right})"

        elif isinstance(expr, UnaryOp):
            operand = self._gen_expr(expr.operand)
            if expr.op == 'NOT':
                return f"(!{operand})"
            elif expr.op == '-':
                return f"(-{operand})"
            elif expr.op == '+':
                return operand
            return operand

        elif isinstance(expr, ArrayAccess):
            array = self._gen_expr(expr.array)
            index = self._gen_expr(expr.index)
            return f"{array}[{index}]"

        elif isinstance(expr, FieldAccess):
            struct = self._gen_expr(expr.struct)
            return f"{struct}.{expr.field}"

        elif isinstance(expr, DerefAccess):
            ref = self._gen_expr(expr.ref)
            return f"(*{ref})"

        elif isinstance(expr, ProcCall):
            proc = self._gen_expr(expr.proc)
            args = ", ".join(self._gen_expr(arg) for arg in expr.arguments)
            # Prefix with module name if it's a simple identifier
            if isinstance(expr.proc, Identifier):
                proc = f"{self.current_module}_{expr.proc.name}"
            return f"{proc}({args})"

        elif isinstance(expr, BuiltinCall):
            args = ", ".join(self._gen_expr(arg) for arg in expr.arguments)
            builtin_map = {
                'ABS': 'abs',
                'LENGTH': 'chill_strlen',
                'SIZE': 'sizeof',
                'CHAR': '(char)',
            }
            func = builtin_map.get(expr.name.upper(), expr.name.lower())
            return f"{func}({args})"

        elif isinstance(expr, IfExpr):
            cond = self._gen_expr(expr.condition)
            then_e = self._gen_expr(expr.then_expr)
            else_e = self._gen_expr(expr.else_expr)
            return f"(({cond}) ? ({then_e}) : ({else_e}))"

        return "/* unknown expr */"

    def _c_type(self, mode: ModeNode) -> str:
        """Convert CHILL mode to C type"""
        if mode is None:
            return "void"

        if isinstance(mode, IntMode):
            return "chill_int"
        elif isinstance(mode, BoolMode):
            return "chill_bool"
        elif isinstance(mode, CharMode):
            return "chill_char"
        elif isinstance(mode, CharsMode):
            length = self._eval_expr(mode.length)
            return f"char[{length}+1]"
        elif isinstance(mode, BoolsMode):
            length = self._eval_expr(mode.length)
            return f"uint8_t[({length}+7)/8]"
        elif isinstance(mode, SetMode):
            return "int"  # Enum values are ints
        elif isinstance(mode, RangeMode):
            return "chill_int"
        elif isinstance(mode, RefMode):
            target = self._c_type(mode.target_mode)
            return f"{target} *"
        elif isinstance(mode, ArrayMode):
            elem = self._c_type(mode.element_mode)
            if isinstance(mode.index_mode, RangeMode):
                lower = self._eval_expr(mode.index_mode.lower)
                upper = self._eval_expr(mode.index_mode.upper)
                size = f"{upper}-{lower}+1"
            else:
                size = "100"
            return f"{elem}[{size}]"
        elif isinstance(mode, ProcMode):
            ret = self._c_type(mode.returns) if mode.returns else "void"
            return f"{ret} (*)()"
        elif isinstance(mode, BufferMode):
            return "chill_buffer"
        elif isinstance(mode, EventMode):
            return "chill_event"
        elif isinstance(mode, DurationMode):
            return "chill_duration"
        elif isinstance(mode, TimeMode):
            return "chill_time"
        elif isinstance(mode, NamedMode):
            return f"{self.current_module}_{mode.name}"
        elif isinstance(mode, StructMode):
            return "struct { /* anonymous */ }"

        return "void *"

    def _format_params(self, params: List[Parameter]) -> str:
        """Format parameter list for C function"""
        if not params:
            return "void"
        parts = []
        for p in params:
            c_type = self._c_type(p.mode)
            parts.append(f"{c_type} {p.name}")
        return ", ".join(parts)

    def _eval_expr(self, expr: Expression) -> str:
        """Evaluate/format an expression as a constant"""
        if isinstance(expr, Literal):
            return str(expr.value)
        elif isinstance(expr, BinaryOp):
            left = self._eval_expr(expr.left)
            right = self._eval_expr(expr.right)
            return f"({left} {expr.op} {right})"
        return "0"

    def _is_string_type(self, expr: Expression) -> bool:
        """Check if expression is a string type (needs strcpy)"""
        # Simplified - check if it looks like a string access
        return False

    def _emit_main(self):
        """Emit main function"""
        self._emit("""
/* Main entry point */
int main(int argc, char *argv[]) {
    /* Initialize signal queues */
""")
        self.indent_level = 1

        for sig_name, sig in self.signals.items():
            self._emit(f"chill_buffer_init(&{self.current_module}_{sig_name}_queue, 16);")

        self._emit("")
        self._emit("/* Start initial processes here if needed */")
        self._emit("")
        self._emit("/* Wait for all processes to complete */")
        self._emit("wait_all_processes();")
        self._emit("")
        self._emit("return 0;")

        self.indent_level = 0
        self._emit("}")


def generate(program: Program) -> str:
    """Convenience function to generate C code"""
    generator = CodeGenerator()
    return generator.generate(program)
