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

#[allow(unused)]
use log::info;
use tower_lsp::lsp_types::*;
use vhdl_lang::{EntHierarchy, Token};
use crate::{server::LspServer, utils::{from_uri_to_escape_path_string, to_escape_path, to_lsp_range, to_symbol_kind}};

pub fn document_symbol(server: &LspServer, params: &DocumentSymbolParams) -> Option<DocumentSymbolResponse> {
    let uri = &params.text_document.uri;
    let path_string = from_uri_to_escape_path_string(uri).unwrap();

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

    let project = server.srcs.vhdl_project.read().ok()?;
    let global_project = project.as_ref().unwrap();
    let path = match PathBuf::from_str(uri.path()) {
        Ok(path) => path,
        Err(error) => {
            info!("error happen in vhdl <hover>: {:?}", error);
            return None;
        }
    };
    let escape_path = to_escape_path(&path);
    let project_file = escape_path.as_path();
    let Some(source) = global_project.project.get_source(project_file) else {
        return None
    };

    // Some files are mapped to multiple libraries, only use the first library for document symbols
    let library_name = global_project.project
        .library_mapping_of(&source)
        .into_iter()
        .next()?;

    fn to_document_symbol(
        EntHierarchy { ent, children }: EntHierarchy,
        ctx: &Vec<Token>,
    ) -> DocumentSymbol {
        // Use the declaration position, if it exists,
        // else the position of the first source range token.
        // The latter is applicable for unnamed elements, e.g., processes or loops.
        let selection_pos = ent.decl_pos().unwrap_or(ent.src_span.start_token.pos(ctx));
        let src_range = ent.src_span.pos(ctx).range();
        #[allow(deprecated)]
        DocumentSymbol {
            name: ent.describe(),
            kind: to_symbol_kind(ent.kind()),
            tags: None,
            detail: None,
            selection_range: to_lsp_range(selection_pos.range),
            range: to_lsp_range(src_range),
            children: if !children.is_empty() {
                Some(
                    children
                        .into_iter()
                        .map(|hierarchy| to_document_symbol(hierarchy, ctx))
                        .collect(),
                )
            } else {
                None
            },
            deprecated: None,
        }
    }

    Some(DocumentSymbolResponse::Nested(
        global_project.project
            .document_symbols(&library_name, &source)
            .into_iter()
            .map(|(hierarchy, tokens)| to_document_symbol(hierarchy, tokens))
            .collect(),
    ))
    // Some(DocumentSymbolResponse::Nested(
    //     scope_tree.as_ref()?.document_symbols(uri, &file.text),
    // ))
}