use std::{collections::HashMap, path::PathBuf, str::FromStr};

use crate::{core, server::LspServer};
#[allow(unused)]
use log::info;
use ropey::Rope;
use tower_lsp::lsp_types::*;

use super::{get_language_id_by_path_str, to_escape_path};

pub fn inlay_hint(server: &LspServer, params: &InlayHintParams) -> Option<Vec<InlayHint>> {
    let uri = &params.text_document.uri;
    let path = PathBuf::from_str(uri.path()).unwrap();
    let path = to_escape_path(&path);
    let path_string = path.to_str().unwrap();
    let visible_range = core::hdlparam::Range::from_lsp_range(&params.range);

    // 等待解析完成
    server.srcs.wait_parse_ready(&path_string, false);
    let source = server.srcs.get_source(&path_string)?;
    let source = source.read().ok()?;

    let rope = &source.text;
    // 先找到 当前 所在的 hdlfile
    let path_to_hdl_file = server.srcs.hdl_param.path_to_hdl_file.read().unwrap();
    if let Some(hdl_file) = &path_to_hdl_file.get(path_string) {     
        let fast = &hdl_file.fast;
        let mut hints = Vec::<InlayHint>::new();
        // 制作例化模块的 hint
        for module in &fast.content {            
            for instance in &module.instances {
                // 根据在可见视图外面的 range 就不管了
                if let Some(range) = &instance.instparams {
                    if is_visible_range(&range, &visible_range) {
                        hints.extend(make_instparam_hints(server, params, instance, rope));
                    }
                }
                if let Some(range) = &instance.instports {
                    if is_visible_range(range, &visible_range) {
                        hints.extend(make_instport_hints(server, params, instance, rope));
                    }
                }
            }
            
            // 在 endmodule 后面添加 module xxx
            if is_visible_range(&module.range, &visible_range) {
                hints.extend(make_endmodule_hints(module));
            }
       
        }

 
        return Some(hints);
    }

    None
}

fn is_visible_range(
    target_range: &core::hdlparam::Range,
    visible_range: &core::hdlparam::Range
) -> bool {
    if target_range.before(visible_range) || target_range.after(visible_range) {
        false
    } else {
        true
    }
}

fn make_endmodule_hints(
    module: &core::hdlparam::Module
) -> Vec<InlayHint> {
    let mut hints = Vec::<InlayHint>::new();
    let end_pos = &module.range.end;
    let start_pos = &module.range.start;
    if end_pos.character == start_pos.character && end_pos.line == start_pos.line {
        // 说明解析器没有找到 endmodule
        return hints;
    }

    let module_desc = MarkupContent {
        kind: MarkupKind::Markdown,
        value: format!("module {}", module.name)
    };

    let hint = InlayHint {
        position: end_pos.to_lsp_position(),
        label: InlayHintLabel::String(format!("module {}", module.name)),
        padding_left: Some(true),
        padding_right: Some(true),
        kind: Some(InlayHintKind::PARAMETER),
        text_edits: None,
        tooltip: Some(InlayHintTooltip::MarkupContent(module_desc)),
        data: None
    };

    hints.push(hint);

    hints
}

fn make_instparam_hints(
    server: &LspServer,
    params: &InlayHintParams,
    instance: &core::hdlparam::Instance,
    rope: &Rope
) -> Vec<InlayHint> {
    let mut hints = Vec::<InlayHint>::new();


    hints
}

pub fn position_to_index(rope: &Rope, position: Position) -> usize {
    let line_start = rope.line_to_char(position.line as usize);
    let char_index = line_start + position.character as usize;
    char_index
}

pub fn index_to_position(slice: &Rope, char_index: usize) -> Position {
    let line = slice.char_to_line(char_index);
    let line_start = slice.line_to_char(line);
    let character = char_index - line_start;
    Position {
        line: line as u32,
        character: character as u32,
    }
}

fn find_instport_inlay_hints_position(
    rope: &Rope,
    range: &Range
) -> Option<Position> {
    // let start = rope.pos_to_byte(&range.start);
    // let end = rope.pos_to_byte(&range.end);
    let start_offset = position_to_index(rope, range.start);
    let end_offset = position_to_index(rope, range.end);
    let instport_text = rope.slice(start_offset .. end_offset);
    let instport_text = instport_text.to_string();

    if let Some(offset) = instport_text.find("(") {
        let target_offset = start_offset + offset + 1;
        let target_position = index_to_position(rope, target_offset);
        return Some(target_position);
    }
    
    None
}

fn make_instport_hints(
    server: &LspServer,
    #[allow(unused)]
    params: &InlayHintParams,
    instance: &core::hdlparam::Instance,
    rope: &Rope
) -> Vec<InlayHint> {
    let hdl_param = server.srcs.hdl_param.clone();
    let mut hints = Vec::<InlayHint>::new();
    let module_context = hdl_param.find_module_context_by_name(&instance.inst_type);
    if module_context.is_none() {
        return hints;
    }
    let (define_module, file_type, def_path) = module_context.unwrap();
    // 制作 port name 到 port 的映射
    let mut port_map = HashMap::<String, &core::hdlparam::Port>::new();
    for port in &define_module.ports {
        port_map.insert(port.name.to_string(), port);
    }

    for port_assigment in &instance.intstport_assignments {
        let port_name = port_assigment.port.clone().unwrap_or("".to_string());
        let port = port_map.get(&port_name);
        if port.is_none() {
            continue;
        }
        let port_info = port.unwrap();
        let instport_range = port_assigment.range.to_lsp_range();
        if let Some(hint_position) = find_instport_inlay_hints_position(rope, &instport_range) {

            let label = {
                let dir_type = port_info.dir_type.to_string();

                // vhdl 转成 verilog
                let dir_type = match dir_type.as_str() {
                    "out" => "output",
                    "in" => "input",
                    _ => &dir_type
                };

                if dir_type == "output" {
                    format!("{}", dir_type)
                } else {
                    format!("{}{}", dir_type, " ".repeat(1))
                }
            };

            let language_id = get_language_id_by_path_str(&def_path);

            let port_desc_value = match file_type.as_str() {
                "common" => {
                    format!("```{}\n{}\n```", language_id, port_info.to_vlog_description())
                }
                "ip" => {
                    // TODO: 支持更多的 IP
                    format!("```verilog\n{}\n```", port_info.to_vhdl_description())
                }
                "primitives" => {
                    format!("```{}\n{}\n```", language_id, port_info.to_vlog_description())
                }
                _ => {
                    format!("```{}\n{}\n```", language_id, port_info.to_vlog_description())
                }
            };

            let port_desc = MarkupContent {
                kind: MarkupKind::Markdown,
                value: port_desc_value
            };

            let hint = InlayHint {
                position: hint_position,
                label: InlayHintLabel::String(label),
                padding_left: Some(true),
                padding_right: Some(true),
                kind: Some(InlayHintKind::PARAMETER),
                text_edits: None,
                tooltip: Some(InlayHintTooltip::MarkupContent(port_desc)),
                data: None
            };
            hints.push(hint);
        } 
    }

    hints
}