use std::{fs, path::PathBuf, str::FromStr};

use log::info;
use ropey::RopeSlice;
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionList, Documentation, MarkupContent, Position, Url};

use crate::{server::LspServer, utils::{is_character_ordered_match, resolve_path, to_escape_path}};

/// 补全 include 内部的系统路径
pub fn include_path_completion(
    uri: &Url,
    line: &RopeSlice,
    pos: Position
) -> Option<CompletionList> {
    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) {                
                // 遍历该路径下所有文件和文件夹，并构建新的自动补全列表
                info!("completion active in {:?}", abs_path);
                if abs_path.is_dir() {
                    let mut completion_items = Vec::<CompletionItem>::new();
                    for entry in fs::read_dir(abs_path).unwrap() {
                        let entry = entry.unwrap();
                        let path: PathBuf = entry.path();
                        let file_name = match path.file_name() {
                            Some(os_str) => os_str.to_str(),
                            None => continue
                        };

                        if file_name.is_none() {
                            continue;
                        }
                        let file_name = file_name.unwrap();

                        if path.is_dir() {
                            let label_details = CompletionItemLabelDetails {
                                description: Some("directory".to_string()),
                                ..Default::default()
                            };
                            completion_items.push(CompletionItem {
                                label: file_name.to_string(),
                                label_details: Some(label_details),
                                kind: Some(CompletionItemKind::FOLDER),
                                ..CompletionItem::default()
                            });
                        }

                        if path.is_file() {
                            let label_details = CompletionItemLabelDetails {
                                description: Some("file".to_string()),
                                ..Default::default()
                            };
                            completion_items.push(CompletionItem {
                                label: file_name.to_string(),
                                label_details: Some(label_details),
                                kind: Some(CompletionItemKind::FILE),
                                ..CompletionItem::default()
                            });
                        }
                    }

                    if !completion_items.is_empty() {
                        return Some(CompletionList {
                            is_incomplete: false,
                            items: completion_items
                        });
                    }
                }
            }
        }
    }
    None
}


/// 补全宏
pub fn vlog_directives_completion(
    token: &str,
    server: &LspServer
) -> Option<CompletionList> {
    // 先把固定宏定义比如 include, define 这种的放入其中
    let mut completion_items = server.vlog_directives.clone();

    // 再从每个文件中搜集定义的宏
    let hdl_param = server.srcs.hdl_param.clone();
    let path_to_hdl_file = hdl_param.path_to_hdl_file.read().unwrap();

    // 遍历所有的 defines
    for hdl_file in path_to_hdl_file.values() {
        for define in &hdl_file.fast.fast_macro.defines {
            if is_character_ordered_match(token, &define.name) {
                let label_details = CompletionItemLabelDetails {
                    description: Some("`define".to_string()),
                    ..Default::default()
                };
                let documentation = Documentation::MarkupContent(MarkupContent {
                    kind: tower_lsp::lsp_types::MarkupKind::Markdown,
                    value: format!("```verilog\n`define {} {}\n```", define.name.trim(), define.replacement.trim())
                });
                completion_items.push(CompletionItem {
                    label: define.name.to_string(),
                    kind: Some(CompletionItemKind::CONSTANT),
                    detail: Some("macro".to_string()),
                    // 用户定义的宏默认最高优先级
                    sort_text: Some("0".to_string()),
                    label_details: Some(label_details),
                    documentation: Some(documentation),
                    ..CompletionItem::default()
                });
            }
        }
    }

    Some(CompletionList {
        is_incomplete: false,
        items: completion_items
    })
}

/// 不是由 ` 触发的宏补全
pub fn vlog_directives_completion_without_prefix(
    token: &str,
    server: &LspServer
) -> Vec<CompletionItem> {
    // 先把固定宏定义比如 include, define 这种的放入其中
    let mut completion_items = server.vlog_directives.clone();

    // 再从每个文件中搜集定义的宏
    let hdl_param = server.srcs.hdl_param.clone();
    let path_to_hdl_file = hdl_param.path_to_hdl_file.read().unwrap();

    // 遍历所有的 defines
    for hdl_file in path_to_hdl_file.values() {
        for define in &hdl_file.fast.fast_macro.defines {
            if is_character_ordered_match(token, &define.name) {
                let label_details = CompletionItemLabelDetails {
                    description: Some("`define".to_string()),
                    ..Default::default()
                };
                let documentation = Documentation::MarkupContent(MarkupContent {
                    kind: tower_lsp::lsp_types::MarkupKind::Markdown,
                    value: format!("```verilog\n`define {} {}\n```", define.name.trim(), define.replacement.trim())
                });
                completion_items.push(CompletionItem {
                    label: define.name.to_string(),
                    kind: Some(CompletionItemKind::CONSTANT),
                    insert_text: Some(format!("`{}", define.name)),
                    detail: Some("macro".to_string()),
                    label_details: Some(label_details),
                    documentation: Some(documentation),
                    // 用户定义的宏默认最高优先级
                    sort_text: Some("0".to_string()),
                    ..CompletionItem::default()
                });
            }
        }
    }

    completion_items
}

pub fn get_dot_completion(
    server: &LspServer,
    line: &RopeSlice,
    url: &Url,
    pos: &Position,
    language_id: &str
) -> Option<CompletionList> {
    // 判断点模式，v 中，点的模式一共两种
    // 一种是 port & param position 赋值
    // 一种是结构体中的属性访问
    info!("current line: {:?}, pos: {:?}", line, pos);

    if is_port_completion(line, pos) {
        return get_position_port_param_completion(server, line, url, pos, language_id);
    }

    // TODO: 加入结构体的补全

    None
}

