/src/lib.rs
mod error;
mod record;

use std::cell::RefCell;
use std::io::{self, Read};
use std::rc::Rc;

use error::OmfError;
use num_traits::FromPrimitive;
use record::{
    AbsoluteSegmentAddress, CommentType, ExtName, GroupComponent, MAttrStart, OmfRecord,
    OmfRecordData, PubName, SegmentAlignment, SegmentAttributes,
};

#[derive(Debug, Clone)]
pub struct SegmentInfo {
    pub segment_attributes: SegmentAttributes,
    pub segment_length: u16,
    pub segment_name_index: u8,
    pub class_name_index: u8,
    pub overlay_name_index: u8,
}

#[derive(Debug, Clone)]
pub struct GroupInfo {
    pub group_name_index: u8,
    pub segment_definitions: Vec<GroupComponent>,
}

#[derive(Debug)]
pub struct OmfInfo {
    pub names: Vec<String>,
    pub segments: Vec<SegmentInfo>,
    pub groups: Vec<GroupInfo>,
}

impl OmfInfo {
    pub fn new() -> OmfInfo {
        OmfInfo {
            names: vec![],
            segments: vec![],
            groups: vec![],
        }
    }
}

pub struct OmfReader<'a> {
    r: &'a mut dyn Read,
    info: Rc<RefCell<OmfInfo>>,
}

