wdext 0.1.0

A DbgEng wrapper framework
// SPDX-FileCopyrightText: 2026 takubokudori
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{
    data::{VarType, Variant},
    dbgeng::WinResult,
    dbgmodel::{
        DebugHostContext, DebugHostType, KeyEnumerator, KeyStore,
        RawEnumerator, WdModelObjectKind, concept::StringDisplayableConcept,
    },
    wd::dbgmodel::WdSymbolKind,
    *,
};
use windows::Win32::System::Diagnostics::Debug::Extensions::IModelObject;

impl_debug_interface!(ModelObject, ModelObjectRef, IModelObject);

impl ModelObject {
    pub fn get_context(&self) -> WinResult<DebugHostContext> {
        unsafe { Ok(self.0.GetContext()?.into()) }
    }

    pub fn get_kind(&self) -> WinResult<WdModelObjectKind> {
        unsafe { Ok(self.0.GetKind()?.0.try_into()?) }
    }

    pub fn get_intrinsic_value(&self) -> WinResult<Variant> {
        unsafe { Ok(self.0.GetIntrinsicValue()?.into()) }
    }

    pub fn get_intrinsic_value_as(&self, vt: VarType) -> WinResult<Variant> {
        unsafe { Ok(self.0.GetIntrinsicValueAs(vt.into())?.into()) }
    }

    pub fn get_key_value(
        &self,
        key: impl AsRef<WStr>,
    ) -> DbgModelResult<(ModelObject, Option<KeyStore>)> {
        let mut object = None;
        let mut metadata = None;
        unsafe {
            dmerr!(
                object,
                self.0.GetKeyValue(
                    pcw!(key),
                    Some(&mut object),
                    Some(&mut metadata)
                )
            );

            Ok((object.unwrap().into(), metadata.map(|x| x.into())))
        }
    }

    pub fn set_key_value(
        &self,
        key: impl AsRef<WStr>,
        object: Option<&ModelObject>,
    ) -> WinResult<()> {
        let ptr = object.map_or(std::ptr::null_mut(), |x| x.as_raw().as_raw());
        unsafe {
            vcall!(self, SetKeyValue, pcw!(key), ptr).ok()?;
        }

        Ok(())
    }

    pub fn enumerate_key_values(&self) -> WinResult<KeyEnumerator> {
        unsafe { Ok(self.0.EnumerateKeyValues()?.into()) }
    }

    pub fn get_raw_value(
        &self,
        kind: WdSymbolKind,
        name: impl AsRef<WStr>,
        search_flags: RawSearchFlags,
    ) -> DbgModelResult<ModelObject> {
        unsafe {
            let mut object = std::ptr::null_mut();

            let object = dmerr!(@raw object,
                vcall!(
                self,
                GetRawValue,
                kind.into(),
                pcw!(name),
                search_flags.0 as u32,
                &mut object
            ));

            Ok(object)
        }
    }

    pub fn enumerate_raw_values(
        &self,
        kind: WdSymbolKind,
        search_flags: RawSearchFlags,
    ) -> WinResult<RawEnumerator> {
        unsafe {
            Ok(self
                .0
                .EnumerateRawValues(kind.into(), search_flags.0 as u32)?
                .into())
        }
    }

    pub fn dereference(&self) -> WinResult<ModelObject> {
        unsafe { Ok(self.0.Dereference()?.into()) }
    }

    pub fn try_cast_to_runtime_type(&self) -> DbgModelResult<ModelObject> {
        unsafe {
            let mut runtime_typed_object = std::ptr::null_mut();
            let runtime_typed_object = dmerr!(@raw runtime_typed_object,
                vcall!(self,TryCastToRuntimeType, &mut runtime_typed_object)
            );
            Ok(runtime_typed_object)
        }
    }

    pub fn get_concept<T: Interface>(&self) -> WinResult<T> {
        let mut concept_interface = None;
        let mut concept_metadata = None;
        unsafe {
            self.0.GetConcept(
                &T::IID,
                &mut concept_interface,
                Some(&mut concept_metadata),
            )?;
            concept_interface.unwrap().cast::<T>()
        }
    }

    pub fn get_location(&self) -> WinResult<Location> {
        unsafe { self.0.GetLocation() }
    }

    pub fn get_type_info(&self) -> WinResult<DebugHostType> {
        unsafe { Ok(self.0.GetTypeInfo()?.into()) }
    }

