Continue where was left off. Forgot no details.
use anyhow::Result;
use crate::disassembler::DisassembledInstruction;
use crate::optimization::OptimizedCode;
use crate::advanced_function_detection::{Function, LocalVariable, Parameter};
use crate::control_flow_analysis::{BasicBlock, ControlFlowGraph, Edge, EdgeType};
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Write;
use petgraph::graph::{Graph, NodeIndex};
use petgraph::algo::dominators::{self, Dominators};
use petgraph::visit::EdgeRef;
use itertools::Itertools;
use z3::{ast::Bool, Config, Context, Solver};

pub fn generate_pseudocode(
    optimized: &OptimizedCode,
    functions: &[Function],
    control_flow: &HashMap<u64, ControlFlowGraph>
) -> Result<String> {
    let mut pseudocode = String::new();
    let strings = extract_strings(&optimized.instructions);
    let global_variables = detect_global_variables(optimized);
    let type_info = infer_types(optimized, functions, control_flow);

    // Generate global variable declarations
    writeln!(pseudocode, "// Global Variables")?;
    for (addr, var) in &global_variables {
        let var_type = type_info.get(var).map_or("auto", |t| t.as_str());
        writeln!(pseudocode, "{} {} = 0x{:x};", var_type, var, addr)?;
    }
    pseudocode.push('\n');

    // Generate function declarations
    writeln!(pseudocode, "// Function Declarations")?;
    for (index, function) in functions.iter().enumerate() {
        let function_name = generate_function_name(function, index);
        let return_type = type_info.get(&function_name).map_or("void", |t| t.as_str());
        let params = function.parameters.iter()
            .map(|p| format!("{} {}", type_info.get(&p.name).map_or("auto", |t| t.as_str()), p.name))
            .join(", ");
        writeln!(pseudocode, "{} {}({});", return_type, function_name, params)?;
    }
    pseudocode.push('\n');

    // Generate function definitions
    writeln!(pseudocode, "// Function Definitions")?;
    for (index, function) in functions.iter().enumerate() {
        let function_name = generate_function_name(function, index);
        let mut context = PseudocodeContext::new();
        
        if let Some(cfg) = control_flow.get(&function.start_address) {
            let optimized_cfg = optimize_cfg(cfg.clone());
            let blocks = generate_function_body(&optimized_cfg, optimized, &strings, &mut context, &global_variables, &type_info);
            
            // Generate function signature
            let return_type = type_info.get(&function_name).map_or("void", |t| t.as_str());
            let params = function.parameters.iter()
                .map(|p| format!("{} {}", type_info.get(&p.name).map_or("auto", |t| t.as_str()), p.name))
                .join(", ");
            writeln!(pseudocode, "{} {}({}) {{", return_type, function_name, params)?;
            
            // Local variable declarations
            if !context.local_variables.is_empty() {
                writeln!(pseudocode, "    // Local variables")?;
                for var in &context.local_variables {
                    let var_type = type_info.get(var).map_or("auto", |t| t.as_str());
                    writeln!(pseudocode, "    {} {};", var_type, var)?;
                }
                pseudocode.push('\n');
            }
            
            // Function body
            for block in blocks {
                pseudocode.push_str(&block);
            }
            
            writeln!(pseudocode, "}}\n")?;
        } else {
            writeln!(pseudocode, "// No control flow graph available for function at 0x{:x}\n", function.start_address)?;
        }
    }

    Ok(pseudocode)
}

struct PseudocodeContext {
    variables: HashMap<String, Variable>,
    local_variables: HashSet<String>,
    parameters: Vec<String>,
    current_condition: Option<String>,
    stack_offset: i32,
    label_counter: usize,
    indentation: usize,
    var_counter: usize,
    loop_stack: Vec<String>,
    switch_stack: Vec<String>,
}

#[derive(Clone, Debug)]
struct Variable {
    name: String,
    var_type: VarType,
    value: Option<String>,
    version: usize,
    is_constant: bool,
    is_array: bool,
    array_size: Option<usize>,
}

#[derive(Clone, Debug, PartialEq)]
enum VarType {
    Int8,
    Int16,
    Int32,
    Int64,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    Float,
    Double,
    Pointer,
    Bool,
    Char,
    Unknown,
}

