use std::ptr::addr_of_mut;

use crate::d_strings;
use crate::hu_stuff::get_chat_macro_ptr;
use crate::m_argv::{M_CheckParm, M_GetOptionalArgumentValueByArgument, PARM_NOT_FOUND};

const MAXCHAR: i8 = i8::MAX;
const MAXSHORT: i16 = i16::MAX;
const MAXINT: i32 = i32::MAX;
const MAXLONG: i32 = i32::MAX;

// Minimum values
const MINCHAR: i8 = i8::MIN;
const MINSHORT: i16 = i16::MIN;
const MININT: i32 = i32::MIN;
const MINLONG: i32 = i32::MIN;



// static mut variable stuff

#[allow(non_upper_case_globals)]
static mut mouseSensitivity: i32 = 0;

#[allow(non_upper_case_globals)]
static mut snd_SfxVolumne: i32 = 0;

#[allow(non_upper_case_globals)]
static mut snd_MusicVolume: i32 = 0;

#[allow(non_upper_case_globals)]
static mut showMessages: i32 = 0;

#[allow(non_upper_case_globals)]
static mut usemouse: i32 = 0;

#[allow(non_upper_case_globals)]
static mut mousebfire: i32 = 0;

#[allow(non_upper_case_globals)]
static mut mousebstrafe: i32 = 0;

#[allow(non_upper_case_globals)]
static mut mousebforward: i32 = 0;

#[allow(non_upper_case_globals)]
static mut usejoystick: i32 = 0;

#[allow(non_upper_case_globals)]
static mut joybfire: i32 = 0;

#[allow(non_upper_case_globals)]
static mut joybstrafe: i32 = 0;

#[allow(non_upper_case_globals)]
static mut joybuse: i32 = 0;

#[allow(non_upper_case_globals)]
static mut joybspeed: i32 = 0;

#[allow(non_upper_case_globals)]
static mut screenblocks: i32 = 0;

#[allow(non_upper_case_globals)]
static mut detailLevel: i32 = 0;

#[allow(non_upper_case_globals)]
static mut numChannels: i32 = 0;

#[allow(non_upper_case_globals)]
static mut usegamma: i32 = 0;

/// Represents a type-safe pointer to either a numeric or string global variable 
pub enum DefaultLocation {

    /// Pointer to an integer(i32) value
    Int(*mut i32),

    /// Pointer to a String value
    Str(*mut String)
}

/// Represents a type to represent the extacted default fallback value
pub enum ConfigValue {

    /// Represents an integer(i32) configuration value
    Int(i32),

    /// Represents a String configuration value
    Str(String)
}

/// Represents the name, location and default value of a configuation variable
#[repr(C)]
pub struct default_t {

    /// The key used for the configuration value from the corresponding '.cfg' file
    pub name: &'static str,

    /// The memory location of the global variable to update
    pub location: DefaultLocation,

    /// The fallback value if the setting is missing from the file
    pub default_value: ConfigValue
}


