use std::{env::consts::OS, path::{Path, PathBuf}, str::FromStr};

use log::info;
use percent_encoding::percent_decode_str;
use regex::Regex;
use ropey::RopeSlice;
use tower_lsp::lsp_types::*;
use vhdl_lang::{ast::ObjectClass, AnyEntKind, Concurrent, Object, Overloaded, SrcPos, Type};

pub mod command;
pub mod fast;
pub mod file;
pub use file::*;

/// 根据 pos 获取到当前光标所在的字符串，相当于 getWordRangeAtPosition
pub fn get_definition_token(line: &RopeSlice, pos: Position) -> String {
    let mut token = String::new();
    let mut line_iter = line.chars();
    for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) {
        line_iter.next();
    }
    let mut c = line_iter.prev();
    while c.is_some() && (c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
        token.push(c.unwrap());
        c = line_iter.prev();
    }
    token = token.chars().rev().collect();
    line_iter = line.chars();
    for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) {
        line_iter.next();
    }
    let mut c = line_iter.next();
    while c.is_some() && (c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
        token.push(c.unwrap());
        c = line_iter.next();
    }
    token
}

pub fn get_word_range_at_position(line: &RopeSlice, pos: Position, regex: Regex) -> Option<(String, Range)> {
    let mut line_iter = line.chars();

    // 找到 character 这个位置
    for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) {
        line_iter.next();
    }
    let mut c = line_iter.prev();

    let mut token = String::new();
    let mut start_character = pos.character;
    // 往前找
    while c.is_some() && (regex.is_match(&c.unwrap().to_string())) {
        token.push(c.unwrap());
        start_character -= 1;
        c = line_iter.prev();
    }
    token = token.chars().rev().collect();
    line_iter = line.chars();

    for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) {
        line_iter.next();
    }
    let mut c = line_iter.next();
    let mut end_character = pos.character;
    // 往后找
    while c.is_some() && (regex.is_match(&c.unwrap().to_string())) {
        token.push(c.unwrap());
        end_character += 1;
        c = line_iter.next();
    }
    
    if token.len() == 0 {
        return None;
    }

    let range = Range::new(
        Position { line: pos.line, character: start_character },
        Position { line: pos.line, character: end_character }
    );

    Some((token, range))
}

/// 根据 uri 获取 hdl 的 language id
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
#[allow(unused)]
pub fn get_language_id_by_uri(uri: &Url) -> String {
    let path = uri.path();
    let ext_name = std::path::Path::new(path)
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .unwrap_or("");

    get_language_id_by_extname(ext_name)
}

/// 根据路径字符串获取 hdl 的 language id
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
#[allow(unused)]
pub fn get_language_id_by_pathbuf(pathbuf: &PathBuf) -> String {
    let ext_name = pathbuf.as_path()
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .unwrap_or("");
    get_language_id_by_extname(ext_name)
}

/// 根据路径字符串获取 hdl 的 language id
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
#[allow(unused)]
pub fn get_language_id_by_path_str(path_str: &str) -> String {
    let ext_name = std::path::Path::new(path_str)
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .unwrap_or("");
    get_language_id_by_extname(ext_name)
}

fn get_language_id_by_extname(ext_name: &str) -> String {
    match ext_name {
        "vhd" | "vhdl" | "vho" | "vht" => "vhdl".to_string(),
        "v" | "V" | "vh" | "vl" => "verilog".to_string(),
        "sv" | "svh" => "systemverilog".to_string(),
        _ => "plaintext".to_string()
    }
} 

/// 根据基础路径和给出的 path 路径，计算出绝对路径
/// 
/// 如果 path 是绝对路径直接返回
/// 会进行存在性检查，不存在的路径直接返回 None
/// 
/// 例子：
/// 
/// resolve_path("/home/ubuntu/hello.v", "./control.v") -> Some("/home/ubuntu/control.v")
/// 
/// resolve_path("/home/ubuntu/hello.v", "/opt/control.v") -> Some("/opt/control.v")
/// 
pub fn resolve_path(base: &str, path_string: &str) -> Option<PathBuf> {
    let path = Path::new(path_string);
    let abs_path: PathBuf;

    if path.is_absolute() {
        abs_path = PathBuf::from(path);
    } else {
        let base = Path::new(base).parent()?;
        abs_path = base.join(path);
    }
    if abs_path.exists() {
        let path = abs_path.as_path().to_owned();
        return Some(path);
    }
    None
}