impl PseudocodeContext {
    fn new() -> Self {
        PseudocodeContext {
            variables: HashMap::new(),
            local_variables: HashSet::new(),
            parameters: Vec::new(),
            current_condition: None,
            stack_offset: 0,
            label_counter: 0,
            indentation: 1,
            var_counter: 0,
            loop_stack: Vec::new(),
            switch_stack: Vec::new(),
        }
    }

    fn next_label(&mut self) -> String {
        self.label_counter += 1;
        format!("label_{}", self.label_counter)
    }

    fn indent(&self) -> String {
        "    ".repeat(self.indentation)
    }

    fn add_variable(&mut self, name: String, var_type: VarType, value: Option<String>, is_constant: bool, is_array: bool, array_size: Option<usize>) {
        let version = self.variables.values()
            .filter(|v| v.name == name)
            .map(|v| v.version)
            .max()
            .map_or(1, |v| v + 1);
        
        let versioned_name = format!("{}_{}", name, version);
        self.variables.insert(versioned_name.clone(), Variable { name, var_type, value, version, is_constant, is_array, array_size });
        self.local_variables.insert(versioned_name);
    }

    fn update_variable(&mut self, name: &str, value: String) {
        if let Some(var) = self.variables.values_mut().find(|v| v.name == name) {
            var.value = Some(value);
            var.version += 1;
        } else {
            self.add_variable(name.to_string(), VarType::Unknown, Some(value), false, false, None);
        }
    }

    fn get_variable(&self, name: &str) -> Option<&Variable> {
        self.variables.values().find(|v| v.name == name)
    }

    fn get_versioned_name(&self, name: &str) -> String {
        self.variables.values()
            .filter(|v| v.name == name)
            .max_by_key(|v| v.version)
            .map_or(name.to_string(), |v| format!("{}_{}", v.name, v.version))
    }

    fn generate_var_name(&mut self) -> String {
        self.var_counter += 1;
        format!("var_{}", self.var_counter)
    }

    fn push_loop(&mut self, label: String) {
        self.loop_stack.push(label);
    }

    fn pop_loop(&mut self) -> Option<String> {
        self.loop_stack.pop()
    }

    fn current_loop(&self) -> Option<&String> {
        self.loop_stack.last()
    }

    fn push_switch(&mut self, label: String) {
        self.switch_stack.push(label);
    }

    fn pop_switch(&mut self) -> Option<String> {
        self.switch_stack.pop()
    }

    fn current_switch(&self) -> Option<&String> {
        self.switch_stack.last()
    }
}

