use std::{path::PathBuf, str::FromStr, sync::Arc};

use log::info;
use regex::Regex;
use ropey::RopeSlice;
use tower_lsp::lsp_types::{Hover, HoverContents, LanguageString, MarkedString, Position, Range, Url};

use crate::{core::{self, hdlparam::{Define, FastHdlparam}, primitive_parser::PrimitiveText}, server::LspServer};

use super::{get_language_id_by_path_str, get_word_range_at_position, resolve_path, to_escape_path};

/// 将 4'b0011 分解为 ("b", "0011")
/// 需要支持的格式：
/// - `4'b0011`
/// - `8'b0000_1111`
fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> {
    if digit_string.len() == 0 {
        return None;
    }

    match digit_string.split_once("'") {
        Some((width_string, body_string)) => {
            let tag = body_string.get(0..1);
            let digit = body_string.get(1..);

            if tag.is_none() || digit.is_none() {
                return None;
            }
            
            let width = width_string.parse::<u32>();
            if width.is_err() {
                return None;
            }

            // 此时，4'b0011 被自动分割成了 
            // tag: b
            // digit: 0011
            // 前面的 4 被丢弃了，因为 4 可以通过 0011 的长度判断出来
            let tag = tag.unwrap();
            let digit = digit.unwrap();

            return Some((tag, digit));
        },
        None => return None
    };
}

fn convert_tag_to_radix(tag: &str) -> Option<u32> {
    let tag = &tag.to_string().to_lowercase()[..];
    match tag {
        "b" => Some(2),
        "o" => Some(8),
        "h" => Some(16),
        _ => None
    }
}

/// 计算出有符号和无符号下的表示
/// - `tag`: b, o, h 这些代表进制的字符，
/// - `digit_string`: 进制数
/// 
/// 需要支持的格式：
/// - `4'b0011`
/// - `8'b0000_1111`
fn convert_to_sign_unsign<'a>(tag: &'a str, digit_string: &str) -> Option<(String, String)> {
    let radix = convert_tag_to_radix(tag);
    if radix.is_none() {
        return None;
    }
    let radix = radix.unwrap();
    let raw_digit_string = digit_string.replace("_", "");
    let unsigned_decimal = u128::from_str_radix(&raw_digit_string, radix);
    
    if unsigned_decimal.is_err() {
        return None;
    }
    let unsigned_decimal = unsigned_decimal.unwrap();

    let pow = radix.pow(digit_string.len() as u32) as u128;

    let mut signed_decimal = unsigned_decimal as i128;
    if unsigned_decimal >= pow >> 1 {
        signed_decimal = (unsigned_decimal as i128) - (pow as i128);
    }

    Some((
        signed_decimal.to_string(),
        unsigned_decimal.to_string()
    ))
}

/// 将 1'b1 翻译成 10进制
pub fn hover_format_digit(line: &RopeSlice, pos: Position, language_id: &str) -> Option<Hover> {
    let regex = Regex::new(r"[0-9'bho_]").unwrap();
    let token_result = get_word_range_at_position(line, pos, regex);

    if token_result.is_none() {
        return None;
    }

    let ( digit_string, range ) = token_result.unwrap();

    if let Some((tag, digit)) = parse_digit_string(&digit_string) {
        if let Some((signed_string, unsigned_string)) = convert_to_sign_unsign(tag, digit) {
            let digit_title = LanguageString {
                language: language_id.to_string(),
                value: format!("{}'{}{}", digit.replace("_", "").len(), tag, digit)
            };
            let markdown = HoverContents::Array(vec![
                MarkedString::LanguageString(digit_title),
                MarkedString::String(format!("`unsigned` {}\n", unsigned_string)),
                MarkedString::String(format!("`signed` {}", signed_string))
            ]);
            let hover = Hover {
                contents: markdown,
                range: Some(range)
            };
            return Some(hover);
        }
    }

    None
}

