use std::collections::BTreeMap;
use std::io;
use crate::ifd::tags::{self, FieldTag};
use crate::ifd::values::{AllocatedFieldValues, FieldValues, OffsetsToIfds};
use crate::write::{Cursor, EndianFile};
pub struct IfdChain(Vec<Ifd>);
impl IfdChain {
pub fn new(ifds: Vec<Ifd>) -> IfdChain {
if ifds.len() == 0 {
panic!("Cannot create a chain without IFDs.")
}
for ifd in ifds.iter() {
if ifd.entry_count() == 0 {
panic!("Tried to create a chain containing empty IFDs.\nEach IFD must have at least 1 entry.")
}
}
IfdChain(ifds)
}
pub fn single(ifd: Ifd) -> IfdChain {
IfdChain::new(vec![ifd])
}
pub(crate) fn allocate(self, c: &mut Cursor) -> AllocatedIfdChain {
let len = self.0.len();
let mut ifds = Vec::with_capacity(len);
for (index, ifd) in self.0.into_iter().enumerate() {
ifds.push(ifd.allocate(c, index + 1 == len));
}
AllocatedIfdChain(ifds)
}
}
pub(crate) struct AllocatedIfdChain(Vec<AllocatedIfd>);
impl AllocatedIfdChain {
pub(crate) fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
for ifd in self.0.into_iter() {
ifd.write_to(file)?;
}
Ok(())
}
}
pub struct Ifd {
entries: BTreeMap<FieldTag, Box<FieldValues>>,
}
impl Ifd {
pub fn new() -> Ifd {
Ifd {
entries: BTreeMap::new(),
}
}
pub fn with_entry<T: FieldValues + 'static>(mut self, tag: FieldTag, value: T) -> Self {
if self.entries.insert(tag, Box::new(value)).is_some() {
panic!("Tried to add the same tag twice.");
}
self
}
pub fn with_entries<C: IntoIterator<Item=(FieldTag, Box<FieldValues>)>>(mut self, entries: C) -> Self {
entries.into_iter().for_each(|(tag, value)| {
if self.entries.insert(tag, value).is_some() {
panic!("Tried to add the same tag twice.");
}
});
self
}
pub fn with_subifds(self, subifds: Vec<IfdChain>) -> Self {
self.with_entry(tags::SubIFDs, OffsetsToIfds::new(subifds))
}
pub fn single(self) -> IfdChain {
IfdChain::single(self)
}
fn entry_count(&self) -> u32 {
self.entries.len() as u32
}
fn size(&self) -> u32 {
self.entry_count() * 12 + 6
}
fn allocate(self, c: &mut Cursor, last_ifd: bool) -> AllocatedIfd {
c.allocate(self.size());
let mut entries = BTreeMap::new();
for (tag, value) in self.entries {
entries.insert(tag, value.allocate(c));
}
let offset_to_next_ifd = if last_ifd {
None
} else {
Some(c.allocated_bytes())
};
AllocatedIfd {
entries,
offset_to_next_ifd,
}
}
}
struct AllocatedIfd {
entries: BTreeMap<FieldTag, Box<AllocatedFieldValues>>,
offset_to_next_ifd: Option<u32>,
}
impl AllocatedIfd {
fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
let mut big_values = Vec::new();
file.write_u16(self.entries.len() as u16)?;
for (tag, value) in self.entries.into_iter() {
let value = Self::write_entry_to((tag, value), file)?;
if let Some(value) = value {
big_values.push(value);
}
}
file.write_u32(self.offset_to_next_ifd.unwrap_or(0))?;
for value in big_values {
value.write_to(file)?;
}
Ok(())
}
fn write_entry_to(
(tag, value): (FieldTag, Box<AllocatedFieldValues>),
file: &mut EndianFile,
) -> io::Result<Option<Box<AllocatedFieldValues>>> {
file.write_u16(tag)?;
file.write_u16(value.type_id())?;
file.write_u32(value.count())?;
match value.position() {
Some(position) => {
file.write_u32(position)?;
Ok(Some(value))
}
None => {
let size = value.size();
value.write_to(file)?;
for _ in 0..(4 - size) {
file.write_u8(0)?;
}
Ok(None)
}
}
}
}