fn generate_function_body(
    cfg: &ControlFlowGraph,
    optimized: &OptimizedCode,
    strings: &HashMap<u64, String>,
    context: &mut PseudocodeContext,
    global_variables: &HashMap<u64, String>,
    type_info: &HashMap<String, String>
) -> Vec<String> {
    let mut blocks = Vec::new();
    let loops = detect_loops(cfg);
    let jump_tables = detect_jump_tables(optimized);
    let dominators = compute_dominators(cfg);
    
    if cfg.basic_blocks.is_empty() {
        blocks.push(format!("{}// Empty function or failed to analyze control flow\n", context.indent()));
        return blocks;
    }

    let mut visited = HashSet::new();
    let mut stack = vec![(0, false)];

    while let Some((block_index, is_loop_end)) = stack.pop() {
        if visited.contains(&block_index) {
            if is_loop_end {
                context.indentation -= 1;
                blocks.push(format!("{}}}\n", context.indent()));
            }
            continue;
        }
        visited.insert(block_index);

        let block = &cfg.basic_blocks[block_index];
        let mut block_code = String::new();

        if loops.contains(&block_index) {
            let loop_label = context.next_label();
            writeln!(block_code, "{}while (true) {{ // Loop {}", context.indent(), loop_label).unwrap();
            context.push_loop(loop_label);
            context.indentation += 1;
        }

        let label = context.next_label();
        writeln!(block_code, "{}// Block {} (0x{:x} - 0x{:x})", context.indent(), label, block.start_address, block.end_address).unwrap();

        let simplified_block = simplify_block(block, optimized, strings, context, global_variables, type_info);
        block_code.push_str(&simplified_block);

        blocks.push(block_code);

        let outgoing_edges: Vec<_> = cfg.edges.iter()
            .filter(|e| e.from == block_index)
            .collect();

        match outgoing_edges.len() {
            0 => {
                // No outgoing edges, likely a return or end of function
                blocks.push(format!("{}return;\n", context.indent()));
            },
            1 => {
                // Single outgoing edge, likely an unconditional jump or fallthrough
                let target = outgoing_edges[0].to;
                stack.push((target, loops.contains(&block_index)));
            },
            2 => {
                // Two outgoing edges, likely an if-else structure
                let condition = detect_condition(&cfg.basic_blocks[block_index], optimized);
                let true_branch = outgoing_edges.iter().find(|e| e.edge_type == EdgeType::Conditional).map(|e| e.to).unwrap_or(0);
                let false_branch = outgoing_edges.iter().find(|e| e.edge_type == EdgeType::Fallthrough).map(|e| e.to).unwrap_or(0);

                writeln!(blocks.last_mut().unwrap(), "{}if ({}) {{", context.indent(), condition).unwrap();
                context.indentation += 1;
                stack.push((false_branch, false));
                stack.push((true_branch, false));
                context.indentation -= 1;
                blocks.push(format!("{}}} else {{\n", context.indent()));
                context.indentation += 1;
            },
            _ => {
                // More than two outgoing edges, likely a switch statement
                let switch_var = detect_switch_variable(&cfg.basic_blocks[block_index], optimized);
                let switch_label = context.next_label();
                writeln!(blocks.last_mut().unwrap(), "{}switch ({}) {{ // {}", context.indent(), switch_var, switch_label).unwrap();
                context.push_switch(switch_label.clone());
                for edge in &outgoing_edges {
                    let case_value = detect_case_value(edge, optimized, &jump_tables);
                    writeln!(blocks.last_mut().unwrap(), "{}    case {}:", context.indent(), case_value).unwrap();
                    context.indentation += 1;
                    writeln!(blocks.last_mut().unwrap(), "{}goto block_{};", context.indent(), edge.to).unwrap();
                    context.indentation -= 1;
                }
                writeln!(blocks.last_mut().unwrap(), "{}}}", context.indent()).unwrap();
                context.pop_switch();

                for edge in outgoing_edges {
                    stack.push((edge.to, false));
                }
            }
        }

        if is_loop_end {
            context.indentation -= 1;
            if let Some(loop_label) = context.pop_loop() {
                blocks.push(format!("{}}} // End of loop {}\n", context.indent(), loop_label));
            } else {
                blocks.push(format!("{}}} // End of loop\n", context.indent()));
            }
        }
    }

    blocks
}

fn simplify_block(
    block: &BasicBlock,
    optimized: &OptimizedCode,
    strings: &HashMap<u64, String>,
    context: &mut PseudocodeContext,
    global_variables: &HashMap<u64, String>,
    type_info: &HashMap<String, String>
) -> String {
    let mut simplified = String::new();
    let mut instructions = block.instructions.iter()
        .filter_map(|&index| optimized.instructions.get(index))
        .collect::<Vec<_>>();

    // Detect and simplify patterns
    simplify_instruction_patterns(&mut instructions);

    for instruction in instructions {
        let mut line = simplify_instruction(instruction, strings, context, global_variables, type_info);
        
        // Enhance constant folding and propagation
        if let Some(folded) = constant_fold(&line) {
            line = folded;
        }

        if !line.is_empty() {
            writeln!(simplified, "{}{}", context.indent(), line).unwrap();
        }
    }

    simplified
}

