use std::collections::HashSet;
use std::path::PathBuf;
use log::info;
use vhdl_lang::ast::DesignFile;
use vhdl_lang::{kind_str, Token, VHDLParser, VHDLStandard};

use super::hdlparam::*;

#[allow(unused)]
pub fn vhdl_parser(path: &str) -> FastHdlparam {
    // The path of SystemVerilog source file
    let path = PathBuf::from(path);

    let mut hdlparam = FastHdlparam {
        fast_macro: Macro {
            defines: Vec::new(),
            errors: Vec::new(),
            includes: Vec::new(),
            invalid: Vec::new()
        },
        content: Vec::new(),
        file_type: "common".to_string()
    };

    let parser = VHDLParser::new(VHDLStandard::VHDL2008);
    let mut diagnostics = Vec::new();
    let (_, design_file) = parser.parse_design_file(&path, &mut diagnostics).unwrap();

    let mut all_tockens = Vec::new();
    for (tokens, _) in design_file.design_units {
        all_tockens.extend(tokens);
    }

    hdlparam.content.extend(parse_tokens(all_tockens));
    hdlparam
}

pub fn vhdl_parse(path: &PathBuf) -> Option<DesignFile> {
    let mut diagnostics = Vec::new();
    let parser = VHDLParser::new(VHDLStandard::VHDL2008);
    if let Ok((_, design_file)) = parser.parse_design_file(path, &mut diagnostics) {
        return Some(design_file);
    }
    None
}

pub fn vhdl_parse_str(path: &PathBuf, code: &str) -> Option<DesignFile> {
    let mut diagnostics = Vec::new();
    let parser = VHDLParser::new(VHDLStandard::VHDL2008);
    if let Ok((_, design_file)) = parser.parse_vhdl_str(code, path, &mut diagnostics) {
        return Some(design_file);
    }
    None
}

pub fn make_fast_from_design_file(design_file: &DesignFile) -> Option<FastHdlparam> {
    let mut hdlparam = FastHdlparam {
        fast_macro: Macro {
            defines: Vec::new(),
            errors: Vec::new(),
            includes: Vec::new(),
            invalid: Vec::new()
        },
        content: Vec::new(),
        file_type: "common".to_string()
    };

    let mut all_tockens = Vec::new();
    for (tokens, _) in &design_file.design_units {
        all_tockens.extend(tokens.clone());
    }

    hdlparam.content.extend(parse_tokens(all_tockens));
    Some(hdlparam)
}


fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
    let mut modules = Vec::new();
    let mut last_module_name = String::new();
    let mut instance_type = HashSet::new();

    let mut i = 0;
    while i < tokens.len() {
        let token = &tokens[i];
        match kind_str(token.kind) {
            "entity" => {
                let start_pos = tokens[i].pos.range.start;
                i += 1;
                let entity_name = get_value(&tokens[i]);

                if (i >= 2 as usize) && (kind_str(tokens[i - 2].kind) != "use") || (i < 2) {
                    // 遍历整个 entity 内部的 token，找到结束的 token 和代表 port 的 token
                    let mut end = i;
                    let mut port_begin: usize = 0;
                    while (
                        end + 1 < tokens.len()) &&
                        !(kind_str(tokens[end].kind) == "end" &&
                        (kind_str(tokens[end + 1].kind) == "entity" || get_value(&tokens[end + 1]) == entity_name)
                    ) {
                        if kind_str(tokens[end].kind) == "port" {
                            port_begin = end;
                        }
                        end += 1;
                    }

                    let (next_i, end_pos) = if end + 1 < tokens.len() && get_value(&tokens[end + 1]) == entity_name {
                        (end + 1,  tokens[end + 2].pos.range.end)
                    } else if end + 3 < tokens.len() {
                        (end + 2, tokens[end + 3].pos.range.end)
                    } else {
                        (i, tokens[end].pos.range.end)
                    };
                    
                    // 创建一个空的 module 并标记上 last_module_name
                    let module = Module {
                        name: entity_name.to_string(),
                        arch_name: "".to_string(),
                        params: Vec::new(),
                        ports: Vec::new(),
                        instances: Vec::new(),
                        range: Range {
                            start: Position {
                                line: start_pos.line + 1,
                                character: start_pos.character + 1
                            },
                            end: Position {
                                line: end_pos.line + 1,
                                character: end_pos.character + 1
                            }
                        }
                    };
                    last_module_name = entity_name.to_string();
                    modules.push(module);

                    // 如果内部含有 port，则不能直接跳过，否则直接跳过
                    if port_begin > 0 {
                        i = port_begin;
                        continue;
                    } else {
                        i = next_i;
                    }
                }
            }
            "architecture" => {
                if (i >= 1 as usize) && (kind_str(tokens[i-1].kind) != "end") || (i < 1) {
                    // !!! We not think of Architecture as VHDL Module Now
                    // !!! So skip all Architecture Parse when there is no it's Entity
                    let name = get_value(&tokens[i+3]);
                    if let None = modules.iter().find(|module| module.name == name) {
                        // let start_pos = tokens[i].pos.range.start;
                        i += 1;
                        let arch_name = get_value(&tokens[i]);
                        // println!("arch name {:?}", arch_name);
                        let mut end = i;
                        while (end+1 < tokens.len()) &&
                            !(kind_str(tokens[end].kind) == "end" &&
                            (kind_str(tokens[end+1].kind) == "architecture" || get_value(&tokens[end+1]) == arch_name)) {
                            end += 1;
                        }
                        if end+1 < tokens.len() && get_value(&tokens[end+1]) == arch_name {
                            i = end + 2;
                        } else if end + 3 < tokens.len() {
                            i = end + 3;
                        } else {
                            i = end;
                        };
                        // let end_pos = if end+1 < tokens.len() && get_value(&tokens[end+1]) == arch_name {
                        //     i = end + 2;
                        //     tokens[end+2].pos.range.end
                        // } else if end + 3 < tokens.len() {
                        //     i = end + 3;
                        //     tokens[end+3].pos.range.end
                        // } else {
                        //     i = end;
                        //     tokens[end].pos.range.end
                        // };
                        // let module = Module {
                        //     name: name.to_string(),
                        //     params: Vec::new(),
                        //     ports: Vec::new(),
                        //     instances: Vec::new(),
                        //     range: Range {
                        //         start: Position {
                        //             line: start_pos.line + 1,
                        //             character: start_pos.character + 1
                        //         },
                        //         end: Position {
                        //             line: end_pos.line + 1,
                        //             character: end_pos.character + 1
                        //         }
                        //     }
                        // };
                        // last_module_name = name.to_string();
                        // modules.push(module);
                    } else {
                        let module = modules.iter_mut().find(|module| module.name == name).unwrap();
                        let arch_name = get_value(&tokens[i+1]);
                        module.arch_name = arch_name;
                    }
                }
            }
            "configuration" => {
                i += 1;
                while i < tokens.len() && !(kind_str(tokens[i].kind) == "end" && kind_str(tokens[i+1].kind) == "configuration") {
                    i += 1;
                }
                i += 1;
            }
            "package" => {
                i += 1;
                // println!("get package");
                while i < tokens.len() && !(kind_str(tokens[i].kind) == "end" && kind_str(tokens[i+1].kind) == "package") {
                    i += 1;
                }
                i += 1;
            }
            "component" => {
                i += 1;
                instance_type.insert(get_value(&tokens[i]));
                // println!("instance {:?}", kind_str(tokens[i].kind));
                while i < tokens.len() && !(kind_str(tokens[i].kind) == "end" && kind_str(tokens[i+1].kind) == "component") {
                    i += 1;
                }
                i += 1;
            }
            "signal" => {
                i += 1;
                while !(kind_str(tokens[i+1].kind) == ";") {
                    i += 1;
                }
            }
            "attribute" => {
                i += 1;
                while !(kind_str(tokens[i+1].kind) == ";") {
                    i += 1;
                }
            }
            ":" => {
                if (i+2 < tokens.len()) && (kind_str(tokens[i+2].kind) != "use") {
                    if instance_type.contains(&get_value(&tokens[i+1])) {
                        let instance = Instance {
                            name: get_value(&tokens[i-1]),
                            inst_type: get_value(&tokens[i+1]),
                            instports: None,
                            instparams: None,
                            intstport_assignments: Vec::new(),
                            intstparam_assignments: Vec::new(),
                            range: Range {
                                start: Position {
                                    line: tokens[i-1].pos.range.start.line + 1,
                                    character: tokens[i-1].pos.range.start.character + 1
                                },
                                end: Position {
                                    line: tokens[i+1].pos.range.start.line + 1,
                                    character: tokens[i+1].pos.range.start.character + 1
                                }
                            }
                        };
                        if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
                            module.instances.push(instance);
                        }
                    } else if kind_str(tokens[i+1].kind) == "entity" {
                        let instance = Instance {
                            name: get_value(&tokens[i-1]),
                            inst_type: get_value(&tokens[i+2]),
                            instports: None,
                            instparams: None,
                            intstport_assignments: Vec::new(),
                            intstparam_assignments: Vec::new(),
                            range: Range {
                                start: Position {
                                    line: tokens[i-1].pos.range.start.line + 1,
                                    character: tokens[i-1].pos.range.start.character + 1
                                },
                                end: Position {
                                    line: tokens[i+2].pos.range.start.line + 1,
                                    character: tokens[i+2].pos.range.start.character + 1
                                }
                            }
                        };
                        if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
                            module.instances.push(instance);
                        }
                        i += 1;
                    } else {
                        let name = get_value(&tokens[i-1]);
                        let mut inst_type = String::new();
                        while i < tokens.len() {
                            if kind_str(tokens[i].kind) == "generic" || kind_str(tokens[i].kind) == "port" || kind_str(tokens[i].kind) == "end" {
                                i = i - 1;
                                break;
                            } 
                            inst_type = inst_type + &get_value(&tokens[i]);
                            i += 1;
                        }
                        let instance = Instance {
                            name,
                            inst_type,
                            instports: None,
                            instparams: None,
                            intstport_assignments: Vec::new(),
                            intstparam_assignments: Vec::new(),
                            range: Range {
                                start: Position {
                                    line: tokens[i-1].pos.range.start.line + 1,
                                    character: tokens[i-1].pos.range.start.character + 1
                                },
                                end: Position {
                                    line: tokens[i+1].pos.range.start.line + 1,
                                    character: tokens[i+1].pos.range.start.character + 1
                                }
                            }
                        };
                        if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
                            module.instances.push(instance);
                        }
                    }
                }
            }
            "generic" => {
                let is_map = kind_str(tokens[i+1].kind) == "map";
                let (params, next_index) = parse_parameters(&tokens, i + 1, is_map);
                if is_map {
                    let start = params.first().unwrap().range.start.clone();
                    let end = params.last().unwrap().range.start.clone();
                    if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
                        module.instances.last_mut().unwrap().instparams = Some(Range { start, end });
                    }
                } else {
                    if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
                        module.params.extend(params);
                    }
                }
                i = next_index;
            }
            "port" => {
                let is_map = kind_str(tokens[i+1].kind) == "map";
                let (ports, next_index) = parse_port(&tokens, i + 1, is_map);
                if is_map {
                    let start = ports.first().unwrap().range.start.clone();
                    let end = ports.last().unwrap().range.start.clone();
                    if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
                        module.instances.last_mut().unwrap().instports = Some(Range { start, end });
                    }
                } else {
                    if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
                        module.ports.extend(ports);
                    }
                }
                i = next_index;
            }
            _ => {}
        }
        i += 1;
    }

    // println!("{:?}", modules);
    modules

}