fn get_defaults() -> Vec<default_t> {
    unsafe {
        vec![
            // mouse_sensitivity
            default_t {
                name: "mouse_sensitivity",
                location: DefaultLocation::Int(addr_of_mut!(mouseSensitivity)),
                default_value: ConfigValue::Int(5)
            },
            // snd_SfxVolumne
            default_t {
                name: "sfx_volume",
                location: DefaultLocation::Int(addr_of_mut!(snd_SfxVolumne)),
                default_value: ConfigValue::Int(8)
            },
            // snd_MusicVolume
            default_t {
                name: "snd_MusicVolumne",
                location: DefaultLocation::Int(addr_of_mut!(snd_MusicVolume)),
                default_value: ConfigValue::Int(8)
            },
            // showMessages
            default_t {
                name: "show_messages",
                location: DefaultLocation::Int(addr_of_mut!(showMessages)),
                default_value: ConfigValue::Int(1)
            },
            // usemouse
            default_t {
                name: "use_mouse",
                location: DefaultLocation::Int(addr_of_mut!(usemouse)),
                default_value: ConfigValue::Int(1)
            },
            // mousebfire
            default_t {
                name: "mouseb_fire",
                location: DefaultLocation::Int(addr_of_mut!(mousebfire)),
                default_value: ConfigValue::Int(0)
            },
            // mousebstrafe
            default_t {
                name: "mouseb_strafe",
                location: DefaultLocation::Int(addr_of_mut!(mousebstrafe)),
                default_value: ConfigValue::Int(1)
            },
            // mousebforward
            default_t {
                name: "mouseb_forward",
                location: DefaultLocation::Int(addr_of_mut!(mousebforward)),
                default_value: ConfigValue::Int(2)
            },
            // usejoystick
            default_t {
                name: "use_joystick",
                location: DefaultLocation::Int(addr_of_mut!(usejoystick)),
                default_value: ConfigValue::Int(0)
            },
            // joybfire
            default_t {
                name: "joyb_fire",
                location: DefaultLocation::Int(addr_of_mut!(joybfire)),
                default_value: ConfigValue::Int(0)
            },
            // joybstrafe
            default_t {
                name: "joyb_strafe",
                location: DefaultLocation::Int(addr_of_mut!(joybstrafe)),
                default_value: ConfigValue::Int(1)
            },
            // joybuse
            default_t {
                name: "joyb_use",
                location: DefaultLocation::Int(addr_of_mut!(joybuse)),
                default_value: ConfigValue::Int(3)
            },
            // joybspeed
            default_t {
                name: "joyb_speed",
                location: DefaultLocation::Int(addr_of_mut!(joybspeed)),
                default_value: ConfigValue::Int(2)
            },
            // screenblocks
            default_t {
                name: "screenblocks",
                location: DefaultLocation::Int(addr_of_mut!(screenblocks)),
                default_value: ConfigValue::Int(9)
            },
            // detailLevel
            default_t {
                name: "detailLevel",
                location: DefaultLocation::Int(addr_of_mut!(detailLevel)),
                default_value: ConfigValue::Int(0)
            },
            // numChannels
            default_t {
                name: "snd_channels",
                location: DefaultLocation::Int(addr_of_mut!(numChannels)),
                default_value: ConfigValue::Int(3)
            },
            // usegamma
            default_t {
                name: "usegamma",
                location: DefaultLocation::Int(addr_of_mut!(usegamma)),
                default_value: ConfigValue::Int(0)
            },
            // chatmacro0
            default_t {
                name: "chatmacro0",
                location: DefaultLocation::Str(get_chat_macro_ptr(0)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO0.to_string())
            },
            // chatmacro1
            default_t {
                name: "chatmacro1",
                location: DefaultLocation::Str(get_chat_macro_ptr(1)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO1.to_string())
            },
            // chatmacro2
            default_t {
                name: "chatmacro2",
                location: DefaultLocation::Str(get_chat_macro_ptr(2)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO2.to_string())
            },
            // chatmacro3
            default_t {
                name: "chatmacro3",
                location: DefaultLocation::Str(get_chat_macro_ptr(3)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO3.to_string())
            },
            // chatmacro4
            default_t {
                name: "chatmacro4",
                location: DefaultLocation::Str(get_chat_macro_ptr(4)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO4.to_string())
            },
            // chatmacro5
            default_t {
                name: "chatmacro5",
                location: DefaultLocation::Str(get_chat_macro_ptr(5)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO5.to_string())
            },
            // chatmacro6
            default_t {
                name: "chatmacro6",
                location: DefaultLocation::Str(get_chat_macro_ptr(6)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO6.to_string())
            },
            // chatmacro7
            default_t {
                name: "chatmacro7",
                location: DefaultLocation::Str(get_chat_macro_ptr(7)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO7.to_string())
            },
            // chatmacro8
            default_t {
                name: "chatmacro8",
                location: DefaultLocation::Str(get_chat_macro_ptr(8)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO8.to_string())
            },
            // chatmacro9
            default_t {
                name: "chatmacro9",
                location: DefaultLocation::Str(get_chat_macro_ptr(9)),
                default_value: ConfigValue::Str(d_strings::HUSTR_CHATMACRO9.to_string())
            }            
        ]
    }
}

#[allow(non_snake_case)]
pub fn M_WriteFile(name: &str, source: std::fs::File, length: i64) {

}

#[allow(non_snake_case)]

pub fn M_ReadFile(name: &str, buffer: Vec<u8>) {

}

#[allow(non_snake_case)]
pub fn M_ScreenShot() {

}

#[allow(non_snake_case)]
pub fn M_LoadDefaults() {
    // Set to defaults
    let mut defaults = get_defaults();
    for item in &defaults {
        unsafe {
            match &item.location {
                DefaultLocation::Int(ptr) => {
                    if let ConfigValue::Int(val) = item.default_value {
                        **ptr = val;
                    }
                }
                DefaultLocation::Str(ptr) => {
                    if let ConfigValue::Str(val) = &item.default_value {
                        **ptr = val.clone();
                    }
                }
            }
        }
    } // end iter

    // load custom cfg?
    let mut cfg_location = "DEFAULT.CFG";
    let p = M_CheckParm("-config");
    if p != PARM_NOT_FOUND {
        cfg_location = M_GetOptionalArgumentValueByArgument("-config").unwrap();
    }

    // read contents
    if let Ok(cfg_content) = std::fs::read_to_string(cfg_location) {
        for line in cfg_content.lines() {
            let cfg_line = line.trim();
            if cfg_line.is_empty() {
                continue;
            }
            // split the non-empty cfg line into two
            if let Some((parsed_cfg_key, parsed_cfg_value)) = cfg_line.split_once(char::is_whitespace) {
                let key = parsed_cfg_key.trim();
                let value = parsed_cfg_value.trim();

                if let Some(cfg_item) = defaults.iter_mut().find(|d| d.name == key) {
                    unsafe {
                        match &cfg_item.location {
                            DefaultLocation::Int(ptr) => {
                                if value.starts_with("0x") {
                                    if let Ok(num) = i32::from_str_radix(value, 16) {
                                        **ptr = num;
                                    }
                                    else {
                                        println!("Unable to parse value '{}' as an integer for configuation setting '{}'", value, key);
                                        println!("Using defaults");
                                    }
                                }
                                else if let Ok(num) = i32::from_str_radix(value, 10) {
                                     **ptr = num;
                                }
                                else {
                                    println!("Unable to parse value '{}' as an integer for configuation setting '{}'", value, key);
                                    println!("Using defaults");
                                }
                            }
                            DefaultLocation::Str(ptr) => {
                                let cleaned_str = value.trim_matches('"').to_string();
                                **ptr = cleaned_str;
                            }
                        }
                    }
                }
            }
        }
    } else {
        println!("Unable to read configuation file '{}'", &cfg_location);
        println!("Using defaults");
    }

}

#[allow(non_snake_case)]
pub fn M_SaveDefaults() {
    
}

#[allow(non_snake_case)]
pub fn M_DrawText(x: i32, y: i32, direct: bool, text: &str) {

}