pub fn hover_include(uri: &Url, line: &RopeSlice, pos: Position, language_id: &String) -> Option<Hover> {
    let line_text = line.as_str().unwrap_or("");
    if line_text.trim().starts_with("`include") {
        let character = pos.character as usize;
        let first_quote_idx = line_text.find("\"");
        let last_quote_idx = line_text.rfind("\"");
        
        if first_quote_idx.is_none() || last_quote_idx.is_none() {
            return None;
        }

        let first_quote_idx = first_quote_idx.unwrap();
        let last_quote_idx = last_quote_idx.unwrap();
        if character >= first_quote_idx && character <= last_quote_idx {
            let mut path_string = &line_text[(first_quote_idx + 1) .. last_quote_idx];
            if path_string.starts_with("./") || path_string.starts_with(".\\") {
                path_string = &path_string[2 ..];
            }
            
            // 路径转换
            let path = match PathBuf::from_str(uri.path()) {
                Ok(path) => path,
                Err(error) => {
                    info!("error happen in <goto_include_definition>: {:?}", error);
                    return None;
                }
            };
            let escape_path = to_escape_path(&path);
            let escape_path = escape_path.to_str().unwrap_or("");
            if escape_path.len() == 0 {
                return None;
            }

            if let Some(abs_path) = resolve_path(escape_path, path_string) {                
                let content = format!("{:?}", abs_path).replace("\\", "/");
                let language_string = LanguageString {
                    language: language_id.to_string(),
                    value: content
                };
                let markdown = MarkedString::LanguageString(language_string);
                let range = Range::new(
                    Position { line: pos.line, character: first_quote_idx as u32 },
                    Position { line: pos.line, character: (last_quote_idx + 1) as u32 }
                );
                let hover = Hover {
                    contents: HoverContents::Scalar(markdown),
                    range: Some(range)
                };
                return Some(hover);
            }
        }
    }

    None
}

fn make_macro_define_content(macro_define: &Define) -> String {
    let macro_name = macro_define.name.trim();
    let macro_value = macro_define.replacement.trim();
    if macro_define.params.len() == 0 {
        format!("`define {} {}", macro_name, macro_value)
    } else {
        let mut macro_sig_vec: Vec<String> = Vec::new();
        for macro_param in &macro_define.params {
            if macro_param.value == "Unknown" {
                macro_sig_vec.push(macro_param.name.to_string());
            } else {
                macro_sig_vec.push(format!("{}={}", macro_param.name, macro_param.value));
            }
        }

        let macro_sig = macro_sig_vec.join(", ");
        format!("`define {}({}) {}", macro_name, macro_sig, macro_value)
    }
}

pub fn hover_macro(server: &LspServer, line: &RopeSlice, pos: Position, language_id: &str) -> Option<Hover> {
    let macro_text_regex = Regex::new(r"[`_0-9a-zA-Z]").unwrap();
    if let Some((macro_text, range)) = get_word_range_at_position(line, pos, macro_text_regex) {
        if macro_text.starts_with("`") {
            if let Some((macro_define, _)) = server.find_macros(&macro_text) {
                let content = make_macro_define_content(&macro_define);
                let language_string = LanguageString {
                    language: language_id.to_string(),
                    value: content
                };
                let markdown = MarkedString::LanguageString(language_string);
                let hover = Hover {
                    contents: HoverContents::Scalar(markdown),
                    range: Some(range)
                };
                return Some(hover);   
            }
        }
    }
    None
}