#[allow(unused)]
fn parse_port(tokens: &[Token], start: usize, is_map: bool) -> (Vec<Port>, usize) {
    let mut ports = Vec::new();
    let mut i = start;
    let mut stack = Vec::new();

    while i < tokens.len() {
        let token = &tokens[i];
        match kind_str(token.kind) {
            "(" => {
                stack.push(token);
            }
            ")" => {
                if stack.is_empty() {
                    break;
                }
                stack.pop();
            }
            ";" => {
                if stack.is_empty() {
                    break;
                }
            }
            "{identifier}" => {
                if is_map {
                    let start_pos = tokens[i].pos.range.start;
                    while kind_str(tokens[i+1].kind) != ")" {
                        i += 1;
                    }
                    let end_pos = tokens[i].pos.range.end;
                    let port = Port {
                        name: "none".to_string(),
                        dir_type: "none".to_string(),
                        net_type: "none".to_string(),
                        width: "none".to_string(),
                        signed: "unsigned".to_string(),
                        range: Range {
                            start: Position {
                                line: start_pos.line + 1,
                                character: start_pos.character + 1
                            },
                            end: Position {
                                line: end_pos.line + 1,
                                character: end_pos.character + 1
                            }
                        },
                    };
                    ports.push(port);
                } else {
                    let mut ports_token = Vec::new();
                    while kind_str(tokens[i].kind) != ":" {
                        // println!("{:?}", kind_str(tokens[i].kind));
                        if kind_str(tokens[i].kind) == "{identifier}" {
                            ports_token.push(&tokens[i]);
                        }
                        i += 1;
                    }
                    // let start_pos = tokens[i].pos.range.start;
                    let width ;
                    let end_idx;
                    let direction = if kind_str(tokens[i+1].kind) == "buffer" || kind_str(tokens[i+1].kind) == "out" {
                        i += 1;
                        "out" 
                    } else if kind_str(tokens[i+1].kind) == "in" {
                        i += 1;
                        "in"
                    } else {
                        "unknown"
                    };
                    if kind_str(tokens[i+2].kind) == "(" {
                        let (width_str, index) = parse_width(&tokens, i+2);
                        width = "[".to_string() + &width_str + "]";
                        end_idx = index-1;
                    } else if kind_str(tokens[i+2].kind) == "range" {
                        width = "[".to_string() + &get_value(&tokens[i+3]) + ":" + &get_value(&tokens[i+5]) + "]";
                        end_idx = i+5;
                    } else {
                        width = "1".to_string();
                        end_idx = i+1;
                    }
                    let end_pos = tokens[end_idx].pos.range.end;
                    for tok in ports_token {
                        let port = Port {
                            name: get_value(&tok),
                            dir_type: direction.to_string(),
                            net_type: get_value(&tokens[i+1]),
                            width: width.to_string(),
                            signed: "unsigned".to_string(),
                            range: Range {
                                start: Position {
                                    line: tok.pos.range.start.line + 1,
                                    character: tok.pos.range.start.character + 1
                                },
                                end: Position {
                                    line: end_pos.line + 1,
                                    character: end_pos.character + 1
                                }
                            },
                        };
                        ports.push(port);
                    }
                    i = end_idx;
                }
            }
            _ => {}
        }
        i += 1;
    }

    (ports, i)
}


