use crate::utils::get_definition_token;
use crate::server::LSPServer;
use crate::sources::LSPSupport;

#[allow(unused)]
use log::info;
use tower_lsp::lsp_types::*;

use super::{feature::*, Definition, Scope};

pub fn goto_definition(server: &LSPServer, params: &GotoDefinitionParams) -> Option<GotoDefinitionResponse> {
    let doc = &params.text_document_position_params.text_document.uri;
    let pos = params.text_document_position_params.position;
    let file_id = server.srcs.get_id(doc).to_owned();
    server.srcs.wait_parse_ready(file_id, false);
    let file = server.srcs.get_file(file_id)?;
    let file = file.read().ok()?;

    let line_text = file.text.line(pos.line as usize);
    let token: String = get_definition_token(&line_text, pos);

    // match include
    if let Some(definition) = goto_include_definition(doc, &line_text, pos) {
        return Some(definition);
    }

    // match macro
    if let Some(definition) = goto_macro_definition(server, &line_text, pos) {
        return Some(definition);
    }

    // match instance
    let scope_tree = server.srcs.scope_tree.read().ok()?;

    // match position port & param
    if let Some(definition) = goto_position_port_param_definition(server, &line_text, doc, pos) {
        return Some(definition);
    }

    // match module name
    if let Some(definition) = goto_module_declaration_definition(server, &token) {
        return Some(definition);
    }

    let byte_idx = file.text.pos_to_byte(&pos);
    let global_scope = scope_tree.as_ref()?;
    let def = global_scope.get_definition(&token, byte_idx, doc)?;
    
    let def_pos = file.text.byte_to_pos(def.byte_idx());
    Some(GotoDefinitionResponse::Scalar(Location::new(
        def.url(),
        Range::new(def_pos, def_pos),
    )))
}