fn simplify_instruction_patterns(instructions: &mut Vec<&DisassembledInstruction>) {
    // Pattern 1: Recognize increment/decrement
    for i in 0..instructions.len() - 1 {
        if instructions[i].mnemonic == "add" && instructions[i].op_str.ends_with(", 1") {
            if let Some(dest) = instructions[i].op_str.split(',').next() {
                instructions[i] = &DisassembledInstruction {
                    address: instructions[i].address,
                    mnemonic: "inc".to_string(),
                    op_str: dest.trim().to_string(),
                    size: instructions[i].size,
                };
                instructions.remove(i + 1);
            }
        } else if instructions[i].mnemonic == "sub" && instructions[i].op_str.ends_with(", 1") {
            if let Some(dest) = instructions[i].op_str.split(',').next() {
                instructions[i] = &DisassembledInstruction {
                    address: instructions[i].address,
                    mnemonic: "dec".to_string(),
                    op_str: dest.trim().to_string(),
                    size: instructions[i].size,
                };
                instructions.remove(i + 1);
            }
        }
    }

    // Pattern 2: Recognize simple loops
    for i in 0..instructions.len() - 3 {
        if instructions[i].mnemonic == "mov" &&
           instructions[i + 1].mnemonic == "cmp" &&
           instructions[i + 2].mnemonic == "jl" {
            // This might be the start of a for loop
            let loop_var = instructions[i].op_str.split(',').next().unwrap_or("").trim();
            let loop_end = instructions[i + 1].op_str.split(',').last().unwrap_or("").trim();
            instructions[i] = &DisassembledInstruction {
                address: instructions[i].address,
                mnemonic: "for_loop_start".to_string(),
                op_str: format!("{}, {}", loop_var, loop_end),
                size: instructions[i].size + instructions[i + 1].size + instructions[i + 2].size,
            };
            instructions.drain(i + 1..i + 3);
        }
    }

    // Pattern 3: Recognize function prologue/epilogue
    if instructions.len() >= 3 &&
       instructions[0].mnemonic == "push" && instructions[0].op_str == "ebp" &&
       instructions[1].mnemonic == "mov" && instructions[1].op_str == "ebp, esp" &&
       instructions[2].mnemonic == "sub" && instructions[2].op_str.starts_with("esp,") {
        instructions[0] = &DisassembledInstruction {
            address: instructions[0].address,
            mnemonic: "function_prologue".to_string(),
            op_str: instructions[2].op_str.split(',').last().unwrap_or("").trim().to_string(),
            size: instructions[0].size + instructions[1].size + instructions[2].size,
        };
        instructions.drain(1..3);
    }

    if instructions.len() >= 2 &&
       instructions[instructions.len() - 2].mnemonic == "pop" && instructions[instructions.len() - 2].op_str == "ebp" &&
       instructions[instructions.len() - 1].mnemonic == "ret" {
        let last_index = instructions.len() - 1;
        instructions[last_index - 1] = &DisassembledInstruction {
            address: instructions[last_index - 1].address,
            mnemonic: "function_epilogue".to_string(),
            op_str: String::new(),
            size: instructions[last_index - 1].size + instructions[last_index].size,
        };
        instructions.pop();
    }
}

fn constant_fold(line: &str) -> Option<String> {
    let mut parser = fasteval::Parser::new();
    let mut slab = fasteval::Slab::new();

    if let Ok(expr) = parser.parse(line, &mut slab) {
        if let Ok(result) = fasteval::eval_compiled_ref(&expr, &slab, &mut fasteval::EmptyNamespace) {
            return Some(result.to_string());
        }
    }

    None
}

fn simplify_instruction(
    instruction: &DisassembledInstruction,
    strings: &HashMap<u64, String>,
    context: &mut PseudocodeContext,
    global_variables: &HashMap<u64, String>,
    type_info: &HashMap<String, String>
) -> String {
    match instruction.mnemonic.as_str() {
        "mov" => simplify_mov(instruction, strings, context, global_variables, type_info),
        "add" | "sub" | "imul" | "idiv" => simplify_arithmetic(instruction, context, type_info),
        "and" | "or" | "xor" | "not" => simplify_bitwise(instruction, context, type_info),
        "shl" | "shr" => simplify_shift(instruction, context, type_info),
        "cmp" => simplify_cmp(instruction, context, type_info),
        "test" => simplify_test(instruction, context, type_info),
        "je" | "jne" | "jg" | "jge" | "jl" | "jle" | "ja" | "jae" | "jb" | "jbe" => simplify_conditional_jump(instruction, context),
        "jmp" => simplify_jmp(instruction, context),
        "call" => simplify_call(instruction, context, type_info),
        "ret" => simplify_return(instruction, context, type_info),
        "push" => simplify_push(instruction, context, type_info),
        "pop" => simplify_pop(instruction, context, type_info),
        "inc" => simplify_inc(instruction, context, type_info),
        "dec" => simplify_dec(instruction, context, type_info),
        "lea" => simplify_lea(instruction, context, global_variables, type_info),
        "for_loop_start" => simplify_for_loop_start(instruction, context, type_info),
        "function_prologue" => simplify_function_prologue(instruction, context),
        "function_epilogue" => simplify_function_epilogue(context),
        "nop" => String::new(),
        "int3" => "// Breakpoint".to_string(),
        _ => format!("// Unsimplified: {} {}", instruction.mnemonic, instruction.op_str),
    }
}