fn goto_instantiation<'a>(
    server: &LspServer,
    fast: &'a FastHdlparam,
    token_name: &str,
    pos: &Position,
    range: &Range,
    language_id: &str
) -> Option<Hover> {
    for module in &fast.content {
        for instance in &module.instances {
            if let Some(param_range) = &instance.instparams {
                // let in_scope = compare_pos(&range.start, pos) != 1 && compare_pos(pos, &range.end) != 1;
                // info!("pos: {pos:?}, param_range: {range:?}, in_scope: {in_scope:?}");
                if param_range.contains(pos) {
                    let module = match server.srcs.hdl_param.find_module_by_name(&instance.inst_type) {
                        Some(module) => module,
                        None => return None
                    };

                    // info!("current pos: {pos:#?}");
                    // info!("param_range: {param_range:#?}");
                    // info!("position param find belong module: {:?}", module);

                    let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
                    if file_type == "primitives" {
                        let primitives_text = server.srcs.primitive_text.clone();
                        let params_assignments = &module.instances.first().unwrap().intstparam_assignments;
                        for assignment in params_assignments {
                            if assignment.parameter.clone().unwrap() == token_name {
                                let hover = make_primitives_param_desc_hover(
                                    primitives_text, &instance.inst_type,
                                    assignment, range, language_id
                                );
                                return Some(hover);
                            }
                        }
                    } else {
                        for param in &module.params {
                            if token_name == param.name {
                                let hover = make_param_desc_hover(&file_type, param, range, language_id);
                                return Some(hover);
                            }
                        }
                    }
                    return None;
                }       
            }

            if let Some(port_range) = &instance.instports {
                if port_range.contains(pos) {
                    let module = match server.srcs.hdl_param.find_module_by_name(&instance.inst_type) {
                        Some(module) => module,
                        None => return None
                    };

                    // info!("current pos: {pos:#?}");
                    // info!("port_range: {port_range:#?}");
                    // info!("position port find belong module: {:?}", module);

                    let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
                    if file_type == "primitives" {
                        let primitives_text = server.srcs.primitive_text.clone();
                        let port_assignments = &module.instances.first().unwrap().intstport_assignments;
                        for assignment in port_assignments {
                            if assignment.port.clone().unwrap() == token_name {
                                let hover = make_primitives_port_desc_hover(
                                    primitives_text, &instance.inst_type,
                                    assignment, range, language_id
                                );
                                return Some(hover);
                            }
                        }
                    } else {
                        for port in &module.ports {
                            if token_name == port.name {
                                let hover = make_port_desc_hover(&file_type, port, range, language_id);
                                return Some(hover);
                            }
                        }
                    }
                    return None;
                }
            }
        }
    }
    
    None
}

/// 计算 position 赋值的 port 或者 param
/// 比如 .clk ( clk ) 中的 .clk
pub fn hover_position_port_param(
    server: &LspServer,
    line: &RopeSlice,
    url: &Url,
    pos: Position,
    language_id: &str
) -> Option<Hover> {
    let position_port_regex = Regex::new(r"[._0-9a-zA-Z]").unwrap();
    if let Some((name, range)) = get_word_range_at_position(line, pos, position_port_regex) {
        if name.starts_with(".") {
            let name = &name[1..];
            // 进入最近的 scope 寻找
            let fast_map = server.srcs.hdl_param.path_to_hdl_file.read().unwrap();
            let path = PathBuf::from_str(url.path()).unwrap();
            let path = to_escape_path(&path);
            let path_string = path.to_str().unwrap();
            if let Some(hdl_file) = fast_map.get(path_string) {
                // 先找到属于哪一个 module
                let fast = &hdl_file.fast;
                if let Some(hover) = goto_instantiation(server, fast, name, &pos, &range, language_id) {
                    return Some(hover);
                }
            }
        }
    }

    None
}

fn make_primitives_param_desc_hover(
    primitives_text: Arc<PrimitiveText>,
    primitives_name: &str,
    inst_param: &crate::core::hdlparam::InstParameter,
    range: &Range, language_id: &str) -> Hover {

    let name = inst_param.parameter.clone().unwrap();
    let text_map = primitives_text.name_to_text.read().unwrap();
    let comment = if let Some(text) = text_map.get(primitives_name) {
        primitives_text.get_comment(&name, text)
    } else {
        "".to_string()
    };

    let language_string = LanguageString {
        language: language_id.to_string(),
        value: "parameter ".to_string() + &name + " " + &comment,
    };
    Hover {
        contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)),
        range: Some(range.clone())
    }
}

