use std::{path::PathBuf, str::FromStr};
#[allow(unused)]
use log::info;
use serde::{Deserialize, Serialize};
use ropey::Rope;
use tower_lsp::lsp_types::{Diagnostic, Url};

use crate::{server::LspServer, utils::command::is_command_valid};

#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct LinterStatus {
    pub tool_name: String,
    pub available: bool,
    pub invoke_name: String
}


impl Default for LinterStatus {
    fn default() -> Self {
        LinterStatus {
            tool_name: "".to_string(),
            available: false,
            invoke_name: "".to_string()
        }
    }
}

pub trait AbstractLinterConfiguration {
    /// - language_id: 语言 id
    /// - invoker: 调用可执行文件的名字，比如 `iverilog`，`xvlog`，`verible-verilog-syntax`
    /// - args: 调用执行器进行诊断的命令行参数（不包含文件路径）
    fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self;
    
    /// 提供一次诊断目前基于 uri 中的地址读取真实路径来获取诊断结果
    fn provide_diagnostics(&self, uri: &Url, rope: &Rope, server: &LspServer) -> Option<Vec<Diagnostic>>;

    /// 获取 linter 安装路径，内部实现，内部调用
    fn get_linter_path(&self) -> &str;

    /// 获取 可执行文件 的名字，比如
    /// - iverilog.exe
    /// - iverilog
    /// - xvlog.bat
    fn get_exe_name(&self) -> String;

    /// 获取真实调用路径，比如
    /// iverilog.exe 或者 /path/to/verilog.exe
    fn get_invoke_name(&self) -> String {
        let exe_name = self.get_exe_name();

        let linter_path = self.get_linter_path();
        let pathbuf = PathBuf::from_str(linter_path);
        if linter_path.len() == 0 || !pathbuf.as_ref().unwrap().exists() {
            exe_name.to_string()
        } else {
            // 路径存在
            let pathbuf = pathbuf.unwrap();
            let pathbuf = pathbuf.join(exe_name);
            let path_string = pathbuf.to_str().unwrap();
            path_string.to_string()
        }
    }
    
    /// 获取与该工具匹配的诊断器名字与调用函数名。并检查它们是否有效
    /// 
    /// 参考链接：https://kirigaya.cn/blog/article?seq=284
    fn linter_status(
        &self,
        server: &LspServer
    ) -> LinterStatus {
        let invoke_name = self.get_invoke_name();
        let available = is_command_valid(&invoke_name, server);
        LinterStatus {
            tool_name: self.get_exe_name(),
            available,
            invoke_name
        }
    }
}

/// rope 的第 line_index 行文字中，第一个和最后一个非空字符在本行的索引
pub fn find_non_whitespace_indices(rope: &Rope, line_index: usize) -> Option<(usize, usize)> {
    let line_text = rope.line(line_index);
    let mut first_non_whitespace_index = None;
    let mut last_non_whitespace_index = None;
    for (i, c) in line_text.chars().enumerate() {
        if !c.is_whitespace() {
            if first_non_whitespace_index.is_none() {
                first_non_whitespace_index = Some(i);
            }
            last_non_whitespace_index = Some(i);
        }
    }
    match (first_non_whitespace_index, last_non_whitespace_index) {
        (Some(first), Some(last)) => Some((first, last)),
        _ => None,
    }
}