use crate::records::duplicate_generic_parameter::DuplicateGenericParameter;
use crate::records::type_checker_2::TypeChecker2;
use crate::type_aliases::type_error_data::TypeErrorData;
use luaur_ast::records::ast_array::AstArray;
use luaur_ast::records::ast_generic_type::AstGenericType;
use luaur_ast::records::ast_generic_type_pack::AstGenericTypePack;
use luaur_ast::records::ast_name::AstName;
use luaur_common::records::dense_hash_set::DenseHashSet;

impl TypeChecker2 {
    pub fn visit_generics(
        &mut self,
        generics: AstArray<*mut AstGenericType>,
        generic_packs: AstArray<*mut AstGenericTypePack>,
    ) {
        let mut seen: DenseHashSet<AstName> = DenseHashSet::new(AstName {
            value: core::ptr::null(),
        });

        for generic in generics.iter() {
            let generic = unsafe { &**generic };
            let name = generic.name;

            if seen.contains(&name) {
                let parameter_name = unsafe {
                    core::ffi::CStr::from_ptr(name.value)
                        .to_string_lossy()
                        .into_owned()
                };
                self.report_error_type_error_data_location(
                    TypeErrorData::DuplicateGenericParameter(DuplicateGenericParameter::new(
                        parameter_name,
                    )),
                    &generic.base.location,
                );
            } else {
                seen.insert(name);
            }

            if !generic.default_value.is_null() {
                self.visit_ast_type(generic.default_value);
            }
        }

        for generic_pack in generic_packs.iter() {
            let generic_pack = unsafe { &**generic_pack };
            let name = generic_pack.name;

            if seen.contains(&name) {
                let parameter_name = unsafe {
                    core::ffi::CStr::from_ptr(name.value)
                        .to_string_lossy()
                        .into_owned()
                };
                self.report_error_type_error_data_location(
                    TypeErrorData::DuplicateGenericParameter(DuplicateGenericParameter::new(
                        parameter_name,
                    )),
                    &generic_pack.base.location,
                );
            } else {
                seen.insert(name);
            }

            if !generic_pack.default_value.is_null() {
                self.visit_ast_type_pack(generic_pack.default_value);
            }
        }
    }
}