impl<'a> OmfReader<'a> {
    pub fn new(r: &'a mut dyn Read) -> OmfReader<'a> {
        OmfReader {
            r,
            info: Rc::new(RefCell::new(OmfInfo::new())),
        }
    }

    fn read_u8(&mut self) -> Result<u8, io::Error> {
        let mut buf = [0u8; 1];
        self.r.read_exact(&mut buf)?;
        Ok(buf[0])
    }

    fn read_u16(&mut self) -> Result<u16, io::Error> {
        let mut buf = [0u8; 2];
        self.r.read_exact(&mut buf)?;
        Ok(u16::from_le_bytes(buf))
    }

    fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>, io::Error> {
        let mut buf = Vec::new();
        buf.resize(len, 0u8);
        self.r.read_exact(&mut buf)?;
        Ok(buf)
    }

    fn read_string(&mut self) -> Result<String, OmfError> {
        let len = self.read_u8()? as usize;
        let str_vec = self.read_bytes(len)?;
        Ok(String::from_utf8(str_vec)?)
    }

    fn get_next_record(&mut self) -> Result<Option<OmfRecord>, OmfError> {
        let record_type = match self.read_u8() {
            Ok(v) => v,
            Err(_) => return Ok(None),
        };
        let record_length = self.read_u16()? as usize;

        let data = match record_type {
            0x80 => {
                let name = self.read_string()?;
                OmfRecordData::THeadr { name }
            }
            0x88 => {
                let tmp = self.read_u8()?;
                let no_purge = tmp & 0x80 != 0;
                let no_list = tmp & 0x80 != 0;
                let comment_type = CommentType { no_purge, no_list };
                let comment_class = self.read_u8()?;
                let comment_bytes = self.read_bytes(record_length - 3)?;
                OmfRecordData::Coment {
                    comment_type,
                    comment_class,
                    comment_bytes,
                }
            }
            0x8A => {
                let module_type = self.read_u8()?;
                let main = module_type & 0x80 != 0;
                let start = if module_type & 0x40 != 0 {
                    MAttrStart::Start {
                        end_data: self.read_u8()?,
                        frame_datum: self.read_u8()?,
                        target_datum: self.read_u8()?,
                        target_displacement: self.read_u16()?,
                    }
                } else {
                    MAttrStart::NoStart
                };
                OmfRecordData::ModEnd { main, start }
            }
            0x8C => {
                let mut names = vec![];
                let mut c = 0;
                while c < record_length - 1 {
                    let name = self.read_string()?;
                    let type_index = self.read_u8()?;
                    c += name.len() + 2;
                    names.push(ExtName { name, type_index });
                }
                OmfRecordData::ExtDef { names }
            }
            0x90 => {
                let base_group_index = self.read_u8()?;
                let base_segment_index = self.read_u8()?;
                let base_frame = if base_segment_index == 0 {
                    self.read_u16()?
                } else {
                    0u16
                };
                let mut names = vec![];
                let mut c = 0;
                let rep_len = record_length - 3 - if base_segment_index == 0 { 2 } else { 0 };
                while c < rep_len {
                    let name = self.read_string()?;
                    let public_offset = self.read_u16()?;
                    let type_index = self.read_u8()?;
                    c += name.len() + 4;
                    names.push(PubName {
                        name,
                        public_offset,
                        type_index,
                    });
                }
                OmfRecordData::PubDef {
                    base_group_index,
                    base_segment_index,
                    base_frame,
                    names,
                }
            }
            0x96 => {
                let mut names = vec![];
                let mut c = 0;
                while c < record_length - 1 {
                    let name = self.read_string()?;
                    c += name.len() + 1;
                    names.push(name);
                }
                self.info.borrow_mut().names.append(&mut (names.clone()));
                OmfRecordData::LNames { names }
            }
            0x98 => {
                let tmp = self.read_u8()?;
                let alignment =
                    FromPrimitive::from_u8(tmp >> 5).ok_or(OmfError::Value("alignment"))?;
                let combination =
                    FromPrimitive::from_u8((tmp >> 2) & 3).ok_or(OmfError::Value("combination"))?;
                let absolute_segment_address = if alignment == SegmentAlignment::AbsoluteSegment {
                    let frame_number = self.read_u16()?;
                    let offset = self.read_u8()?;
                    Some(AbsoluteSegmentAddress {
                        frame_number,
                        offset,
                    })
                } else {
                    None
                };
                let segment_attributes = SegmentAttributes {
                    alignment,
                    combination,
                    big: tmp & 2 != 0,
                    bd32bit: tmp & 1 != 0,
                    absolute_segment_address,
                };
                let segment_length = self.read_u16()?;
                let segment_name_index = self.read_u8()?;
                let class_name_index = self.read_u8()?;
                let overlay_name_index = self.read_u8()?;
                self.info.borrow_mut().segments.push(SegmentInfo {
                    segment_attributes: segment_attributes,
                    segment_length,
                    segment_name_index,
                    class_name_index,
                    overlay_name_index,
                });
                OmfRecordData::SegDef {
                    segment_attributes,
                    segment_length,
                    segment_name_index,
                    class_name_index,
                    overlay_name_index,
                }
            }
            0x9A => {
                let group_name_index = self.read_u8()?;
                let mut segment_definitions = vec![];
                let mut c = 0;
                while c < record_length - 2 {
                    let index = self.read_u8()?;
                    let segment_definition = self.read_u8()?;
                    c += 2;
                    segment_definitions.push(GroupComponent {
                        index,
                        segment_definition,
                    });
                }
                self.info.borrow_mut().groups.push(GroupInfo {
                    group_name_index,
                    segment_definitions: segment_definitions.clone(),
                });
                OmfRecordData::GrpDef {
                    group_name_index,
                    segment_definitions,
                }
            }
            0xA0 => {
                let segment_index = self.read_u8()?;
                let enumerated_data_offset = self.read_u16()?;
                let data = self.read_bytes(record_length - 4)?;
                OmfRecordData::LEData {
                    segment_index,
                    enumerated_data_offset,
                    data,
                }
            }
            _ => {
                let data = self.read_bytes(record_length - 1)?;
                OmfRecordData::Unknown { data }
            }
        };
        let checksum = self.read_u8()?;

        Ok(Some(OmfRecord::new(
            record_type,
            record_length,
            data,
            checksum,
            Rc::clone(&self.info),
        )))
    }
}

impl<'a> Iterator for OmfReader<'a> {
    type Item = OmfRecord;

    fn next(&mut self) -> Option<Self::Item> {
        self.get_next_record().expect("next")
    }
}