/// 把 uri 路径转换成合法的操作系统文件路径
/// 
/// windows 中 
/// 
/// /c%3A/Users/MyCpu.v 转换成 c:/Users/MyCpu.v
pub fn to_escape_path(path: &PathBuf) -> PathBuf {
    let decoded_path = percent_decode_str(path.to_str().unwrap()).decode_utf8_lossy();
    let decoded_path_str = decoded_path.as_ref();
    match OS {
        "windows" => {
            // 去掉开头的斜杠
            let trimmed_path_str = decoded_path_str.trim_start_matches('/');
            PathBuf::from(trimmed_path_str)
        },
        _ => {
            // 其他操作系统（如 Linux）保持原样
            PathBuf::from(decoded_path_str)
        },
    }
}

/// 将 uri 转换为 digital lsp 内部使用的路径字符串
/// 比如 hdlparam 里面有一些以 String 作为主键的 hashmap
/// 它们的 String 如果代表路径，那么都是通过该函数从 uri 转换而来的
pub fn from_uri_to_escape_path_string(
    uri: &Url
) -> Option<String> {
    let path = match PathBuf::from_str(uri.path()) {
        Ok(path) => path,
        Err(error) => {
            info!("PathBuf::from_str(uri.path()) 发生错误 {:?}", error);
            return None;
        }
    };
    let escape_path = to_escape_path(&path);
    let escape_path_string = escape_path.to_str().unwrap_or("");
    if escape_path_string.len() == 0 {
        info!("escape_path_string 为空");
        return None;
    }

    Some(escape_path_string.to_string())
}

pub fn to_lsp_pos(position: vhdl_lang::Position) -> Position {
    Position {
        line: position.line,
        character: position.character,
    }
}

pub fn to_lsp_range(range: vhdl_lang::Range) -> Range {
    Range {
        start: to_lsp_pos(range.start),
        end: to_lsp_pos(range.end),
    }
}

pub fn to_symbol_kind(kind: &AnyEntKind) -> SymbolKind {
    match kind {
        AnyEntKind::ExternalAlias { class, .. } => object_class_kind(ObjectClass::from(*class)),
        AnyEntKind::ObjectAlias { base_object, .. } => object_kind(base_object.object()),
        AnyEntKind::Object(o) => object_kind(o),
        AnyEntKind::LoopParameter(_) => SymbolKind::CONSTANT,
        AnyEntKind::PhysicalLiteral(_) => SymbolKind::CONSTANT,
        AnyEntKind::DeferredConstant(_) => SymbolKind::CONSTANT,
        AnyEntKind::File { .. } => SymbolKind::FILE,
        AnyEntKind::InterfaceFile { .. } => SymbolKind::INTERFACE,
        AnyEntKind::Component(_) => SymbolKind::CLASS,
        AnyEntKind::Attribute(_) => SymbolKind::PROPERTY,
        AnyEntKind::Overloaded(o) => overloaded_kind(o),
        AnyEntKind::Type(t) => type_kind(t),
        AnyEntKind::ElementDeclaration(_) => SymbolKind::FIELD,
        AnyEntKind::Sequential(_) => SymbolKind::NAMESPACE,
        AnyEntKind::Concurrent(Some(Concurrent::Instance)) => SymbolKind::MODULE,
        AnyEntKind::Concurrent(_) => SymbolKind::NAMESPACE,
        AnyEntKind::Library => SymbolKind::NAMESPACE,
        AnyEntKind::View(_) => SymbolKind::INTERFACE,
        AnyEntKind::Design(d) => match d {
            vhdl_lang::Design::Entity(_, _) => SymbolKind::MODULE,
            vhdl_lang::Design::Architecture(..) => SymbolKind::MODULE,
            vhdl_lang::Design::Configuration => SymbolKind::MODULE,
            vhdl_lang::Design::Package(_, _) => SymbolKind::PACKAGE,
            vhdl_lang::Design::PackageBody(..) => SymbolKind::PACKAGE,
            vhdl_lang::Design::UninstPackage(_, _) => SymbolKind::PACKAGE,
            vhdl_lang::Design::PackageInstance(_) => SymbolKind::PACKAGE,
            vhdl_lang::Design::InterfacePackageInstance(_) => SymbolKind::PACKAGE,
            vhdl_lang::Design::Context(_) => SymbolKind::NAMESPACE,
        },
    }
}

