//! The result of a compilation

use std::collections::HashMap;

use write_fonts::{
    tables::{self as wtables, gdef::GlyphClassDef, maxp::Maxp},
    types::GlyphId,
    BuilderError, FontBuilder,
};

use super::Opts;

use crate::GlyphMap;

/// The tables generated by this compilation.
///
/// All tables are optional, and the set of tables that are present depends
/// on the input file.
///
/// Each table is a type defined in the [`write-fonts`][] crate. The caller
/// may either interact with these directly, or else they may use the [`to_binary`]
/// method to generate a binary font.
///
/// [`to_binary`]: Compilation::to_binary
pub struct Compilation {
    /// The options passed in for this compilation.
    pub(crate) opts: Opts,
    /// The `head` table, if one was generated
    pub head: Option<wtables::head::Head>,
    /// The `hhea` table, if one was generated
    pub hhea: Option<wtables::hhea::Hhea>,
    /// The `vhea` table, if one was generated
    pub vhea: Option<wtables::vhea::Vhea>,
    /// The `OS/2` table, if one was generated
    pub os2: Option<wtables::os2::Os2>,
    /// The `GDEF` table, if one was generated
    pub gdef: Option<wtables::gdef::Gdef>,
    /// The `BASE` table, if one was generated
    pub base: Option<wtables::base::Base>,
    /// The `name` table, if one was generated
    pub name: Option<wtables::name::Name>,
    /// The `STAT` table, if one was generated
    pub stat: Option<wtables::stat::Stat>,
    /// The `GSUB` table, if one was generated
    pub gsub: Option<wtables::gsub::Gsub>,
    /// The `GPOS` table, if one was generated
    pub gpos: Option<wtables::gpos::Gpos>,
    /// Any *explicit* gdef classes declared in the FEA.
    ///
    /// This is provided so that the user can reference them if they are going
    /// to manually generate kerning or markpos lookups.
    pub gdef_classes: Option<HashMap<GlyphId, GlyphClassDef>>,
}

impl Compilation {
    /// Assemble the output tables into a `FontBuilder`.
    ///
    /// This is a convenience method. To compile a binary font you can use
    /// [`to_binary`] instead, and for more fine-grained control you can inspect
    /// and manipulate the raw tables directly.
    ///
    /// [`to_binary`]: Compilation::to_binary
    pub fn to_font_builder(&self) -> Result<FontBuilder, BuilderError> {
        let mut builder = FontBuilder::default();
        macro_rules! add_if_some {
            ($table:expr) => {
                if let Some(table) = $table.as_ref() {
                    builder.add_table(table)?;
                }
            };
        }
        add_if_some!(self.head);
        add_if_some!(self.hhea);
        add_if_some!(self.vhea);
        add_if_some!(self.os2);
        add_if_some!(self.gdef);
        add_if_some!(self.base);
        add_if_some!(self.name);
        add_if_some!(self.stat);
        add_if_some!(self.gsub);
        add_if_some!(self.gpos);
        Ok(builder)
    }

    /// Compile the output tables into a font.
    ///
    /// This is a convenience method used for things like testing; if you are
    /// building a font compiler you will probably prefer to manipulate the
    /// generated tables directly.
    pub fn to_binary(&self, glyph_map: &GlyphMap) -> Result<Vec<u8>, BuilderError> {
        // because we often inspect our output with ttx, and ttx fails if maxp is
        // missing, we create a maxp table.
        let mut builder = self.to_font_builder()?;
        let maxp = Maxp::new(glyph_map.len().try_into().unwrap());
        builder.add_table(&maxp)?;
        if self.opts.make_post_table {
            let post = glyph_map.make_post_table();
            builder.add_table(&post)?;
        }

        Ok(builder.build())
    }
}