fn simplify_mov(
    instruction: &DisassembledInstruction,
    strings: &HashMap<u64, String>,
    context: &mut PseudocodeContext,
    global_variables: &HashMap<u64, String>,
    type_info: &HashMap<String, String>
) -> String {
    let parts: Vec<&str> = instruction.op_str.split(',').collect();
    if parts.len() == 2 {
        let dest = simplify_operand(parts[0].trim(), context, global_variables, type_info);
        let src = simplify_operand(parts[1].trim(), context, global_variables, type_info);
        if let Some(string_value) = strings.get(&instruction.address) {
            context.add_variable(dest.clone(), VarType::Pointer, Some(format!("\"{}\"", string_value)), true, false, None);
            let versioned_dest = context.get_versioned_name(&dest);
            format!("{} = \"{}\";", versioned_dest, string_value)
        } else {
            let var_type = infer_type(&src, type_info);
            context.add_variable(dest.clone(), var_type.clone(), Some(src.clone()), false, false, None);
            let versioned_dest = context.get_versioned_name(&dest);
            format!("{} = {};", versioned_dest, src)
        }
    } else {
        format!("// Unsimplified mov: {}", instruction.op_str)
    }
}

fn infer_type(value: &str, type_info: &HashMap<String, String>) -> VarType {
    if let Some(type_str) = type_info.get(value) {
        match type_str.as_str() {
            "int8_t" => VarType::Int8,
            "int16_t" => VarType::Int16,
            "int32_t" => VarType::Int32,
            "int64_t" => VarType::Int64,
            "uint8_t" => VarType::UInt8,
            "uint16_t" => VarType::UInt16,
            "uint32_t" => VarType::UInt32,
            "uint64_t" => VarType::UInt64,
            "float" => VarType::Float,
            "double" => VarType::Double,
            "bool" => VarType::Bool,
            "char" => VarType::Char,
            _ if type_str.ends_with('*') => VarType::Pointer,
            _ => VarType::Unknown,
        }
    } else if value.starts_with("0x") {
        VarType::UInt32 // Assume 32-bit for simplicity
    } else if value.parse::<i32>().is_ok() {
        VarType::Int32
    } else if value.parse::<f32>().is_ok() {
        VarType::Float
    } else if value.starts_with('&') {
        VarType::Pointer
    } else {
        VarType::Unknown
    }
}

fn simplify_arithmetic(instruction: &DisassembledInstruction, context: &mut PseudocodeContext, type_info: &HashMap<String, String>) -> String {
    let parts: Vec<&str> = instruction.op_str.split(',').collect();
    if parts.len() == 2 {
        let dest = simplify_operand(parts[0].trim(), context, &HashMap::new(), type_info);
        let src = simplify_operand(parts[1].trim(), context, &HashMap::new(), type_info);
        let op = match instruction.mnemonic.as_str() {
            "add" => "+=",
            "sub" => "-=",
            "imul" => "*=",
            "idiv" => "/=",
            _ => unreachable!(),
        };
        format!("{} {} {};", dest, op, src)
    } else {
        format!("// Unsimplified {}: {}", instruction.mnemonic, instruction.op_str)
    }
}

