wdext 0.1.0

A DbgEng wrapper framework
// SPDX-FileCopyrightText: 2026 takubokudori
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{dbgeng::WinResult, extsfns::*, *};
use windows_core::PSTR;

impl_debug_interface!(
    DebugFailureAnalysis,
    DebugFailureAnalysisRef,
    IDebugFailureAnalysis3
);

macro_rules! get_entry {
    ($($t:tt)*) => {{
        let entry = $($t)*;
        if entry.is_null() { return None; }
        FaEntry::from(*entry)
    }};
}

// IDebugFailureAnalysis
impl DebugFailureAnalysis {
    pub fn get_failure_class(&self) -> DebugFailureClass {
        unsafe { self.0.GetFailureClass().try_into().unwrap() }
    }

    pub fn get_failure_type(&self) -> DebugFailureType {
        unsafe { self.0.GetFailureType().into() }
    }

    pub fn get_failure_code(&self) -> u32 { unsafe { self.0.GetFailureCode() } }

    pub fn get(&self, tag: FaTag) -> Option<FaEntry> {
        unsafe {
            let entry = self.0.Get(tag.into());
            (!entry.is_null()).then_some(FaEntry::from(*entry))
        }
    }

    pub fn get_next(
        &self,
        entry: Option<&FaEntry>,
        tag: FaTag,
        tag_mask: FaTag,
    ) -> Option<FaEntry> {
        unsafe {
            let entry = self.0.GetNext(
                entry.map_or(std::ptr::null_mut(), |x| &x.0 as *const _),
                tag.into(),
                tag_mask.into(),
            );
            (!entry.is_null()).then_some(FaEntry::from(*entry))
        }
    }

    pub fn get_string(
        &self,
        tag: FaTag,
        max_size: u32,
    ) -> Option<(FaEntry, ACPString)> {
        let mut buf = Vec::with_capacity(max_size as usize);
        unsafe {
            let s = vcall!(
                self,
                GetString,
                tag.into(),
                PSTR(buf.as_mut_ptr()),
                max_size
            );
            let entry = get_entry!(s);
            let ret = ACPString::clone_from_raw(buf.as_ptr());

            Some((entry, ret))
        }
    }

    pub fn get_buffer(
        &self,
        tag: FaTag,
        size: u32,
    ) -> Option<(FaEntry, Vec<u8>)> {
        let mut buf = Vec::with_capacity(size as usize);
        unsafe {
            let entry = get_entry!(self.0.GetBuffer(
                tag.into(),
                buf.as_mut_ptr() as *mut _,
                size
            ));
            buf.set_len(size as usize);

            Some((entry, buf))
        }
    }

    pub fn get_ulong(&self, tag: FaTag) -> Option<(FaEntry, u32)> {
        let mut value = 0;
        unsafe {
            let entry = get_entry!(self.0.GetUlong(tag.into(), &mut value));
            Some((entry, value))
        }
    }

    pub fn get_ulong64(&self, tag: FaTag) -> Option<(FaEntry, u64)> {
        let mut value = 0;
        unsafe {
            let entry = get_entry!(self.0.GetUlong64(tag.into(), &mut value));
            Some((entry, value))
        }
    }

    pub fn next_entry(&self, entry: Option<&FaEntry>) -> Option<FaEntry> {
        unsafe {
            let entry = self.0.NextEntry(entry.map(|x| &x.0 as *const _));
            (!entry.is_null()).then_some(FaEntry::from(*entry))
        }
    }
}

// IDebugFailureAnalysis2
impl DebugFailureAnalysis {
    pub fn set_string(
        &self,
        tag: FaTag,
        str: impl AsRef<ACPStr>,
    ) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.SetString(tag.into(), pca!(str)));
            Some(entry)
        }
    }

    pub fn set_extension_command(
        &self,
        tag: FaTag,
        extension: impl AsRef<ACPStr>,
    ) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(
                self.0.SetExtensionCommand(tag.into(), pca!(extension))
            );
            Some(entry)
        }
    }

    pub fn set_ulong(&self, tag: FaTag, value: u32) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.SetUlong(tag.into(), value));
            Some(entry)
        }
    }

    pub fn set_ulong64(&self, tag: FaTag, value: u64) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.SetUlong64(tag.into(), value));
            Some(entry)
        }
    }

    pub fn set_buffer(
        &self,
        tag: FaTag,
        fa_entry_type: FaEntryType,
        buf: &[u8],
    ) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.SetBuffer(
                tag.into(),
                fa_entry_type.into(),
                buf.as_ptr() as *const _,
                buf.len() as u32
            ));
            Some(entry)
        }
    }

    pub fn add_string(
        &self,
        tag: FaTag,
        str: impl AsRef<ACPStr>,
    ) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.AddString(tag.into(), pca!(str)));
            Some(entry)
        }
    }

    pub fn add_extension_command(
        &self,
        tag: FaTag,
        extension: impl AsRef<ACPStr>,
    ) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(
                self.0.AddExtensionCommand(tag.into(), pca!(extension))
            );
            Some(entry)
        }
    }

    pub fn add_ulong(&self, tag: FaTag, value: u32) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.AddUlong(tag.into(), value));
            Some(entry)
        }
    }

    pub fn add_ulong64(&self, tag: FaTag, value: u64) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.AddUlong64(tag.into(), value));
            Some(entry)
        }
    }

    pub fn add_buffer(
        &self,
        tag: FaTag,
        fa_entry_type: FaEntryType,
        buf: &[u8],
    ) -> Option<FaEntry> {
        unsafe {
            let entry = get_entry!(self.0.AddBuffer(
                tag.into(),
                fa_entry_type.into(),
                buf.as_ptr() as *const _,
                buf.len() as u32,
            ));
            Some(entry)
        }
    }

    pub fn get_debug_fa_tag_control(&self) -> WinResult<DebugFaEntryTags<'_>> {
        unsafe {
            Ok(DebugFaEntryTags::_from_base(
                self.0.GetDebugFATagControl()?,
                self,
            ))
        }
    }

    // GetAnalysisXml

    // AddStructuredAnalysisData
}

// IDebugFailureAnalysis3
impl DebugFailureAnalysis {
    // AddThreads

    pub fn attribute_get(&self, index: u32) -> WinResult<Variant> {
        unsafe { Ok(self.0.AttributeGet(index)?.into()) }
    }

    /*
    pub fn attribute_get_name(&self, index: u32) -> WinResult<BSTR> {
        unsafe { self.0.AttributeGetName(index) }
    }
    */

    pub fn attribute_set(&self, index: u32, value: Variant) -> WinResult<()> {
        unsafe { self.0.AttributeSet(index, &value.0) }
    }

    // BlameApplication

    // BlameProcess

    // BlameThread

    // BlameModule

    // BlameStitch

    // BlameTEB

    // BlameETHREAD

    // BlameContext

    // BlameManufacturer

    // BlameProduct

    // BlackBacktrace

    // BlackBacktraceArray

    // ProblemClassIsSet

    // ProblemClassDelete

    // ProblemClassSet

    // ProblemClassSetBSTR

    // SetAdditionalXML

    // GetAdditionalXML

    // DeleteAdditionalXML

    // GetTAGName

    // GetTAGIndex

    // GetProblemClassName

    // GetProblemClassIndex

    // GetAttributeName

    // GetAttributeIndex

    // GetContextAsThread

    // GetLastEventContextAsThread

    // GetThreadByStringArray
}