use log::info;
use serde::{Deserialize, Serialize};
use regex::Regex;
use ropey::Rope;
use std::process::{Command, Stdio};
use tower_lsp::lsp_types::*;
use crate::server::LspServer;
use super::AbstractLinterConfiguration;

#[derive(Debug, Serialize, Deserialize)]
pub struct VerilatorConfiguration {
    pub language_id: String,
    pub linter: VerilatorLinter,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct VerilatorLinter {
    pub name: String,
    pub exe_name: String,
    /// 目前是否启动
    pub enabled: bool,
    pub path: String,
    pub args: Vec<String>,
}

impl VerilatorLinter {
    fn new(invoker: &str, args: Vec<String>) -> Self {
        Self {
            name: "verilator".to_string(),
            exe_name: invoker.to_string(),
            enabled: false,
            path: invoker.to_string(),
            args,
        }
    }
}


/// convert captured severity string to DiagnosticSeverity
fn verilator_severity(severity: &str) -> Option<DiagnosticSeverity> {
    match severity {
        "Error" => Some(DiagnosticSeverity::ERROR),
        s if s.starts_with("Warning") => Some(DiagnosticSeverity::WARNING),
        // NOTE: afaik, verilator doesn't have an info or hint severity
        _ => Some(DiagnosticSeverity::INFORMATION),
    }
}


impl AbstractLinterConfiguration for VerilatorConfiguration {
    fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
        VerilatorConfiguration {
            language_id: language_id.to_string(),
            linter: VerilatorLinter::new(invoker, args)
        }
    }
    
    fn provide_diagnostics(
        &self,
        uri: &Url,
        #[allow(unused)]
        rope: &Rope,
        server: &LspServer
    ) -> Option<Vec<Diagnostic>> {
        let mut diagnostics = Vec::<Diagnostic>::new();
        
        let invoke_name = self.get_invoke_name();
        let pathbuf = uri.to_file_path().unwrap();
        let path_string = pathbuf.to_str().unwrap();

        let child = Command::new(&invoke_name)
            .stdin(Stdio::piped())
            .stderr(Stdio::piped())
            .stdout(Stdio::piped())
            .args(&self.linter.args)
            .arg(path_string)
            .spawn()
            .ok()?;

        let output = child.wait_with_output().ok()?;
        let output_string = String::from_utf8(output.stderr).ok()?;
        let lint_level = server.srcs.get_lsp_configuration_string_value("digital-ide.function.lsp.linter.linter-level").unwrap_or("error".to_string());

        info!("verilator linter: {:?}, output:\n{}", path_string, output_string);
        
        static REGEX_STORE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
        let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"%(?P<severity>Error|Warning)(-(?P<warning_type>[A-Z0-9_]+))?: (?P<filepath>[^:]+):(?P<line>\d+):((?P<col>\d+):)? ?(?P<message>.*)").unwrap() });        
        // verilator 错误输出样例
        // %Warning-IMPLICIT: library/Apply/Comm/FDE/AGC/AGC.v:23:12: Signal definition not found, creating implicitly: 'r_rms'
        //                                                          : ... Suggested alternative: 'ref_rms'
        //    23 |     assign r_rms     = (reference * reference);
        //       |            ^~~~~
        //                    ... Use "/* verilator lint_off IMPLICIT */" and lint_on around source to disable this message.
        // %Error: Exiting due to 5 warning(s)

        // 先将上面的输出处理为一整块一整块的数组，每块分解为两个部分：第一行（用于解析行号和列号）和后面几行（具体错误地址）
        let mut error_tuples = Vec::<(String, String)>::new();
        let mut current_error_tuple: Option<(&str, Vec<&str>)> = None;
        for error_line in output_string.lines() {
            if error_line.starts_with("%") {
                if let Some((first, ref description)) = current_error_tuple {
                    error_tuples.push((first.to_string(), description.join("\n")));
                }
                current_error_tuple = Some((error_line, Vec::<&str>::new()));
            } else {
                if let Some((_, ref mut description)) = current_error_tuple {
                    description.push(error_line);
                } else {
                    continue;   
                }
            }
        }
        
        for error_tuple in error_tuples {
            // 对于 NOTFOUNDMODULE 进行过滤
            // 如果存在于 fast 中，则直接跳过
            if let Some(module_name) = match_not_module_found(error_tuple.0.as_str()) {
                if server.srcs.contains_module(&module_name) {
                    continue;
                }
            }

            // 使用模板进行解析
            let caps = match regex.captures(error_tuple.0.as_str()) {
                Some(caps) => caps,
                None => continue
            };
            // 因为 verilator 会递归检查，因此报错信息中可能会出现非本文件内的信息
            if caps.name("filepath")?.as_str().replace("\\", "/") != path_string {
                continue;
            }

            let line: u32 = caps.name("line")?.as_str().to_string().parse().ok()?;
            let col: u32 = caps.name("col").map_or("1", |m| m.as_str()).parse().ok()?;
            
            // verilator 的诊断索引都是 one index 的
            let pos = Position::new(line - 1, col - 1);
            let severity = verilator_severity(caps.name("severity")?.as_str());

            let message = match severity {
                Some(DiagnosticSeverity::ERROR) => {
                    caps.name("message")?.as_str().to_string()
                },
                Some(DiagnosticSeverity::WARNING) => {
                    // 如果诊断等级为 error ，也就是只显示错误，那么直接跳过
                    if lint_level == "error" {
                        continue;
                    }
                    format!(
                        "{}: {}",
                        caps.name("warning_type")?.as_str(),
                        caps.name("message")?.as_str()
                    )
                },
                _ => "".to_string(),
            };

            let message = format!("{}\n\n{}", message, error_tuple.1);

            let diagnostic = Diagnostic {
                range: Range::new(pos, pos),
                code: None,
                severity,
                source: Some("Digital IDE: verilator".to_string()),
                message,
                related_information: None,
                tags: None,
                code_description: None,
                data: None
            };

            diagnostics.push(diagnostic);
        }

        Some(diagnostics)
    }

    fn get_linter_path(&self) -> &str {
        &self.linter.path
    }

    fn get_exe_name(&self) -> String {
        if std::env::consts::OS == "windows" {
            format!("{}.exe", self.linter.exe_name)
        } else {
            self.linter.exe_name.to_string()
        }
    }
}


/// %Warning-DECLFILENAME: /home/dide/project/Digital-Test/DIDEtemp/user/sim/FFT/FFT_IFFT_tb.v:82:5: Filename 'FFT_IFFT_tb' does not match NOTFOUNDMODULE name: 'FFT_IFFT'
/// 匹配最后一个 module 的 name
fn match_not_module_found(error_line: &str) -> Option<String> {
    if let Some(start_index) = error_line.find("NOTFOUNDMODULE") {
        let mut strings = "".to_string();
        for char in error_line.chars().skip(start_index).skip_while(|x| x.to_string() != "'") {
            strings.push(char);
        }
        
        info!("strings: {:?}", strings);
        if strings.starts_with("'") && strings.ends_with("'") {
            strings = strings[1 .. strings.len() - 1].to_string();
        }
        return Some(strings);
    }

    None
}