use molecule::{pack_number, Number, NUMBER_SIZE};

pub trait DefaultContent {
    fn default_content(&self) -> Vec<u8>;
}

impl DefaultContent for super::Primitive {
    fn default_content(&self) -> Vec<u8> {
        vec![0]
    }
}

impl DefaultContent for super::Option_ {
    fn default_content(&self) -> Vec<u8> {
        Vec::new()
    }
}

impl DefaultContent for super::Union {
    fn default_content(&self) -> Vec<u8> {
        let first_union_item = self.items().first().unwrap();
        let inner_content = first_union_item.typ.default_content();
        let total_size = NUMBER_SIZE + inner_content.len();
        let mut content = Vec::with_capacity(total_size);
        content.extend_from_slice(&pack_number(first_union_item.id() as Number));
        content.extend_from_slice(&inner_content);
        content
    }
}

impl DefaultContent for super::Array {
    fn default_content(&self) -> Vec<u8> {
        vec![0; self.total_size()]
    }
}

impl DefaultContent for super::Struct {
    fn default_content(&self) -> Vec<u8> {
        vec![0; self.total_size()]
    }
}

impl DefaultContent for super::FixVec {
    fn default_content(&self) -> Vec<u8> {
        let item_count = 0;
        let mut content = Vec::with_capacity(NUMBER_SIZE);
        content.extend_from_slice(&pack_number(item_count as Number));
        content
    }
}

impl DefaultContent for super::DynVec {
    fn default_content(&self) -> Vec<u8> {
        let total_size = NUMBER_SIZE;
        let mut content = Vec::with_capacity(NUMBER_SIZE);
        content.extend_from_slice(&pack_number(total_size as Number));
        content
    }
}

impl DefaultContent for super::Table {
    fn default_content(&self) -> Vec<u8> {
        let field_count = self.fields().len();
        let (total_size, content) = if field_count == 0 {
            let total_size = NUMBER_SIZE;
            let mut content = Vec::with_capacity(total_size);
            content.extend_from_slice(&pack_number(total_size as Number));
            (total_size, content)
        } else {
            let (total_size, offsets, field_data) = self.fields().iter().fold(
                (
                    NUMBER_SIZE * (field_count + 1),
                    Vec::with_capacity(field_count),
                    Vec::with_capacity(field_count),
                ),
                |(mut current_offset, mut offsets, mut field_data), field| {
                    offsets.push(current_offset);
                    let data = field.typ.default_content();
                    current_offset += data.len();
                    field_data.push(data);
                    (current_offset, offsets, field_data)
                },
            );
            let mut content = Vec::with_capacity(total_size);
            content.extend_from_slice(&pack_number(total_size as Number));
            for offset in offsets.into_iter() {
                content.extend_from_slice(&pack_number(offset as Number));
            }
            for data in field_data.into_iter() {
                content.extend_from_slice(&data);
            }
            (total_size, content)
        };
        assert_eq!(content.len(), total_size);
        content
    }
}

impl DefaultContent for super::TopDecl {
    fn default_content(&self) -> Vec<u8> {
        match self {
            super::TopDecl::Primitive(inner) => inner.default_content(),
            super::TopDecl::Option_(inner) => inner.default_content(),
            super::TopDecl::Union(inner) => inner.default_content(),
            super::TopDecl::Array(inner) => inner.default_content(),
            super::TopDecl::Struct(inner) => inner.default_content(),
            super::TopDecl::FixVec(inner) => inner.default_content(),
            super::TopDecl::DynVec(inner) => inner.default_content(),
            super::TopDecl::Table(inner) => inner.default_content(),
        }
    }
}
