/*
    Copyright (C) 2012, 2013, 2014 Johan Mattsson

    This library is free software; you can redistribute it and/or modify 
    it under the terms of the GNU Lesser General Public License as 
    published by the Free Software Foundation; either version 3 of the 
    License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful, but 
    WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
    Lesser General Public License for more details.
*/

namespace BirdFont {

public class HeadTable : OtfTable {

	public int16 xmin = int16.MIN;
	public int16 ymin = int16.MIN;
	public int16 xmax = int16.MAX;
	public int16 ymax = int16.MAX;
	
	uint32 adjusted_checksum = 0;

	uint16 mac_style;
	uint16 lowest_PPEM;
	int16 font_direction_hint;
		
	public int16 loca_offset_size = 1; // 0 for int16 1 for int32
	int16 glyph_data_format;

	Fixed version;
	Fixed font_revision;
	
	uint32 magic_number;
	
	uint16 flags;
	
	uint64 created;
	uint64 modified;

	public static uint16 units_per_em;
	public static double UNITS;
	
	const uint8 BASELINE_AT_ZERO = 1 << 0;
	const uint8 LSB_AT_ZERO = 1 << 1;
	const uint8 SIZE_SPECIFIC_INSTRUCTIONS = 1 << 2;
	const uint8 FORCE_PPEM_INTEGER_VALUES = 1 <<  3;
	
	GlyfTable glyf_table;
	
	public HeadTable (GlyfTable gt) {
		glyf_table = gt;
		id = "head";
	}
	
	/** Set default value for unit. */
	public static void init (int units_per_em) {
		if (units_per_em <= 0 || units_per_em > uint16.MAX) {
			HeadTable.units_per_em = 1024;
		} else {
			HeadTable.units_per_em = (uint16) units_per_em;
		}
		
		UNITS = 10 * (HeadTable.units_per_em / 1000.0);
	}
	
	public uint32 get_adjusted_checksum () {
		return adjusted_checksum;
	}
	
	/** Get the units per em for generated fonts, not for fonts parsed 
	 * with this library.
	 */
	public double get_units_per_em () {
		return units_per_em * 10;
	}
	
	public override void parse (FontData dis) throws GLib.Error {
		return_if_fail (offset > 0 && length > 0);

		dis.seek (offset);
		
		font_data = new FontData ();		
	
		version = dis.read_fixed ();

		if (!version.equals (1, 0)) {
			warning (@"Expecting head version 1.0 got $(version.get_string ())\n");
		}
		
		font_revision = dis.read_fixed ();
		adjusted_checksum = dis.read_ulong ();
		magic_number = dis.read_ulong ();
		
		if (magic_number != 0x5F0F3CF5) {
			warning (@"Magic number is invalid ($(magic_number)).");
			return;
		}
		
		flags = dis.read_ushort ();
		
		if ((flags & BASELINE_AT_ZERO) > 0) {
			printd ("Flag BASELINE_AT_ZERO has been set.\n");
		}

		if ((flags & LSB_AT_ZERO) > 0) {
			printd ("Flags LSB_AT_ZERO has been set.\n");
		}
		
		units_per_em = dis.read_ushort ();
		
		created = dis.read_udate ();
		modified = dis.read_udate ();
		
		xmin = dis.read_short ();
		ymin = dis.read_short ();
		
		xmax = dis.read_short ();
		ymax = dis.read_short ();

		printd (@"font boundaries:\n");
		printd (@"xmin: $xmin\n");
		printd (@"ymin: $ymin\n");
		printd (@"xmax: $xmax\n");
		printd (@"ymax: $ymax\n");
				
		mac_style = dis.read_ushort ();
		lowest_PPEM = dis.read_ushort ();
		font_direction_hint = dis.read_short ();
		
		loca_offset_size = dis.read_short ();
		glyph_data_format = dis.read_short ();
		
		if (glyph_data_format != 0) {
			warning (@"Unknown glyph data format. Expecting 0 got $glyph_data_format.");
		}
		
		printd (@"Version: $(version.get_string ())\n");
		printd (@"flags: $flags\n");
		printd (@"font_revision: $(font_revision.get_string ())\n");
		printd (@"flags: $flags\n");
		printd (@"Units per em: $units_per_em\n");
		printd (@"lowest_PPEM: $lowest_PPEM\n");
		printd (@"font_direction_hint: $font_direction_hint\n");
		printd (@"loca_offset_size: $loca_offset_size\n");
		printd (@"glyph_data_format: $glyph_data_format\n");
		
		// Some deprecated values follow here ...
	}
	
	public uint32 get_font_checksum () {
		return adjusted_checksum;
	}
	
	public void set_check_sum_adjustment (uint32 csa) {
		this.adjusted_checksum = csa;
	}
	
	public uint32 get_checksum_position () {
		return 8;
	}
	
	public void process () throws GLib.Error {
		FontData font_data = new FontData ();
		Fixed version = 1 << 16;
		Fixed font_revision = 1 << 16;
		uint64 time = (uint64) Math.rint (GLib.get_real_time () / 1000000.0);
		const uint64 time_diff = 60 * 60 * 24 * (365 * (1970 - 1904) + 17); // FIXME: add leap seconds

		font_data.add_fixed (version);
		font_data.add_fixed (font_revision);
		
		// Zero on the first run and updated by directory tables checksum calculation
		// for the entire font.
		font_data.add_u32 (adjusted_checksum);
		
		font_data.add_u32 (0x5F0F3CF5); // magic number
		
		// FIXME: delete LSB_AT_ZERO?
		
		// flags
		font_data.add_u16 (BASELINE_AT_ZERO | LSB_AT_ZERO | FORCE_PPEM_INTEGER_VALUES); 
				
		font_data.add_u16 (units_per_em); // units per em (should be a power of two for ttf fonts)
		
		font_data.add_u64 (time + time_diff); // creation time since 1901-01-01
		font_data.add_u64 (time + time_diff); // modified time since 1901-01-01

		xmin = glyf_table.xmin;
		ymin = glyf_table.ymin;
		xmax = glyf_table.xmax;
		ymax = glyf_table.ymax;

		printd (@"font boundaries:\n");
		printd (@"xmin: $xmin\n");
		printd (@"ymin: $ymin\n");
		printd (@"xmax: $xmax\n");
		printd (@"ymax: $ymax\n");
		
		font_data.add_short (xmin);
		font_data.add_short (ymin);
		font_data.add_short (xmax);
		font_data.add_short (ymax);
	
		font_data.add_u16 (0); // mac style
		font_data.add_u16 (8); // smallest recommended size in pixels, ppem
		font_data.add_16 (2); // deprecated direction hint
		font_data.add_16 (loca_offset_size);  // long offset
		font_data.add_16 (0);  // Use current glyph data format
		
		font_data.pad ();
		
		this.font_data = font_data;
	}
}

}
