use encoding_rs_io::DecodeReaderBytesBuilder;

use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

use crate::parser::refs::{DF_ENCODING, RAW_TOKEN_RE};

use super::raws::RawObjectKind;

impl super::DFParser {
    /// It reads a file, line by line, and checks the first line for the filename, reads lines until it encounters the
    /// [OBJECT:<type>] tag in the file.
    ///
    /// Arguments:
    ///
    /// * `input_path`: Path to the file to be read
    /// * `module_info`: Information about the raw module `input_path` is within
    ///
    /// Returns:
    ///
    /// `RawObjectKind` for the type of [OBJECT] tag encountered, and `RawObjectKind::None` if it is unsupported.
    pub fn read_raw_file_type<P: AsRef<Path>>(input_path: &P) -> RawObjectKind {
        let caller = "Raw File Type Checker";
        // Validate file exists
        if !input_path.as_ref().exists() {
            log::error!(
                "{} - Path doesn't exist {}",
                caller,
                input_path.as_ref().display()
            );
            return RawObjectKind::None;
        }
        if !input_path.as_ref().is_file() {
            log::error!(
                "{} - Path does not point to a file {}",
                caller,
                input_path.as_ref().display(),
            );
            return RawObjectKind::None;
        }

        // Open the file
        let Ok(file) = File::open(input_path) else {
        log::error!("{} - Unable to open file {}",caller, input_path.as_ref().display());
        return RawObjectKind::None;
    };

        // Setup a file reader for the encoding used by DF
        let decoding_reader = DecodeReaderBytesBuilder::new()
            .encoding(Some(*DF_ENCODING))
            .build(file);
        let reader = BufReader::new(decoding_reader);

        // String to store the parsed filename in
        let mut raw_filename = String::new();

        // Read in lines until we encounter the [OBJECT tag] or complete the file.
        for (index, line) in reader.lines().enumerate() {
            if line.is_err() {
                log::error!(
                    "{} - Error processing {}:{}",
                    caller,
                    input_path.as_ref().display(),
                    index
                );
                continue;
            }
            // Match the line so we can check the pieces of it
            let line = match line {
                Ok(l) => l,
                Err(e) => {
                    log::error!("Line-reading error\n{:?}", e);
                    continue;
                }
            };
            // The filename is always the top line of a DF raw file
            if index == 0 {
                raw_filename = String::from(&line);
                continue;
            }
            // Multiple matches can occur in a single line, so we loop over all captures within the match
            // for this line.
            for cap in RAW_TOKEN_RE.captures_iter(&line) {
                log::trace!("{} - Key: {} Value: {}", caller, &cap[2], &cap[3]);
                // Match the front part of the tag
                match &cap[2] {
                    // We are only concerned with the [OBJECT] key
                    "OBJECT" => {
                        log::trace!("{} - {} is a {} raw file", caller, raw_filename, &cap[3]);
                        match &cap[3] {
                            "CREATURE" => {
                                return RawObjectKind::Creature;
                            }
                            "PLANT" => {
                                return RawObjectKind::Plant;
                            }
                            "INORGANIC" => {
                                return RawObjectKind::Inorganic;
                            }
                            "GRAPHICS" => {
                                return RawObjectKind::Graphics;
                            }
                            "TILE_PAGE" => {
                                return RawObjectKind::GraphicsTilePage;
                            }
                            // Currently, any other type of raw object is unsupported
                            &_ => {
                                log::debug!(
                                    "{} - Currently no support for OBJECT:{}",
                                    caller,
                                    &cap[3]
                                );
                                return RawObjectKind::None;
                                // current_object = RawObjectKind::None;
                            }
                        }
                    }
                    &_ => (),
                }
            }
        }

        // Reading through the entire file and not finding an [OBJECT] tag means the raw file is invalid
        log::warn!(
            "{} - no [OBJECT] tag in {}",
            caller,
            input_path.as_ref().display()
        );
        RawObjectKind::None
    }
}