fn simplify_bitwise(instruction: &DisassembledInstruction, context: &mut PseudocodeContext, type_info: &HashMap<String, String>) -> String {
    let parts: Vec<&str> = instruction.op_str.split(',').collect();
    if parts.len() == 2 {
        let dest = simplify_operand(parts[0].trim(), context, &HashMap::new(), type_info);
        let src = simplify_operand(parts[1].trim(), context, &HashMap::new(), type_info);
        let op = match instruction.mnemonic.as_str() {
            "and" => "&=",
            "or" => "|=",
            "xor" => "^=",
            "not" => "= ~",
            _ => unreachable!(),
        };
        format!("{} {} {};", dest, op, src)
    } else {
        format!("// Unsimplified {}: {}", instruction.mnemonic, instruction.op_str)
    }
}

fn simplify_shift(instruction: &DisassembledInstruction, context: &mut PseudocodeContext, type_info: &HashMap<String, String>) -> String {
    let parts: Vec<&str> = instruction.op_str.split(',').collect();
    if parts.len() == 2 {
        let dest = simplify_operand(parts[0].trim(), context, &HashMap::new(), type_info);
        let src = simplify_operand(parts[1].trim(), context, &HashMap::new(), type_info);
        let op = if instruction.mnemonic == "shl" { "<<=" } else { ">>=" };
        format!("{} {} {};", dest, op, src)
    } else {
        format!("// Unsimplified {}: {}", instruction.mnemonic, instruction.op_str)
    }
}

fn simplify_cmp(instruction: &DisassembledInstruction, context: &mut PseudocodeContext, type_info: &HashMap<String, String>) -> String {
    let parts: Vec<&str> = instruction.op_str.split(',').collect();
    if parts.len() == 2 {
        let left = simplify_operand(parts[0].trim(), context, &HashMap::new(), type_info);
        let right = simplify_operand(parts[1].trim(), context, &HashMap::new(), type_info);
        context.current_condition = Some(format!("{} - {}", left, right));
        format!("// Compare: {} - {}", left, right)
    } else {
        format!("// Unsimplified cmp: {}", instruction.op_str)
    }
}

fn simplify_test(instruction: &DisassembledInstruction, context: &mut PseudocodeContext, type_info: &HashMap<String, String>) -> String {
    let parts: Vec<&str> = instruction.op_str.split(',').collect();
    if parts.len() == 2 {
        let left = simplify_operand(parts[0].trim(), context, &HashMap::new(), type_info);
        let right = simplify_operand(parts[1].trim(), context, &HashMap::new(), type_info);
        context.current_condition = Some(format!("{} & {}", left, right));
        format!("// Test: {} & {}", left, right)
    } else {
        format!("// Unsimplified test: {}", instruction.op_str)
    }
}

fn simplify_conditional_jump(instruction: &DisassembledInstruction, context: &mut PseudocodeContext) -> String {
    let condition = match instruction.mnemonic.as_str() {
        "je" => "==",
        "jne" => "!=",
        "jg" => ">",
        "jge" => ">=",
        "jl" => "<",
        "jle" => "<=",
        "ja" => "> (unsigned)",
        "jae" => ">= (unsigned)",
        "jb" => "< (unsigned)",
        "jbe" => "<= (unsigned)",
        _ => "?",
    };
    
    if let Some(ref current_condition) = context.current_condition {
        format!("if ({} {} 0) {{ goto {}; }}", current_condition, condition, instruction.op_str)
    } else {
        format!("if (condition {}) {{ goto {}; }}", condition, instruction.op_str)
    }
}

fn simplify_jmp(instruction: &DisassembledInstruction, context: &mut PseudocodeContext) -> String {
    if let Some(current_switch) = context.current_switch() {
        format!("goto {}_{};", current_switch, instruction.op_str)
    } else if let Some(current_loop) = context.current_loop() {
        if instruction.op_str == current_loop {
            "continue;".to_string()
        } else {
            format!("goto {};", instruction.op_str)
        }
    } else {
        format!("goto {};", instruction.op_str)
    }
}

fn simplify_call(instruction: &DisassembledInstruction, context: &mut PseudocodeContext, type_info: &HashMap<String, String>) -> String {
    let target = simplify_operand(&instruction.op_str, context, &HashMap::new(), type_info);
    format!("{}();", target)
}

fn simplify_return(instruction: &DisassembledInstruction, context: &mut Pseudocode