use crate::core::hdlparam::{self, FastHdlparam};
use ropey::Rope;
use sv_parser::*;
use tower_lsp::lsp_types::*;

// 定义有关 scope 相关的基础类的
pub mod common;
// 定义如何把 ast 转变为类似于 common 中的数据结构
pub mod parse;

use common::*;
use parse::*;

type ScopesAndDefs = Option<(Vec<Box<dyn Scope>>, Vec<Box<dyn Definition>>)>;

/// Take a given syntax node from a sv-parser syntax tree and extract out the definition/scope at
/// that point.
pub fn match_definitions(
    syntax_tree: &SyntaxTree,
    event_iter: &mut EventIter,
    node: RefNode,
    url: &Url,
) -> ScopesAndDefs {
    let mut definitions: Vec<Box<dyn Definition>> = Vec::new();
    let mut scopes: Vec<Box<dyn Scope>> = Vec::new();    
    match node {
        RefNode::ModuleDeclaration(n) => {
            let module = module_dec(syntax_tree, n, event_iter, url);
            if module.is_some() {
                scopes.push(Box::new(module?));
            }
        }
        RefNode::InterfaceDeclaration(n) => {
            let interface = interface_dec(syntax_tree, n, event_iter, url);
            if interface.is_some() {
                scopes.push(Box::new(interface?));
            }
        }
        RefNode::UdpDeclaration(n) => {
            let dec = udp_dec(syntax_tree, n, event_iter, url);
            if dec.is_some() {
                scopes.push(Box::new(dec?));
            }
        }
        RefNode::ProgramDeclaration(n) => {
            let dec = program_dec(syntax_tree, n, event_iter, url);
            if dec.is_some() {
                scopes.push(Box::new(dec?));
            }
        }
        RefNode::PackageDeclaration(n) => {
            let dec = package_dec(syntax_tree, n, event_iter, url);
            if dec.is_some() {
                scopes.push(Box::new(dec?));
            }
        }
        RefNode::ConfigDeclaration(n) => {
            let dec = config_dec(syntax_tree, n, event_iter, url);
            if dec.is_some() {
                scopes.push(Box::new(dec?));
            }
        }
        RefNode::ClassDeclaration(n) => {
            let dec = class_dec(syntax_tree, n, event_iter, url);
            if dec.is_some() {
                scopes.push(Box::new(dec?));
            }
        }
        RefNode::PortDeclaration(n) => {
            let ports = port_dec_non_ansi(syntax_tree, n, event_iter, url);
            if ports.is_some() {
                for port in ports? {
                    definitions.push(Box::new(port));
                }
            }
        }
        RefNode::NetDeclaration(n) => {
            let nets = net_dec(syntax_tree, n, event_iter, url);
            if nets.is_some() {
                for net in nets? {
                    definitions.push(Box::new(net));
                }
            }
        }
        RefNode::DataDeclaration(n) => {
            let vars = data_dec(syntax_tree, n, event_iter, url);
            if let Some(vars) = vars {
                for var in vars {
                    match var {
                        Declaration::Dec(dec) => definitions.push(Box::new(dec)),
                        Declaration::Import(dec) => definitions.push(Box::new(dec)),
                        Declaration::Scope(scope) => scopes.push(Box::new(scope)),
                    }
                }
            }
        }
        RefNode::ParameterDeclaration(n) => {
            let vars = param_dec(syntax_tree, n, event_iter, url);
            if vars.is_some() {
                for var in vars? {
                    definitions.push(Box::new(var));
                }
            }
        }
        RefNode::LocalParameterDeclaration(n) => {
            let vars = localparam_dec(syntax_tree, n, event_iter, url);
            if vars.is_some() {
                for var in vars? {
                    definitions.push(Box::new(var));
                }
            }
        }
        RefNode::FunctionDeclaration(n) => {
            let dec = function_dec(syntax_tree, n, event_iter, url);
            if dec.is_some() {
                scopes.push(Box::new(dec?));
            }
        }
        RefNode::TaskDeclaration(n) => {
            let dec = task_dec(syntax_tree, n, event_iter, url);
            if dec.is_some() {
                scopes.push(Box::new(dec?));
            }
        }
        RefNode::ModportDeclaration(n) => {
            let decs = modport_dec(syntax_tree, n, event_iter, url);
            if decs.is_some() {
                for dec in decs? {
                    definitions.push(Box::new(dec));
                }
            }
        }
        RefNode::ModuleInstantiation(n) => {
            let decs = module_inst(syntax_tree, n, event_iter, url);
            if decs.is_some() {
                for dec in decs? {
                    definitions.push(Box::new(dec));
                }
            }
        }
        RefNode::TextMacroDefinition(n) => {
            let dec = text_macro_def(syntax_tree, n, event_iter, url);            
            if dec.is_some() {
                definitions.push(Box::new(dec?));
            }
        }
        _ => (),
    }

    Some((scopes, definitions))
}