fn make_primitives_port_desc_hover(
    primitives_text: Arc<PrimitiveText>,
    primitives_name: &str,
    inst_port: &crate::core::hdlparam::InstPort,
    range: &Range, language_id: &str) -> Hover {

    let name = inst_port.port.clone().unwrap();
    let text_map = primitives_text.name_to_text.read().unwrap();
    let comment = if let Some(text) = text_map.get(primitives_name) {
        primitives_text.get_comment(&name, text)
    } else {
        "".to_string()
    };

    let language_string = LanguageString {
        language: language_id.to_string(),
        value: "port ".to_string() + &name + " " + &comment,
    };
    Hover {
        contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)),
        range: Some(range.clone())
    }
}

fn make_port_desc_hover(file_type: &str, port: &crate::core::hdlparam::Port, range: &Range, language_id: &str) -> Hover {
    info!("enter make_port_desc_hover, file_type: {}", file_type);

    let (language, value) = match file_type {
        "common" => {
            (language_id.to_string(), port.to_vlog_description())
        }
        "ip" => {
            ("vhdl".to_string(), port.to_vhdl_description())
        }
        "primitives" => {
            (language_id.to_string(), port.to_vlog_description())
        }
        _ => {
            (language_id.to_string(), port.to_vlog_description())
        }
    };

    let language_string = LanguageString { language, value };
    Hover {
        contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)),
        range: Some(range.clone())
    }
}

fn make_param_desc_hover(file_type: &str, param: &crate::core::hdlparam::Parameter, range: &Range, language_id: &str) -> Hover {
    let value = match file_type {
        "common" => {
            param.to_vlog_description()
        }
        "ip" => {
            param.to_vhdl_description()
        }
        "primitives" => {
            param.to_vlog_description()
        }
        _ => {
            param.to_vlog_description()
        }
    };

    let language_string = LanguageString {
        language: language_id.to_string(),
        value
    };
    Hover {
        contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)),
        range: Some(range.clone())
    }
}

pub fn hover_module_declaration(
    server: &LspServer,
    token_name: &str,
    #[allow(unused)]
    language_id: &str
) -> Option<Hover> {
    // info!("hover_module_declaration token: {:?}", token_name);

    // let test = server.srcs.hdl_param.module_name_to_path.read().unwrap();
    // info!("module name to path: {:#?}", test);
    let hdl_param = server.srcs.hdl_param.clone();
    if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(token_name) {
        match file_type.as_str() {
            "common" => {
                hover_common_module_declaration(
                    server,
                    token_name,
                    &module,
                    &def_path
                )
            },
            "ip" => {
                hover_ip_module_declaration(
                    server,
                    token_name,
                    &module,
                    &def_path
                )
            },
            "primitives" => {
                hover_primitives_module_declaration(
                    server,
                    token_name,
                    &module,
                    &def_path
                )
            },
            _ => None
        }
    } else {
        None
    }
}

fn hover_common_module_declaration(
    #[allow(unused)]
    server: &LspServer,
    #[allow(unused)]
    token_name: &str,
    #[allow(unused)]
    module: &core::hdlparam::Module,
    #[allow(unused)]
    def_path: &str
) -> Option<Hover> {
    let path_uri = Url::from_file_path(def_path.to_string()).unwrap().to_string();
    let def_row = module.range.start.line;
    let def_col = module.range.start.character;
    let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})");

    let port_num = module.ports.len();
    let param_num = module.params.len();
    let instance_num = module.instances.len();

    let port_desc = format!("`port` {port_num}, `param` {param_num}, `instantiation` {instance_num}");

    // 统计 dir
    let mut input_count = 0 as u32;
    let mut output_count = 0 as u32;
    let mut inout_count = 0 as u32;

    for port in &module.ports {
        match port.dir_type.as_str() {
            "input" => input_count += 1,
            "output" => output_count += 1,
            "inout" => inout_count += 1,
            _ => {}
        }
    }

    let io_desc = format!("`input` {input_count}, `output` {output_count}, `inout` {inout_count}");

    let mut markdowns = Vec::<MarkedString>::new();
    markdowns.push(MarkedString::String(port_desc));
    markdowns.push(MarkedString::String(io_desc));
    markdowns.push(MarkedString::String(define_info));
    markdowns.push(MarkedString::String("---".to_string()));

    let language_id = get_language_id_by_path_str(def_path);
    let module_profile = make_module_profile_code(&module);
    let profile_markdown = LanguageString {
        language: language_id.to_string(),
        value: module_profile
    };
    markdowns.push(MarkedString::LanguageString(profile_markdown));

    let hover = Hover {
        contents: HoverContents::Array(markdowns),
        range: None
    };

    return Some(hover);
}