    pub fn get_target_info(&self) -> WinResult<(Location, DebugHostType)> {
        let mut location = Location::default();
        let mut r#type = None;
        unsafe {
            self.0.GetTargetInfo(&mut location, &mut r#type)?;
        }
        Ok((location, r#type.unwrap().into()))
    }

    pub fn get_number_of_parent_models(&self) -> WinResult<u64> {
        unsafe { self.0.GetNumberOfParentModels() }
    }

    /// Returns `(model, contextObject)`.
    pub fn get_parent_model(
        &self,
        i: u64,
    ) -> WinResult<(ModelObject, Option<ModelObject>)> {
        let mut object = None;
        let mut context_object = None;
        unsafe {
            self.0.GetParentModel(i, &mut object, &mut context_object)?;
            Ok((object.unwrap().into(), context_object.map(|x| x.into())))
        }
    }

    pub fn add_parent_model(
        &self,
        model: &ModelObject,
        context_object: Option<&ModelObject>,
        r#override: bool,
    ) -> WinResult<()> {
        unsafe {
            self.0.AddParentModel(
                model.as_raw(),
                context_object.map(|x| x.as_raw()),
                r#override as u8,
            )
        }
    }

    pub fn remove_parent_model(&self, model: &ModelObject) -> WinResult<()> {
        unsafe { self.0.RemoveParentModel(model.as_raw()) }
    }

    pub fn get_key(
        &self,
        key: impl AsRef<WStr>,
    ) -> DbgModelResult<(ModelObject, Option<KeyStore>)> {
        let mut model = None;
        let mut metadata = None;
        unsafe {
            dmerr!(
                model,
                self.0
                    .GetKey(pcw!(key), Some(&mut model), Some(&mut metadata))
            );

            Ok((model.unwrap().into(), metadata.map(|x| x.into())))
        }
    }

    pub fn get_key_reference(
        &self,
        key: impl AsRef<WStr>,
    ) -> DbgModelResult<(ModelObject, Option<KeyStore>)> {
        let mut object_reference = None;
        let mut metadata = None;
        unsafe {
            dmerr!(
                object_reference,
                self.0.GetKeyReference(
                    pcw!(key),
                    Some(&mut object_reference),
                    Some(&mut metadata)
                )
            );

            Ok((object_reference.unwrap().into(), metadata.map(|x| x.into())))
        }
    }

    pub fn set_key(
        &self,
        key: impl AsRef<WStr>,
        object: Option<&ModelObject>,
        metadata: Option<&KeyStore>,
    ) -> WinResult<()> {
        unsafe {
            self.0.SetKey(
                pcw!(key),
                object.as_ref().map(|x| x.as_raw()),
                metadata.as_ref().map(|x| x.as_raw()),
            )?;
        }

        Ok(())
    }

    pub fn clear_keys(&self) -> WinResult<()> { unsafe { self.0.ClearKeys() } }

    pub fn enumerate_keys(&self) -> WinResult<KeyEnumerator> {
        unsafe { Ok(self.0.EnumerateKeys()?.into()) }
    }

    pub fn enumerate_key_references(&self) -> WinResult<KeyEnumerator> {
        unsafe { Ok(self.0.EnumerateKeyReferences()?.into()) }
    }

    pub fn set_concept<T: Interface>(
        &self,
        concept_interface: &T,
        concept_metadata: Option<&KeyStore>,
    ) -> WinResult<()> {
        unsafe {
            let concept_interface: IUnknown = concept_interface.cast()?;
            self.0.SetConcept(
                &T::IID,
                &concept_interface,
                concept_metadata.map(|x| x.as_raw()),
            )
        }
    }

    pub fn clear_concepts(&self) -> WinResult<()> {
        unsafe { self.0.ClearConcepts() }
    }

    pub fn get_raw_reference(
        &self,
        kind: WdSymbolKind,
        name: impl AsRef<WStr>,
        search_flags: RawSearchFlags,
    ) -> DbgModelResult<ModelObject> {
        unsafe {
            let mut object = std::ptr::null_mut();

            let object = dmerr!(@raw object,
                vcall!(
                self,
                GetRawReference,
                kind.into(),
                pcw!(name),
                search_flags.0 as u32,
                &mut object
            ));

            Ok(object)
        }
    }

    pub fn enumerate_raw_references(
        &self,
        kind: WdSymbolKind,
        search_flags: RawSearchFlags,
    ) -> WinResult<RawEnumerator> {
        unsafe {
            Ok(self
                .0
                .EnumerateRawReferences(kind.into(), search_flags.0 as u32)?
                .into())
        }
    }

    // SetContextForDataModel

    // GetContextForDataModel

    pub fn compare(&self, other: &ModelObject) -> WinResult<ModelObject> {
        unsafe {
            let mut result = None;
            self.0.Compare(other.as_raw(), Some(&mut result))?;
            Ok(result.unwrap().into())
        }
    }

    pub fn is_equal_to(&self, other: &ModelObject) -> WinResult<bool> {
        unsafe { self.0.IsEqualTo(other.as_raw()) }
    }

    pub fn _get_string_displayable_concept(
        &self,
    ) -> WinResult<StringDisplayableConcept> {
        Ok(self.get_concept::<IStringDisplayableConcept>()?.into())
    }
}

// IModelObject2
impl ModelObject {
    // EnumerateOwnKeyValues

    // EnumerateOwnKeys

    // EnumerateOwnKeyReferences
}