/// convert the syntax tree to a scope tree
/// the root node is the global scope
pub fn get_scopes_from_syntax_tree(syntax_tree: &SyntaxTree, url: &Url) -> Option<GenericScope> {
    let mut scopes: Vec<Box<dyn Scope>> = Vec::new();
    let mut global_scope: GenericScope = GenericScope::new(url);
    global_scope.ident = String::from("global");
    let mut event_iter = syntax_tree.into_iter().event();
    // iterate over each enter event and extract out any scopes or definitions
    // match_definitions is recursively called so we get a tree in the end
    while let Some(event) = event_iter.next() {
        match event {
            NodeEvent::Enter(node) => {
                let mut result = match_definitions(syntax_tree, &mut event_iter, node, url)?;
                global_scope.defs.append(&mut result.1);
                scopes.append(&mut result.0);
            }
            NodeEvent::Leave(_) => (),
        }
    }
    global_scope.scopes.append(&mut scopes);
    Some(global_scope)
}

pub fn get_scopes_from_vhdl_fast(fast: &FastHdlparam, text: &Rope, url: &Url) -> Option<GenericScope> {
    let mut scopes: Vec<Box<dyn Scope>> = Vec::new();
    let mut global_scope: GenericScope = GenericScope::new(url);
    global_scope.ident = String::from("global");

    for module in &fast.content {
        let mut scope: GenericScope = GenericScope::new(url);
        scope.ident = module.name.clone();
        let module_range = module.range.clone();
        scope.start = position_to_byte_idx(text, &module_range.start);
        scope.end = position_to_byte_idx(text, &module_range.end);
        scope.byte_idx = scope.start + 7;

        for parameter in &module.params {
            let mut def = GenericDec::new(url);
            def.ident = parameter.name.clone();
            let parameter_range = parameter.range.clone();
            def.byte_idx = position_to_byte_idx(text, &parameter_range.start);
            def.completion_kind = CompletionItemKind::TYPE_PARAMETER;
            def.symbol_kind = SymbolKind::TYPE_PARAMETER;
            scope.defs.push(Box::new(def));
        }
        for port in &module.ports {
            let mut port_def = PortDec::new(url);
            port_def.ident = port.name.clone();
            let port_range = port.range.clone();
            port_def.byte_idx = position_to_byte_idx(text, &port_range.start);
            port_def.type_str = port.dir_type.clone();
            scope.defs.push(Box::new(port_def));
        }
        for inst in &module.instances {
            let mut instance = ModInst::new(url);
            instance.ident = inst.name.clone();
            let inst_range = inst.range.clone();
            instance.byte_idx = position_to_byte_idx(text, &inst_range.start);
            instance.type_str = inst.inst_type.clone();
            instance.mod_ident = inst.inst_type.clone();

            scope.defs.push(Box::new(instance));
        }
        scopes.push(Box::new(scope));
    }

    global_scope.scopes.append(&mut scopes);
    Some(global_scope)
}

fn position_to_byte_idx(text: &Rope, pos: &hdlparam::Position) -> usize {
    let char = text.line_to_char(pos.line as usize) + pos.character as usize;
    text.char_to_byte(char)
}