fn is_port_completion(line: &RopeSlice, pos: &Position) -> bool {
    let character = pos.character as usize;
    if character == 0 {
        match line.get_char(character) {
            Some(char) => {
                let char_string = char.to_string();
                return char_string == ".";
            },
            None => return false
        }        
    } else {
        let pre_char = line.get_char(character - 1);
        let cur_char = line.get_char(character);
        match pre_char {
            Some(pre_char) => {
                let pre_char = pre_char.to_string();
                if pre_char == "." {
                    return true;
                }
                match cur_char {
                    Some(cur_char) => {
                        let cur_char = cur_char.to_string();
                        return cur_char == ".";
                    },
                    None => {}
                }
            },
            None => {}
        }
    }
    false
}

fn get_position_port_param_completion(
    server: &LspServer,
    #[allow(unused)]
    line: &RopeSlice,
    url: &Url,
    pos: &Position,
    #[allow(unused)]
    language_id: &str
) -> Option<CompletionList> {
    // 判断在不在一个模块内，并获取这个模块
    let hdl_param = &server.srcs.hdl_param;
    let fast_map = 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();
    // info!("enter get_position_port_param_completion, pos: {pos:?}");
    if let Some(hdl_file) = fast_map.get(path_string) {
        // info!("find hdl_file, content: {:?}", hdl_file.fast.content);
        // 在当前文件的 fast 中寻找
        for module in &hdl_file.fast.content {
            for instance in &module.instances {
                if let Some(_) = &instance.instparams {
                    if instance.gen_dot_completion_param_range().contains(pos) {
                        // 补全当前 module 的所有 param
                        if let Some((inst_module, file_type, _)) = hdl_param.find_module_context_by_name(&instance.inst_type) {
                            let mut completion_items = Vec::<CompletionItem>::new();
                            match file_type.as_str() {
                                "primitives" => {
                                    if let Some(primitives_inst) = inst_module.instances.first() {
                                        for param_assignment in &primitives_inst.intstparam_assignments {
                                            let name = param_assignment.parameter.clone().unwrap();
                                            let param_desc = format!("parameter {}", name);

                                            let label_details = CompletionItemLabelDetails {
                                                description: Some("parameter".to_string()),
                                                ..Default::default()
                                            };

                                            let c_item = CompletionItem {
                                                label: name,
                                                detail: Some(param_desc),
                                                label_details: Some(label_details),
                                                kind: Some(CompletionItemKind::TYPE_PARAMETER),
                                                ..CompletionItem::default()
                                            };
                                            completion_items.push(c_item);
                                        }
                                    }
                                }
                                _ => {
                                    for param in inst_module.params {
                                        let label_details = CompletionItemLabelDetails {
                                            description: Some("parameter".to_string()),
                                            ..Default::default()
                                        };

                                        let param_desc = match file_type.as_str() {
                                            "common" => {
                                                param.to_vlog_description()
                                            }
                                            "ip" => {
                                                param.to_vhdl_description()
                                            }
                                            _ => {
                                                param.to_vlog_description()
                                            }
                                        };

                                        let c_item = CompletionItem {
                                            label: param.name,
                                            detail: Some(param_desc),
                                            label_details: Some(label_details),
                                            kind: Some(CompletionItemKind::TYPE_PARAMETER),
                                            ..CompletionItem::default()
                                        };
                                        completion_items.push(c_item);
                                    }
                                }
                            }
                            return Some(CompletionList {
                                is_incomplete: false,
                                items: completion_items
                            });
                        }
                    }
                }

                if instance.instports.is_some() {
                    let port_range = instance.gen_dot_completion_port_range();
                    if port_range.contains(pos) {
                        if let Some((inst_module, file_type, _)) = hdl_param.find_module_context_by_name(&instance.inst_type) {
                            let mut completion_items = Vec::<CompletionItem>::new();
                            match file_type.as_str() {
                                "primitives" => {
                                    if let Some(primitives_inst) = inst_module.instances.first() {
                                        for port_assignment in &primitives_inst.intstport_assignments {
                                            let label_details = CompletionItemLabelDetails {
                                                detail: Some("port".to_string()),
                                                ..CompletionItemLabelDetails::default()
                                            };

                                            let name = port_assignment.port.clone().unwrap();
                                            let port_desc = format!("port {}", name);

                                            let c_item = CompletionItem {
                                                label: name,
                                                detail: Some(port_desc),
                                                label_details: Some(label_details),
                                                kind: Some(CompletionItemKind::PROPERTY),
                                                ..CompletionItem::default()
                                            };
                                            completion_items.push(c_item);
                                        }
                                    }
                                }
                                _ => {
                                    for port in inst_module.ports {
                                        let label_details = CompletionItemLabelDetails {
                                            description: Some("port".to_string()),
                                            ..Default::default()
                                        };

                                        let port_desc = match file_type.as_str() {
                                            "common" => {
                                                port.to_vlog_description()
                                            }
                                            "ip" => {
                                                port.to_vhdl_description()
                                            }
                                            _ => {
                                                port.to_vlog_description()
                                            }
                                        };

                                        let c_item = CompletionItem {
                                            label: port.name,
                                            detail: Some(port_desc),
                                            label_details: Some(label_details),
                                            kind: Some(CompletionItemKind::PROPERTY),
                                            ..CompletionItem::default()
                                        };
                                        completion_items.push(c_item);
                                    }
                                }
                            }
                            return Some(CompletionList {
                                is_incomplete: false,
                                items: completion_items
                            });
                        }
                    }
                }
            }
        }
    }
    None
}
