use std::collections::BTreeMap;
use std::collections::HashMap;

use prost_types::DescriptorProto;
use prost_types::field_descriptor_proto::Type;
use prost_types::FieldDescriptorProto;
use prost_types::OneofDescriptorProto;

use crate::CodeGenerator;
use crate::ident::to_upper_camel;

pub fn tmp_debug(_dbg_msg: &str)
{
    // let file_name = "/Users/Meepo/shared-file-system/rusty-data-root/data/log/prost.1.log.txt";
    // if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).write(true).read(true).open(file_name)
    // {
    //     use std::io::Write;
    //     file.write_all(_dbg_msg.as_bytes());
    // }
}

impl<'a> CodeGenerator<'a>
{
    fn valid_eq_hash_vec_desc(&mut self, field_vec: Vec<FieldDescriptorProto>, current_message_name: &str) -> bool
    {
        let mut result = true;
        for each in field_vec
        {
            let each_type: Type = each.r#type();
            match each_type
            {
                Type::Double | Type::Float =>
                    {
                        {
                            let dbg_msg = format!("field type name: {}, current name: {}\n\n", each.type_name(), current_message_name);
                            tmp_debug(dbg_msg.as_str());
                        }
                        result = false;
                        self.code_ext_info.map_can_eq_hash.insert(current_message_name.to_string(), false);
                    }
                Type::Group | Type::Message =>
                    {
                        {
                            let dbg_msg = format!("field type name: {}, current name: {}\n\n", each.type_name(), current_message_name);
                            tmp_debug(dbg_msg.as_str());
                        }
                        // log::error!("field_full_type_name, result: {}, {}", field_full_type_name, result);
                        if let Some(is_eq_hash) = self.code_ext_info.map_can_eq_hash.get(each.type_name().to_string().as_str()).cloned()
                        {
                            if !is_eq_hash
                            {
                                result = false;
                                self.code_ext_info.map_can_eq_hash.insert(current_message_name.to_string(), false);
                            }
                        }

                        if !self.code_ext_info.is_last
                        {
                            if self.code_ext_info.map_can_eq_hash.get(current_message_name.to_string().as_str()).cloned().is_none()
                            {
                                result = false;
                            }
                        }
                    }
                _ =>
                    {}
            }
        }
        result
    }

    pub fn valid_eq_hash_message(&mut self, message: &DescriptorProto) -> bool
    {
        let current_struct_name = format!(".{}.{}", self.package, message.name());
        if !self.code_ext_info.is_last
        {
            if let Some(is_valid) = self.code_ext_info.map_can_eq_hash.get(current_struct_name.as_str()).cloned()
            {
                return is_valid;
            }
        }
        self.valid_eq_hash_vec_desc(message.field.clone(), current_struct_name.as_str())
    }

    pub fn valid_eq_hash_oneof(&mut self, i_one_of_desc: OneofDescriptorProto, i_field_desc: Vec<(FieldDescriptorProto, usize)>) -> bool
    {
        let current_struct_name = format!(".{}.{}", self.package, to_upper_camel(i_one_of_desc.name()));
        if !self.code_ext_info.is_last
        {
            if let Some(is_valid) = self.code_ext_info.map_can_eq_hash.get(current_struct_name.as_str()).cloned()
            {
                return is_valid;
            }
        }
        let field_desc: Vec<FieldDescriptorProto> = i_field_desc.into_iter().map(|(element, index)|
            {
                element
            }).collect();
        self.valid_eq_hash_vec_desc(field_desc, current_struct_name.as_str())
    }
}


#[derive(Clone)]
pub struct CodeExtGeneratorInfo
{
    pub map_can_eq_hash: HashMap<String, bool>,
    pub is_last: bool,
}

impl CodeExtGeneratorInfo
{
    pub fn default_not_last() -> Self
    {
        Self
        {
            map_can_eq_hash: HashMap::default(),
            is_last: false,
        }
    }

    pub fn default_is_last() -> Self
    {
        Self
        {
            map_can_eq_hash: HashMap::default(),
            is_last: true,
        }
    }

    pub fn loop_generate<TypeOutput, TypeClosure: FnMut(&mut Self) -> std::io::Result<TypeOutput>>(mut i_closure: TypeClosure) -> std::io::Result<TypeOutput>
    {
        let result =
            {
                let mut previous_map_can_eq_hash = Self::default_not_last();
                let mut count: usize = 0;
                loop
                {
                    count += 1;
                    crate::valid_method::tmp_debug(format!("count: {}", count).as_str());
                    let mut current_map_can_eq_hash = previous_map_can_eq_hash.clone();


                    current_map_can_eq_hash.is_last = false;
                    i_closure(&mut current_map_can_eq_hash)?;

                    if current_map_can_eq_hash.map_can_eq_hash.clone().into_iter().collect::<BTreeMap<_, _>>() == previous_map_can_eq_hash.map_can_eq_hash.clone().into_iter().collect::<BTreeMap<_, _>>()
                    {
                        break;
                    }
                    previous_map_can_eq_hash = current_map_can_eq_hash;
                }
                previous_map_can_eq_hash.is_last = true;
                i_closure(&mut previous_map_can_eq_hash)?
            };
        Ok(result)
    }
}