fn hover_ip_module_declaration(
    #[allow(unused)]
    server: &LspServer,
    #[allow(unused)]
    token_name: &str,
    #[allow(unused)]
    module: &core::hdlparam::Module,
    #[allow(unused)]
    def_path: &str
) -> Option<Hover> {
    let def_path_buf = PathBuf::from_str(def_path).unwrap();
    
    if def_path_buf.exists() {
        // TODO: 当前工具链只支持 Xilinx 下的工具链，所以此处的代码是 vhdl 的代码
        // 如果未来需要支持其他的工具链，则需要从 server 下读取对应的变量

        let path_uri = Url::from_file_path(def_path.to_string()).unwrap().to_string();
        let def_row = module.range.start.line;
        let def_col = module.range.start.character;
        let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})");
    
        let port_num = module.ports.len();
        let param_num = module.params.len();
        let instance_num = module.instances.len();
    
        let port_desc = format!("`port` {port_num}, `generic` {param_num}, `instantiation` {instance_num}");
    
        // 统计 dir
        let mut input_count = 0 as u32;
        let mut output_count = 0 as u32;
        let mut inout_count = 0 as u32;
    
        for port in &module.ports {
            match port.dir_type.as_str() {
                "input" => input_count += 1,
                "output" => output_count += 1,
                "inout" => inout_count += 1,
                _ => {}
            }
        }
    
        let io_desc = format!("`input` {input_count}, `output` {output_count}, `inout` {inout_count}");
    
        let mut markdowns = Vec::<MarkedString>::new();
        markdowns.push(MarkedString::String(port_desc));
        markdowns.push(MarkedString::String(io_desc));
        markdowns.push(MarkedString::String(define_info));
        markdowns.push(MarkedString::String("---".to_string()));
    
        let language_id = get_language_id_by_path_str(def_path);
        let module_profile = make_entity_profile_code(&module);
        let profile_markdown = LanguageString {
            language: language_id.to_string(),
            value: module_profile
        };
        markdowns.push(MarkedString::LanguageString(profile_markdown));
    
        let hover = Hover {
            contents: HoverContents::Array(markdowns),
            range: None
        };
    
        Some(hover)
    } else {
        None
    }
}

fn hover_primitives_module_declaration(
    #[allow(unused)]
    server: &LspServer,
    #[allow(unused)]
    token_name: &str,
    #[allow(unused)]
    module: &core::hdlparam::Module,
    #[allow(unused)]
    def_path: &str
) -> Option<Hover> {
    let primitive_map = server.srcs.primitive_text.name_to_text.read().unwrap();

    if let Some(text) = primitive_map.get(token_name) {
        let mut markdowns = Vec::<MarkedString>::new();

        let mut lines: Vec<&str> = text.split_inclusive('\n').collect();

        if lines.len() > 1 {
            lines.remove(0);
            lines.pop();
        }

        let profile_markdown = LanguageString {
            language: "systemverilog".to_string(),
            value: lines.join("")
        };

        markdowns.push(MarkedString::LanguageString(profile_markdown));

        let hover = Hover {
            contents: HoverContents::Array(markdowns),
            range: None
        };

        Some(hover)
    } else {
        None
    }
}