pub fn type_kind(t: &Type) -> SymbolKind {
    match t {
        vhdl_lang::Type::Array { .. } => SymbolKind::ARRAY,
        vhdl_lang::Type::Enum(_) => SymbolKind::ENUM,
        vhdl_lang::Type::Integer => SymbolKind::NUMBER,
        vhdl_lang::Type::Real => SymbolKind::NUMBER,
        vhdl_lang::Type::Physical => SymbolKind::NUMBER,
        vhdl_lang::Type::Access(_) => SymbolKind::ENUM,
        vhdl_lang::Type::Record(_) => SymbolKind::STRUCT,
        vhdl_lang::Type::Incomplete => SymbolKind::NULL,
        vhdl_lang::Type::Subtype(t) => type_kind(t.type_mark().kind()),
        vhdl_lang::Type::Protected(_, _) => SymbolKind::CLASS,
        vhdl_lang::Type::File => SymbolKind::FILE,
        vhdl_lang::Type::Interface => SymbolKind::TYPE_PARAMETER,
        vhdl_lang::Type::Alias(t) => type_kind(t.kind()),
        vhdl_lang::Type::Universal(_) => SymbolKind::NUMBER,
    }
}

pub fn overloaded_kind(overloaded: &Overloaded) -> SymbolKind {
    match overloaded {
        Overloaded::SubprogramDecl(_) => SymbolKind::FUNCTION,
        Overloaded::Subprogram(_) => SymbolKind::FUNCTION,
        Overloaded::UninstSubprogramDecl(..) => SymbolKind::FUNCTION,
        Overloaded::UninstSubprogram(..) => SymbolKind::FUNCTION,
        Overloaded::InterfaceSubprogram(_) => SymbolKind::FUNCTION,
        Overloaded::EnumLiteral(_) => SymbolKind::ENUM_MEMBER,
        Overloaded::Alias(o) => overloaded_kind(o.kind()),
    }
}

pub fn object_kind(object: &Object) -> SymbolKind {
    if matches!(object.subtype.type_mark().kind(), Type::Protected(..)) {
        SymbolKind::OBJECT
    } else if object.iface.is_some() {
        SymbolKind::INTERFACE
    } else {
        object_class_kind(object.class)
    }
}

pub fn object_class_kind(class: ObjectClass) -> SymbolKind {
	match class {
        ObjectClass::Signal => SymbolKind::EVENT,
        ObjectClass::Constant => SymbolKind::CONSTANT,
        ObjectClass::Variable => SymbolKind::VARIABLE,
        ObjectClass::SharedVariable => SymbolKind::VARIABLE,
    }
}

pub fn from_lsp_pos(position: Position) -> vhdl_lang::Position {
    vhdl_lang::Position {
        line: position.line,
        character: position.character,
    }
}

pub fn from_lsp_range(range: Range) -> vhdl_lang::Range {
    vhdl_lang::Range {
        start: from_lsp_pos(range.start),
        end: from_lsp_pos(range.end),
    }
}

pub fn srcpos_to_location(pos: &SrcPos) -> Location {
    let uri = Url::from_file_path(pos.source.file_name()).unwrap();
    Location {
        uri,
        range: to_lsp_range(pos.range()),
    }
}


/// 用于判断括号匹配的数据结构
pub struct BracketMatcher {
    stack: Vec<char>
}

pub enum BracketMatchResult {
    Valid,
    Invalid,
    Complete
}

impl BracketMatcher {
    pub fn new() -> Self {
        BracketMatcher { stack: Vec::<char>::new() }
    }

    /// 匹配一段字符串内的括号，如果匹配不成功，返回 false
    pub fn consume_string(&mut self, text: &str) -> BracketMatchResult {
        for ch in text.chars() {
            match ch {
                '(' => {
                    self.stack.push(ch);
                }
                ')' => {
                    let top = self.stack.pop();
                    if top.is_none() {
                        return BracketMatchResult::Invalid;
                    } else {
                        let top = top.unwrap();
                        if top != '(' {
                            return BracketMatchResult::Invalid;
                        }
                    }
                    if self.stack.len() == 0 {
                        return BracketMatchResult::Complete;
                    }
                }
                _ => {}
            }
        }

        BracketMatchResult::Valid
    }
}

/// 基于字符顺序判断是否匹配
/// 检查是否可以从 `candidate` 中提取出与 `input` 顺序一致的字符
/// input 忽略大小写
pub fn is_character_ordered_match(
    input: &str,
    candidate: &str
) -> bool {
    let mut input_chars = input.chars().peekable();
    let mut candidate_chars = candidate.chars().peekable();

    while let Some(input_char) = input_chars.next() {
        // 在 candidate 中找到与 input_char 匹配的字符
        let mut matched = false;
        while let Some(candidate_char) = candidate_chars.next() {
            if candidate_char.to_lowercase().eq(input_char.to_lowercase()) {
                matched = true;
                break;
            }
        }

        // 如果未找到匹配的字符，返回 false
        if !matched {
            return false;
        }
    }

    // 如果 input 的所有字符都匹配完成，返回 true
    true
}