#[allow(unused)]
fn parse_width(tokens: &[Token], start: usize) -> (String, usize) {
    let mut width = String::new();
    let mut i = start;
    let mut stack = Vec::new();

    while i < tokens.len() {
        match kind_str(tokens[i].kind) {
            "(" => {
                stack.push(&tokens[i]);
            }
            ")" => {
                if stack.is_empty() {
                    break;
                }
                stack.pop();
            }
            ";" => {
                if stack.is_empty() {
                    break;
                }
            }
            _ => {
                if stack.len() >= 1 {
                    if get_value(&tokens[i]) == "None" {
                        if kind_str(tokens[i].kind) == "downto" || kind_str(tokens[i].kind) == "to" {
                            width = width + ":";
                        } else {
                            width = width + kind_str(tokens[i].kind);
                        }
                    } else {
                        width = width + &get_value(&tokens[i]);
                    }
                }
            }
        }
        i += 1;
    }
    (width, i)
}


#[allow(unused)]
fn parse_parameters(tokens: &[Token], start: usize, is_map: bool) -> (Vec<Parameter>, usize) {
    // println!("I am here, start {start}");
    let mut params = Vec::new();
    let mut i = start;
    let mut stack = Vec::new();

    while i < tokens.len() {
        let token = &tokens[i];
        match kind_str(token.kind) {
            "(" => {
                stack.push(token);
            }
            ")" => {
                if stack.is_empty() {
                    break;
                }
                stack.pop();
            }
            ";" => {
                if stack.is_empty() {
                    break;
                }
            }
            "{identifier}" => {
                if is_map {
                    let start_pos = tokens[i].pos.range.start;
                    let end_pos = tokens[i+2].pos.range.end;
                    let param = Parameter {
                        name: get_value(&tokens[i]),
                        net_type: get_value(&tokens[i+2]),
                        init: get_value(&tokens[i+2]),
                        range: Range {
                            start: Position {
                                line: start_pos.line + 1,
                                character: start_pos.character + 1
                            },
                            end: Position {
                                line: end_pos.line + 1,
                                character: end_pos.character + 1
                            }
                        },
                    };
                    i += 2;
                    params.push(param);
                } else {
                    let mut parameters = Vec::new();
                    while kind_str(tokens[i].kind) != ":" {
                        // println!("{:?}", kind_str(tokens[i].kind));
                        if kind_str(tokens[i].kind) == "{identifier}" {
                            parameters.push(&tokens[i]);
                        }
                        i += 1;
                    }
                    let net_type = get_value(&tokens[i+1]);
                    let init = if kind_str(tokens[i+2].kind) == ":=" {
                        get_value(&tokens[i+3])
                    } else {
                        "unknown".to_string()
                    };
                    let end_pos = if kind_str(tokens[i+2].kind) == ":=" {
                        tokens[i+2].pos.range.end
                    } else {
                        tokens[i+1].pos.range.end
                    };
                    i = if kind_str(tokens[i+2].kind) == ":=" {
                        i + 3
                    } else {
                        i + 1
                    };
                    for param_token in parameters {
                        let start_pos = param_token.pos.range.start;
                        let param = Parameter {
                            name: get_value(param_token),
                            net_type: net_type.to_string(),
                            init: init.to_string(),
                            range: Range {
                                start: Position {
                                    line: start_pos.line + 1,
                                    character: start_pos.character + 1
                                },
                                end: Position {
                                    line: end_pos.line + 1,
                                    character: end_pos.character + 1
                                }
                            },
                        };
                        params.push(param);
                    }
                }
            }
            _ => {}
        }
        i += 1;
    }

    (params, i)
}


#[allow(unused)]
fn get_value(token: &Token) -> String {
    match &token.value {
        vhdl_lang::Value::Identifier(symbol) => {
            return symbol.name_utf8();
        }
        vhdl_lang::Value::String(latin1_string) => {
            return latin1_string.to_string();
        }
        vhdl_lang::Value::BitString(latin1_string, _) => {
            return latin1_string.to_string().replace("\\", "");
        }
        vhdl_lang::Value::AbstractLiteral(latin1_string, _) => {
            return latin1_string.to_string();
        }
        vhdl_lang::Value::Character(character) => {
            return character.to_string();
        }
        vhdl_lang::Value::Text(latin1_string) => {
            return latin1_string.to_string();
        }
        vhdl_lang::Value::None => {
            return "None".to_string();
        }
    }
}