/// 根据 module 获取 module 的简单描述代码
pub fn make_module_profile_code(module: &crate::core::hdlparam::Module) -> String {
    let mut codes = Vec::<String>::new();
    let param_num = module.params.len();
    let port_num = module.ports.len();  
    
    if module.params.len() > 0 {
        codes.push(format!("module {} #(", module.name));

        let max_param_name_length = module.params.iter().map(|param| param.name.len()).max().unwrap_or(0);
        
        for (i, param) in module.params.iter().enumerate() {
            let mut param_desc_array = Vec::<String>::new();
            param_desc_array.push(format!("parameter {}{}", param.name, " ".repeat(max_param_name_length - param.name.len())));
            if param.init != "unknown" {
                param_desc_array.push(format!(" = {}", param.init.to_string()));
            }
            
            let param_desc = param_desc_array.join(" ");
            if i == param_num - 1 {
                codes.push(format!("\t{param_desc}"));
            } else {
                codes.push(format!("\t{param_desc},"));
            }
        }
        codes.push(")(".to_string());
    } else {
        codes.push(format!("module {} (", module.name));
    }
    
    if module.ports.len() > 0 {

        let net_mapper = |net_type: &str| {
            if net_type == "unknown" {
                0
            } else {
                net_type.len()
            }
        };

        let sign_mapper = |signed: &str| {
            if signed == "unsigned" {
                0
            } else {
                "signed".len()
            }
        };

        let width_mapper = |width: &str| {
            if width == "1" {
                0
            } else {
                width.len()
            }
        };

        let max_net_length = module.ports.iter().map(|port| net_mapper(&port.net_type)).max().unwrap_or(0);
        let max_sign_length = module.ports.iter().map(|port| sign_mapper(&port.signed)).max().unwrap_or(0);
        let max_width_length = module.ports.iter().map(|port| width_mapper(&port.width)).max().unwrap_or(0);
        let max_dir_length = module.ports.iter().map(|port| port.dir_type.len()).max().unwrap_or(0);
        
        for (i, port) in module.ports.iter().enumerate() {
            let mut port_desc_array = Vec::<String>::new();

            // input, output, inout
            port_desc_array.push(port.dir_type.to_string());
            port_desc_array.push(" ".repeat(max_dir_length - port.dir_type.len() + 1));

            // reg, wire
            if port.net_type != "unknown" {
                port_desc_array.push(port.net_type.to_string());
            }
            port_desc_array.push(" ".repeat(max_net_length - net_mapper(&port.net_type) + 1));


            // signed, unsigned
            if port.signed != "unsigned" {
                port_desc_array.push("signed".to_string());
            }
            port_desc_array.push(" ".repeat(max_sign_length - sign_mapper(&port.signed) + 1));

            // [3:0]
            if port.width != "1" {
                port_desc_array.push(port.width.to_string());
            }
            port_desc_array.push(" ".repeat(max_width_length - width_mapper(&port.width) + 1));

            // port 的名字
            port_desc_array.push(port.name.to_string());

            let port_desc = port_desc_array.join("");
            if i == port_num - 1 {
                codes.push(format!("\t{port_desc}"));
            } else {
                codes.push(format!("\t{port_desc},"));
            }
        }
    }
    
    codes.push(format!(");"));

    let profile_string = codes.join("\n");
    profile_string
}

pub fn make_vhdl_module_profile_code(module: &crate::core::hdlparam::Module) -> String {
    let mut snippet_codes = Vec::<String>::new();

    snippet_codes.push(format!("u_{} : {}\n", module.name, module.name));

    // 2001 style，先计算出 generic 和 port，然后加入总体例化样板中
    let params_length = module.params.len();
    let ports_length = module.ports.len();

    if params_length > 0 {
        snippet_codes.push("generic map(\n".to_string());

        let max_param_name = module.params.iter().map(|param| param.name.len()).max().unwrap_or(0);
        let mut i: usize = 0;
        for generic in &module.params {
            let n_padding = " ".repeat(max_param_name - generic.name.len() + 1);
            snippet_codes.push(format!("\t{}{} => {}", generic.name, n_padding, generic.init));
            if i < params_length - 1 {
                snippet_codes.push(",\n".to_string());
            }
            i += 1;
        }
        snippet_codes.push(")\n".to_string());
    }

    if ports_length > 0 {
        snippet_codes.push("port map(\n\t-- ports\n".to_string());

        let max_port_name = module.ports.iter().map(|port| port.name.len()).max().unwrap_or(0);
        let mut i: usize = 0;

        for port in &module.ports {
            let n_padding = " ".repeat(max_port_name - port.name.len() + 1);
            snippet_codes.push(format!("\t{}{} => {}", port.name, n_padding, port.name));
            if i < ports_length - 1 {
                snippet_codes.push(",\n".to_string());
            }
            i += 1;
        }
        snippet_codes.push(");\n".to_string());
    }

    snippet_codes.join("")
}

