use std::{fmt::Display, io::{Cursor, ErrorKind, Read}};

use getset::Getters;

use crate::{value::{FromValue, StringOrStringSet, ToStringOrStringSet}, CDatabase};

#[derive(Getters, Eq, PartialEq)]
#[getset(get = "pub")]
pub struct AncestorsCol {
    ancestors: Vec<i32>,
}

impl FromValue for AncestorsCol {
    fn from_value_opt(value: &crate::cache::Value) -> crate::ntds::Result<Option<Self>>
    where
        Self: Sized,
    {
        let ancestors = match value {
            crate::cache::Value::Null(_) => return Ok(None),
            crate::cache::Value::U8(u) => vec![(*u).into()],
            crate::cache::Value::U16(u) => vec![(*u).into()],
            crate::cache::Value::I16(i) => vec![(*i).into()],
            crate::cache::Value::I32(i) => vec![*i],
            crate::cache::Value::Binary(items) | crate::cache::Value::Guid(items) => {
                if items.len() % 4 != 0 {
                    return Err(crate::ntds::Error::InvalidValueDetected(
                        format!("{value:?}"),
                        "Binary",
                    ));
                }
                let mut cursor = Cursor::new(&items[..]);
                let mut ancestors = Vec::new();
                let mut buf = [0; 4];
                loop {
                    match cursor.read(&mut buf) {
                        Ok(4) => ancestors.push(i32::from_le_bytes(buf)),
                        Ok(0) => break,
                        Ok(x) => panic!("unexpected length of buffer, unexpectedly read {x} bytes"),
                        Err(why) if why.kind() == ErrorKind::UnexpectedEof => break,
                        Err(why) => return Err(why.into()),
                    }
                }
                ancestors
            }
            _ => {
                return Err(crate::ntds::Error::InvalidValueDetected(
                    format!("{value:?}"),
                    "Binary",
                ))
            }
        };
        Ok(Some(Self { ancestors }))
    }
}

impl Display for AncestorsCol {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self.ancestors)
    }
}

impl ToStringOrStringSet for AncestorsCol
{
    fn to_string_or_stringset(&self, _database: &CDatabase<'_, '_>) -> StringOrStringSet {
        StringOrStringSet::String(self.to_string())
    }
}