/// vhdl 的 entity 的 profile
pub fn make_entity_profile_code(module: &crate::core::hdlparam::Module) -> String {
    let mut codes = Vec::<String>::new();
    // param 其实就是 generic
    let param_num = module.params.len();
    let port_num = module.ports.len();  
    
    codes.push(format!("entity {} is", module.name));
    // 缩进字符    
    if module.params.len() > 0 {
        codes.push("\tgeneric (".to_string());
        let max_param_name_length = module.params.iter().map(|param| param.name.len()).max().unwrap_or(0);
        let max_param_type_length = module.params.iter().map(|param| param.net_type.len()).max().unwrap_or(0);
        
        for (i, param) in module.params.iter().enumerate() {
            let mut param_desc_array = Vec::<String>::new();
            let param_name_align_spaces = " ".repeat(max_param_name_length - param.name.len() + 1);
            param_desc_array.push(format!("{}{}:", param.name, param_name_align_spaces));

            let param_type_align_spaces = " ".repeat(max_param_type_length - param.net_type.len() + 1);
            param_desc_array.push(format!("{}{}", param.net_type, param_type_align_spaces));
            
            if param.init != "unknown" {
                param_desc_array.push(format!(":= {}", param.init.to_string()));
            }
            
            let param_desc = param_desc_array.join(" ");
            if i == param_num - 1 {
                codes.push(format!("\t\t{param_desc}"));
            } else {
                codes.push(format!("\t\t{param_desc};"));
            }
        }
        codes.push("\t);".to_string());
    }
    
    if module.ports.len() > 0 {
        codes.push("\tport (".to_string());

        let max_port_length = module.ports.iter().map(|port| port.name.len()).max().unwrap_or(0);
        // let max_width_length = module.ports.iter().map(|port| width_mapper(&port.width)).max().unwrap_or(0);
        let max_dir_length = module.ports.iter().map(|port| port.dir_type.len()).max().unwrap_or(0);
        
        for (i, port) in module.ports.iter().enumerate() {
            let mut port_desc_array = Vec::<String>::new();

            // port 的名字
            let port_name_align_spaces = " ".repeat(max_port_length - port.name.len() + 1);
            port_desc_array.push(format!("{}{}: ", port.name, port_name_align_spaces));

            // in, out, inout
            let port_dir_align_spaces = " ".repeat(max_dir_length - port.dir_type.len() + 1);
            port_desc_array.push(format!("{}{}", port.dir_type, port_dir_align_spaces));

            // std_logic, signed, unsigned 等等 
            if port.net_type != "unknown" {
                port_desc_array.push(port.net_type.to_lowercase());
            }

            // (57 downto 0)
            if port.width != "1" {
                // 内部的 width 统一表示成 [57:0] 这样的格式，需要转换一下
                let width_string = port.width.replace("[", "(").replace("]", ")").replace(":", " downto ");
                port_desc_array.push(width_string);
            }
            let port_desc = port_desc_array.join("");
            if i == port_num - 1 {
                codes.push(format!("\t\t{port_desc}"));
            } else {
                codes.push(format!("\t\t{port_desc};"));
            }
        }
        codes.push("\t);".to_string());
    }

    codes.push(format!("end entity {};", module.name));

    let profile_string = codes.join("\n");
    profile_string
}
