// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A simple renderer for TrueType fonts

use std::collections::HashMap;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::result::Result;

use geom::{affine_pt, Affine, Point};
use raster::Raster;

#[derive(PartialEq, Eq, Hash)]
struct Tag(u32);

impl Tag {
    fn from_str(s: &str) -> Tag {
        Tag(get_u32(s.as_bytes(), 0).unwrap())
    }
}

impl Display for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let &Tag(tag) = self;
        let buf = vec![
            ((tag >> 24) & 0xff) as u8,
            ((tag >> 16) & 0xff) as u8,
            ((tag >> 8) & 0xff) as u8,
            (tag & 0xff) as u8,
        ];
        f.write_str(&String::from_utf8(buf).unwrap())
    }
}

fn get_u16(data: &[u8], off: usize) -> Option<u16> {
    if off + 1 > data.len() {
        None
    } else {
        Some(((data[off] as u16) << 8) | data[off + 1] as u16)
    }
}

fn get_i16(data: &[u8], off: usize) -> Option<i16> {
    get_u16(data, off).map(|x| x as i16)
}

fn get_f2_14(data: &[u8], off: usize) -> Option<f32> {
    get_i16(data, off).map(|x| x as f32 * (1.0 / (1 << 14) as f32))
}

fn get_u32(data: &[u8], off: usize) -> Option<u32> {
    if off + 3 > data.len() {
        None
    } else {
        Some(
            ((data[off] as u32) << 24)
                | ((data[off + 1] as u32) << 16)
                | ((data[off + 2] as u32) << 8)
                | data[off + 3] as u32,
        )
    }
}

// TODO: be consistent, use newtype or one-field struct everywhere
struct Head<'a>(&'a [u8]);

impl<'a> Head<'a> {
    fn index_to_loc_format(&self) -> i16 {
        get_i16(self.0, 50).unwrap()
    }

    fn units_per_em(&self) -> u16 {
        get_u16(self.0, 18).unwrap()
    }
}

struct Maxp<'a> {
    data: &'a [u8],
}

impl<'a> Maxp<'a> {
    fn num_glyphs(&self) -> u16 {
        get_u16(self.data, 4).unwrap()
    }
}

struct Loca<'a>(&'a [u8]);

impl<'a> Loca<'a> {
    fn get_off(&self, glyph_ix: u16, fmt: i16) -> Option<u32> {
        if fmt != 0 {
            get_u32(self.0, glyph_ix as usize * 4)
        } else {
            get_u16(self.0, glyph_ix as usize * 2).map(|raw| raw as u32 * 2)
        }
    }
}

struct EncodingRecord<'a>(&'a [u8]);

impl<'a> EncodingRecord<'a> {
    fn get_platform_id(&self) -> u16 {
        get_u16(self.0, 0).unwrap()
    }

    fn get_encoding_id(&self) -> u16 {
        get_u16(self.0, 2).unwrap()
    }

    fn get_offset(&self) -> u32 {
        get_u32(self.0, 4).unwrap()
    }
}

impl<'a> Debug for EncodingRecord<'a> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.debug_struct("EncodingRecord")
            .field("platformID", &self.get_platform_id())
            .field("encodingID", &self.get_encoding_id())
            .field("offset", &self.get_offset())
            .finish()
    }
}

struct Encoding<'a>(&'a [u8]);

impl<'a> Encoding<'a> {
    fn get_format(&self) -> u16 {
        get_u16(self.0, 0).unwrap()
    }

    fn get_length(&self) -> u16 {
        get_u16(self.0, 2).unwrap()
    }

    fn get_language(&self) -> u16 {
        get_u16(self.0, 4).unwrap()
    }
}

impl<'a> Debug for Encoding<'a> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.debug_struct("Encoding")
            .field("format", &self.get_format())
            .field("length", &self.get_length())
            .field("language", &self.get_language())
            .finish()
    }
}

struct EncodingFormat4<'a>(&'a [u8]);

impl<'a> EncodingFormat4<'a> {
    fn get_format(&self) -> u16 {
        get_u16(self.0, 0).unwrap()
    }

    fn get_length(&self) -> u16 {
        get_u16(self.0, 2).unwrap()
    }

    fn get_language(&self) -> u16 {
        get_u16(self.0, 4).unwrap()
    }

    fn get_seg_count_x_2(&self) -> u16 {
        get_u16(self.0, 6).unwrap()
    }

    fn get_seg_count(&self) -> u16 {
        self.get_seg_count_x_2() / 2
    }

    fn get_search_range(&self) -> u16 {
        get_u16(self.0, 8).unwrap()
    }

    fn get_entry_selector(&self) -> u16 {
        get_u16(self.0, 10).unwrap()
    }

    fn get_range_shift(&self) -> u16 {
        get_u16(self.0, 12).unwrap()
    }

    fn get_u16_vec(&self, start_position: u16, count: u16) -> Vec<u16> {
        let mut result = vec![];
        let mut vec_position = start_position;
        let limit = vec_position + 2 * count;
        while vec_position < limit {
            result.push(get_u16(self.0, vec_position as usize).unwrap());
            vec_position += 2;
        }
        result
    }

    fn get_i16_vec(&self, start_position: u16, count: u16) -> Vec<i16> {
        let mut result = vec![];
        let mut vec_position = start_position;
        let limit = vec_position + 2 * count;
        while vec_position < limit {
            result.push(get_i16(self.0, vec_position as usize).unwrap());
            vec_position += 2;
        }
        result
    }

    fn get_end_counts_position() -> u16 {
        14
    }

    fn get_end_counts(&self) -> Vec<u16> {
        let seg_count = self.get_seg_count();
        self.get_u16_vec(Self::get_end_counts_position(), seg_count)
    }

    fn get_start_counts_position(seg_count: u16) -> u16 {
        Self::get_end_counts_position() + 2 + 2 * seg_count
    }

    fn get_start_counts(&self) -> Vec<u16> {
        let seg_count = self.get_seg_count();
        self.get_u16_vec(Self::get_start_counts_position(seg_count), seg_count)
    }

    fn get_id_deltas_position(seg_count: u16) -> u16 {
        Self::get_start_counts_position(seg_count) + 2 * seg_count
    }

    fn get_id_deltas(&self) -> Vec<i16> {
        let seg_count = self.get_seg_count();
        self.get_i16_vec(Self::get_id_deltas_position(seg_count), seg_count)
    }

    fn get_id_range_offset_position(seg_count: u16) -> u16 {
        Self::get_id_deltas_position(seg_count) + 2 * seg_count
    }

    fn get_id_range_offsets(&self) -> Vec<u16> {
        let seg_count = self.get_seg_count();
        self.get_u16_vec(Self::get_id_range_offset_position(seg_count), seg_count)
    }

    fn extract_glyph_id(
        &self, code_point: u16, start_value: u16, seg_count: u16, seg_index: u16,
    ) -> Option<u16> {
        let data = self.0;
        let seg_index_pos = 2 * seg_index;
        let id_range_offset_pos = Self::get_id_range_offset_position(seg_count) + seg_index_pos;
        let id_range_offset_value = get_u16(data, id_range_offset_pos as usize).unwrap();
        let id_delta_pos = Self::get_id_deltas_position(seg_count) + seg_index_pos;
        let id_delta = get_i16(data, id_delta_pos as usize).unwrap();
        if id_range_offset_value == 0 {
            Some((code_point as i16 + id_delta) as u16)
        } else {
            let delta = (code_point - start_value) * 2;
            let pos = id_range_offset_pos.wrapping_add(delta) + id_range_offset_value;
            let glyph_array_value = get_u16(data, pos as usize).unwrap();
            if glyph_array_value == 0 {
                return None;
            }
            let glyph_index = (glyph_array_value as i16).wrapping_add(id_delta);
            Some(glyph_index as u16)
        }
    }

    pub fn lookup_glyph_id(&self, code_point: u16) -> Option<u16> {
        let end_counts_position = Self::get_end_counts_position();
        let seg_count = self.get_seg_count();
        let mut size = seg_count - 1;
        let mut index = size / 2;
        while size > 0 {
            let search = end_counts_position + index * 2;
            let end_value = get_u16(self.0, search as usize).unwrap();
            if end_value >= code_point {
                let start_pos = Self::get_start_counts_position(seg_count) + 2 * index;
                let start_value = get_u16(self.0, start_pos as usize).unwrap();
                if start_value > code_point {
                    size /= 2;
                    index -= size;
                } else {
                    return self.extract_glyph_id(code_point, start_value, seg_count, index);
                }
            } else {
                size /= 2;
                index += size;
            }
        }
        None
    }
}

impl<'a> Debug for EncodingFormat4<'a> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.debug_struct("EncodingFormat4")
            .field("format", &self.get_format())
            .field("length", &self.get_length())
            .field("language", &self.get_language())
            .field("segCountX2", &self.get_seg_count_x_2())
            .field("searchRange", &self.get_search_range())
            .field("entrySelector", &self.get_entry_selector())
            .field("rangeShift", &self.get_range_shift())
            .field("endCounts", &self.get_end_counts())
            .field("startCounts", &self.get_start_counts())
            .field("idDeltas", &self.get_id_deltas())
            .field("idRangeOffsets", &self.get_id_range_offsets())
            .finish()
    }
}

struct Cmap<'a>(&'a [u8]);

impl<'a> Cmap<'a> {
    fn get_version(&self) -> u16 {
        get_u16(self.0, 0).unwrap()
    }

    fn get_num_tables(&self) -> u16 {
        get_u16(self.0, 2).unwrap()
    }

    fn get_encoding_record(&self, index: u16) -> Option<EncodingRecord<'a>> {
        if index >= self.get_num_tables() {
            return None;
        }
        let enc_offset = (index * 8 + 4) as usize;
        let encoding_data = &self.0[enc_offset as usize..(enc_offset + 12) as usize];
        Some(EncodingRecord(encoding_data))
    }

    fn get_encoding_records(&self) -> Vec<EncodingRecord> {
        let mut encodings = vec![];
        for i in 0..self.get_num_tables() {
            encodings.push(self.get_encoding_record(i).unwrap());
        }
        encodings
    }

    fn get_encoding(&self, index: u16) -> Option<Encoding<'a>> {
        if index >= self.get_num_tables() {
            return None;
        }
        let record = self.get_encoding_record(index).unwrap();
        let subtable_len = get_u16(self.0, (record.get_offset() + 2) as usize).unwrap() as u32;
        let encoding_data =
            &self.0[record.get_offset() as usize..(record.get_offset() + subtable_len) as usize];
        Some(Encoding(encoding_data))
    }

    fn get_encoding_format_4_at(&self, index: u16) -> Option<EncodingFormat4<'a>> {
        let encoding = self.get_encoding(index);
        if encoding.is_none() || encoding.unwrap().get_format() != 4 {
            return None;
        }
        let record = self.get_encoding_record(index).unwrap();
        let subtable_len = get_u16(self.0, (record.get_offset() + 2) as usize).unwrap() as u32;
        let encoding_data =
            &self.0[record.get_offset() as usize..(record.get_offset() + subtable_len) as usize];
        Some(EncodingFormat4(encoding_data))
    }

    fn get_encodings(&self) -> Vec<Encoding> {
        let mut encodings = vec![];
        for i in 0..self.get_num_tables() {
            encodings.push(self.get_encoding(i).unwrap());
        }
        encodings
    }

    pub fn find_format_4_encoding(&self) -> Option<u16> {
        for index in 0..self.get_num_tables() {
            let encoding = self.get_encoding(index);
            if let Some(encoding) = encoding {
                if encoding.get_format() == 4 {
                    return Some(index);
                }
            }
        }
        None
    }
}

impl<'a> Debug for Cmap<'a> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.debug_struct("Cmap")
            .field("version", &self.get_version())
            .field("numTables", &self.get_num_tables())
            .field("encodingRecords", &self.get_encoding_records())
            .field("encodings", &self.get_encodings())
            .finish()
    }
}

fn get_bbox_raw(data: &[u8]) -> (i16, i16, i16, i16) {
    (
        get_i16(data, 2).unwrap(),
        get_i16(data, 4).unwrap(),
        get_i16(data, 6).unwrap(),
        get_i16(data, 8).unwrap(),
    )
}

enum Glyph<'a> {
    Empty,
    Simple(SimpleGlyph<'a>),
    Compound(CompoundGlyph<'a>),
}

struct SimpleGlyph<'a> {
    data: &'a [u8],
}

impl<'a> SimpleGlyph<'a> {
    fn number_of_contours(&self) -> i16 {
        get_i16(self.data, 0).unwrap()
    }

    fn bbox(&self) -> (i16, i16, i16, i16) {
        get_bbox_raw(self.data)
    }

    fn points(&self) -> GlyphPoints<'a> {
        let data = self.data;
        let n_contours = self.number_of_contours();
        let insn_len_off = 10 + 2 * n_contours as usize;
        let n_points = get_u16(data, insn_len_off - 2).unwrap() as usize + 1;
        let insn_len = get_u16(data, insn_len_off).unwrap(); // insn_len
        let flags_ix = insn_len_off + insn_len as usize + 2;
        let mut flags_size = 0;
        let mut x_size = 0;
        let mut points_remaining = n_points;
        while points_remaining > 0 {
            let flag = data[flags_ix as usize + flags_size];
            let repeat_count = if (flag & 8) == 0 {
                1
            } else {
                flags_size += 1;
                data[flags_ix as usize + flags_size] as usize + 1
            };
            flags_size += 1;
            match flag & 0x12 {
                0x02 | 0x12 => x_size += repeat_count,
                0x00 => x_size += 2 * repeat_count,
                _ => (),
            }
            points_remaining -= repeat_count;
        }
        let x_ix = flags_ix + flags_size;
        let y_ix = x_ix + x_size;
        GlyphPoints {
            data: data,
            x: 0,
            y: 0,
            points_remaining: n_points,
            last_flag: 0,
            flag_repeats_remaining: 0,
            flags_ix: flags_ix,
            x_ix: x_ix,
            y_ix: y_ix,
        }
    }

    fn contour_sizes(&self) -> ContourSizes {
        let n_contours = self.number_of_contours();
        ContourSizes {
            data: self.data,
            contours_remaining: n_contours as usize,
            ix: 10,
            offset: -1,
        }
    }
}

struct GlyphPoints<'a> {
    data: &'a [u8],
    x: i16,
    y: i16,
    points_remaining: usize,
    last_flag: u8,
    flag_repeats_remaining: u8,
    flags_ix: usize,
    x_ix: usize,
    y_ix: usize,
}

impl<'a> Iterator for GlyphPoints<'a> {
    type Item = (bool, i16, i16);
    fn next(&mut self) -> Option<(bool, i16, i16)> {
        if self.points_remaining == 0 {
            None
        } else {
            if self.flag_repeats_remaining == 0 {
                self.last_flag = self.data[self.flags_ix];
                if (self.last_flag & 8) == 0 {
                    self.flags_ix += 1;
                } else {
                    self.flag_repeats_remaining = self.data[self.flags_ix + 1];
                    self.flags_ix += 2;
                }
            } else {
                self.flag_repeats_remaining -= 1;
            }
            let flag = self.last_flag;
            //println!("flag={:02x}, flags_ix={}, x_ix={}, ({}) y_ix={} ({})",
            // flag, self.flags_ix, self.x_ix, self.data.get(self.x_ix), self.y_ix,
            // self.data.get(self.y_ix));
            match flag & 0x12 {
                0x02 => {
                    self.x -= self.data[self.x_ix] as i16;
                    self.x_ix += 1;
                }
                0x00 => {
                    self.x += get_i16(self.data, self.x_ix).unwrap();
                    self.x_ix += 2;
                }
                0x12 => {
                    self.x += self.data[self.x_ix] as i16;
                    self.x_ix += 1;
                }
                _ => (),
            }
            match flag & 0x24 {
                0x04 => {
                    self.y -= self.data[self.y_ix] as i16;
                    self.y_ix += 1;
                }
                0x00 => {
                    self.y += get_i16(self.data, self.y_ix).unwrap();
                    self.y_ix += 2;
                }
                0x24 => {
                    self.y += self.data[self.y_ix] as i16;
                    self.y_ix += 1;
                }
                _ => (),
            }
            self.points_remaining -= 1;
            Some(((self.last_flag & 1) != 0, self.x, self.y))
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (
            self.points_remaining as usize,
            Some(self.points_remaining as usize),
        )
    }
}

struct ContourSizes<'a> {
    data: &'a [u8],
    contours_remaining: usize,
    ix: usize,
    offset: i32,
}

impl<'a> Iterator for ContourSizes<'a> {
    type Item = usize;
    fn next(&mut self) -> Option<(usize)> {
        if self.contours_remaining == 0 {
            None
        } else {
            let ret = get_u16(self.data, self.ix).unwrap() as i32 - self.offset;
            self.offset += ret;
            self.ix += 2;
            self.contours_remaining -= 1;
            Some(ret as usize)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.contours_remaining, Some(self.contours_remaining))
    }
}

struct CompoundGlyph<'a> {
    data: &'a [u8],
}

struct Components<'a> {
    data: &'a [u8],
    more: bool,
    ix: usize,
}

const ARG_1_AND_2_ARE_WORDS: u16 = 1;
const WE_HAVE_A_SCALE: u16 = 1 << 3;
const MORE_COMPONENTS: u16 = 1 << 5;
const WE_HAVE_AN_X_AND_Y_SCALE: u16 = 1 << 6;
const WE_HAVE_A_TWO_BY_TWO: u16 = 1 << 7;

impl<'a> Iterator for Components<'a> {
    type Item = (u16, Affine);
    fn next(&mut self) -> Option<(u16, Affine)> {
        if !self.more {
            return None;
        }
        let flags = get_u16(self.data, self.ix).unwrap();
        self.ix += 2;
        let glyph_index = get_u16(self.data, self.ix).unwrap();
        self.ix += 2;
        let arg1;
        let arg2;
        if (flags & ARG_1_AND_2_ARE_WORDS) != 0 {
            arg1 = get_i16(self.data, self.ix).unwrap();
            self.ix += 2;
            arg2 = get_i16(self.data, self.ix).unwrap();
            self.ix += 2;
        } else {
            arg1 = self.data[self.ix] as i16;
            self.ix += 1;
            arg2 = self.data[self.ix] as i16;
            self.ix += 1;
        }
        let mut a = 1.0;
        let mut b = 0.0;
        let mut c = 0.0;
        let mut d = 1.0;
        if (flags & WE_HAVE_A_TWO_BY_TWO) != 0 {
            a = get_f2_14(self.data, self.ix).unwrap();
            self.ix += 2;
            b = get_f2_14(self.data, self.ix).unwrap();
            self.ix += 2;
            c = get_f2_14(self.data, self.ix).unwrap();
            self.ix += 2;
            d = get_f2_14(self.data, self.ix).unwrap();
            self.ix += 2;
        } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0 {
            a = get_f2_14(self.data, self.ix).unwrap();
            self.ix += 2;
            d = get_f2_14(self.data, self.ix).unwrap();
            self.ix += 2;
        } else if (flags & WE_HAVE_A_SCALE) != 0 {
            a = get_f2_14(self.data, self.ix).unwrap();
            self.ix += 2;
            d = a;
        }
        // TODO: handle non-ARGS_ARE_XY_VALUES case
        let x = arg1 as f32;
        let y = arg2 as f32;
        let z = Affine::new(a, b, c, d, x, y);
        self.more = (flags & MORE_COMPONENTS) != 0;
        Some((glyph_index, z))
    }
}

impl<'a> CompoundGlyph<'a> {
    fn bbox(&self) -> (i16, i16, i16, i16) {
        get_bbox_raw(self.data)
    }

    fn components(&self) -> Components {
        Components {
            data: self.data,
            ix: 10,
            more: true,
        }
    }
}

pub struct Font<'a> {
    _version: u32,
    _tables: HashMap<Tag, &'a [u8]>,
    head: Head<'a>,
    maxp: Maxp<'a>,
    cmap: Option<Cmap<'a>>,
    loca: Option<Loca<'a>>,
    glyf: Option<&'a [u8]>,
    encoding_index: Option<u16>,
}

struct Metrics {
    l: i32,
    t: i32,
    r: i32,
    b: i32,
}

impl Metrics {
    fn width(&self) -> usize {
        (self.r - self.l) as usize
    }

    fn height(&self) -> usize {
        (self.b - self.t) as usize
    }
}

impl<'a> Font<'a> {
    fn metrics_and_affine(
        &self, xmin: i16, ymin: i16, xmax: i16, ymax: i16, size: u32,
    ) -> (Metrics, Affine) {
        let ppem = self.head.units_per_em();
        let scale = (size as f32) / (ppem as f32);
        let l = (xmin as f32 * scale).floor() as i32;
        let t = (ymax as f32 * -scale).floor() as i32;
        let r = (xmax as f32 * scale).ceil() as i32;
        let b = (ymin as f32 * -scale).ceil() as i32;
        let metrics = Metrics {
            l: l,
            t: t,
            r: r,
            b: b,
        };
        let z = Affine::new(scale, 0.0, 0.0, -scale, -l as f32, -t as f32);
        (metrics, z)
    }

    fn render_glyph_inner(&self, raster: &mut Raster, z: &Affine, glyph: &Glyph) {
        match *glyph {
            Glyph::Simple(ref s) => {
                let mut p = s.points();
                for n in s.contour_sizes() {
                    //println!("n = {}", n);
                    //let v = path_from_pts(p.by_ref().take(n)).collect::<Vec<_>>();
                    //println!("size = {}", v.len());
                    draw_path(raster, z, &mut path_from_pts(p.by_ref().take(n)));
                }
            }
            Glyph::Compound(ref c) => {
                for (glyph_index, affine) in c.components() {
                    //println!("component {} {:?}", glyph_index, affine);
                    let concat = Affine::concat(z, &affine);
                    if let Some(component_glyph) = self.get_glyph(glyph_index) {
                        self.render_glyph_inner(raster, &concat, &component_glyph);
                    }
                }
            }
            _ => {
                println!("unhandled glyph case");
            }
        }
    }

    pub fn render_glyph(&self, glyph_id: u16, size: u32) -> Option<GlyphBitmap> {
        let glyph = self.get_glyph(glyph_id);
        match glyph {
            Some(Glyph::Simple(ref s)) => {
                let (xmin, ymin, xmax, ymax) = s.bbox();
                let (metrics, z) = self.metrics_and_affine(xmin, ymin, xmax, ymax, size);
                let mut raster = Raster::new(metrics.width(), metrics.height());
                //dump_glyph(SimpleGlyph(s));
                self.render_glyph_inner(&mut raster, &z, glyph.as_ref().unwrap());
                //None
                Some(GlyphBitmap {
                    width: metrics.width(),
                    height: metrics.height(),
                    left: metrics.l,
                    top: metrics.t,
                    data: raster.get_bitmap(),
                })
            }
            Some(Glyph::Compound(ref c)) => {
                let (xmin, ymin, xmax, ymax) = c.bbox();
                let (metrics, z) = self.metrics_and_affine(xmin, ymin, xmax, ymax, size);
                let mut raster = Raster::new(metrics.width(), metrics.height());
                self.render_glyph_inner(&mut raster, &z, glyph.as_ref().unwrap());
                Some(GlyphBitmap {
                    width: metrics.width(),
                    height: metrics.height(),
                    left: metrics.l,
                    top: metrics.t,
                    data: raster.get_bitmap(),
                })
            }
            _ => {
                println!("glyph {} error", glyph_id);
                None
            }
        }
    }

    fn get_glyph(&self, glyph_ix: u16) -> Option<Glyph> {
        if glyph_ix >= self.maxp.num_glyphs() {
            return None;
        }
        let fmt = self.head.index_to_loc_format();
        match self.loca {
            Some(ref loca) => match (
                loca.get_off(glyph_ix, fmt),
                loca.get_off(glyph_ix + 1, fmt),
                self.glyf,
            ) {
                (Some(off0), Some(off1), Some(glyf)) => if off0 == off1 {
                    Some(Glyph::Empty)
                } else {
                    let glyph_data = &glyf[off0 as usize..off1 as usize];
                    if get_i16(glyph_data, 0) == Some(-1) {
                        Some(Glyph::Compound(CompoundGlyph { data: glyph_data }))
                    } else {
                        Some(Glyph::Simple(SimpleGlyph { data: glyph_data }))
                    }
                },
                (_, _, _) => None,
            },
            None => None,
        }
    }

    pub fn lookup_glyph_id(&self, code_point: u32) -> Option<u16> {
        match self.encoding_index {
            Some(encoding_index) => {
                if code_point > u16::max_value() as u32 {
                    return None;
                }

                self.cmap
                    .as_ref()
                    .unwrap()
                    .get_encoding_format_4_at(encoding_index)
                    .unwrap()
                    .lookup_glyph_id(code_point as u16)
            }
            None => None,
        }
    }
}

#[derive(Debug)]
enum PathOp {
    MoveTo(Point),
    LineTo(Point),
    QuadTo(Point, Point),
}

use self::PathOp::{LineTo, MoveTo, QuadTo};

struct BezPathOps<T> {
    inner: T,
    first_oncurve: Option<Point>,
    first_offcurve: Option<Point>,
    last_offcurve: Option<Point>,
    alldone: bool,
    closing: bool,
}

fn path_from_pts<T: Iterator>(inner: T) -> BezPathOps<T> {
    BezPathOps {
        inner: inner,
        first_oncurve: None,
        first_offcurve: None,
        last_offcurve: None,
        alldone: false,
        closing: false,
    }
}

impl<I> Iterator for BezPathOps<I>
where
    I: Iterator<Item = (bool, i16, i16)>,
{
    type Item = PathOp;
    fn next(&mut self) -> Option<PathOp> {
        loop {
            if self.closing {
                if self.alldone {
                    return None;
                } else {
                    match (self.first_offcurve, self.last_offcurve) {
                        (None, None) => {
                            self.alldone = true;
                            return Some(LineTo(self.first_oncurve.unwrap()));
                        }
                        (None, Some(last_offcurve)) => {
                            self.alldone = true;
                            return Some(QuadTo(last_offcurve, self.first_oncurve.unwrap()));
                        }
                        (Some(first_offcurve), None) => {
                            self.alldone = true;
                            return Some(QuadTo(first_offcurve, self.first_oncurve.unwrap()));
                        }
                        (Some(first_offcurve), Some(last_offcurve)) => {
                            self.last_offcurve = None;
                            return Some(QuadTo(
                                last_offcurve,
                                Point::lerp(0.5, &last_offcurve, &first_offcurve),
                            ));
                        }
                    }
                }
            } else {
                match self.inner.next() {
                    None => {
                        self.closing = true;
                    }
                    Some((oncurve, x, y)) => {
                        let p = Point::new(x, y);
                        if self.first_oncurve.is_none() {
                            if oncurve {
                                self.first_oncurve = Some(p);
                                return Some(MoveTo(p));
                            } else {
                                match self.first_offcurve {
                                    None => self.first_offcurve = Some(p),
                                    Some(first_offcurve) => {
                                        let midp = Point::lerp(0.5, &first_offcurve, &p);
                                        self.first_oncurve = Some(midp);
                                        self.last_offcurve = Some(p);
                                        return Some(MoveTo(midp));
                                    }
                                }
                            }
                        } else {
                            match (self.last_offcurve, oncurve) {
                                (None, false) => self.last_offcurve = Some(p),
                                (None, true) => return Some(LineTo(p)),
                                (Some(last_offcurve), false) => {
                                    self.last_offcurve = Some(p);
                                    return Some(QuadTo(
                                        last_offcurve,
                                        Point::lerp(0.5, &last_offcurve, &p),
                                    ));
                                }
                                (Some(last_offcurve), true) => {
                                    self.last_offcurve = None;
                                    return Some(QuadTo(last_offcurve, p));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub enum FontError {
    Invalid,
}

pub fn parse(data: &[u8]) -> Result<Font, FontError> {
    if data.len() < 12 {
        return Err(FontError::Invalid);
    }
    let version = get_u32(data, 0).unwrap();
    let num_tables = get_u16(data, 4).unwrap() as usize;
    let _search_range = get_u16(data, 6).unwrap();
    let _entry_selector = get_u16(data, 8).unwrap();
    let _range_shift = get_u16(data, 10).unwrap();
    let mut tables = HashMap::new();
    for i in 0..num_tables {
        let header = &data[12 + i * 16..12 + (i + 1) * 16];
        let tag = get_u32(header, 0).unwrap();
        let _check_sum = get_u32(header, 4).unwrap();
        let offset = get_u32(header, 8).unwrap();
        let length = get_u32(header, 12).unwrap();
        let table_data = &data[offset as usize..(offset + length) as usize];
        //println!("{}: {}", Tag(tag), table_data.len());
        tables.insert(Tag(tag), table_data);
    }
    let head = Head(*tables.get(&Tag::from_str("head")).unwrap()); // todo: don't fail
    let maxp = Maxp {
        data: *tables.get(&Tag::from_str("maxp")).unwrap(),
    };
    let loca = tables.get(&Tag::from_str("loca")).map(|&data| Loca(data));
    let glyf = tables.get(&Tag::from_str("glyf")).map(|&data| data);
    let cmap = tables.get(&Tag::from_str("cmap")).map(|&data| Cmap(data));
    let encoding_index = cmap.as_ref().and_then(|cmap| cmap.find_format_4_encoding());
    let f = Font {
        _version: version,
        _tables: tables,
        head: head,
        maxp: maxp,
        loca: loca,
        cmap: cmap,
        glyf: glyf,
        encoding_index: encoding_index,
    };
    //println!("version = {:x}", version);
    Ok(f)
}

/*
fn dump_glyph(g: Glyph) {
    match g {
        Glyph::Empty => println!("empty"),
        Glyph::Simple(s) => {
            //println!("{} contours", s.number_of_contours())
            let mut p = s.points();
            for n in s.contour_sizes() {
                for _ in 0..n {
                    println!("{:?}", p.next().unwrap());
                }
                println!("z");
            }
            let mut p = s.points();
            for n in s.contour_sizes() {
                for pathop in path_from_pts(p.by_ref().take(n)) {
                    println!("{:?}", pathop);
                }
            }
        },
        _ => println!("other")
    }
}
*/

/*
fn dump(data: Vec<u8>) {
    println!("length is {}", data.len());
    match parse(&data) {
        Ok(font) => {
            println!("numGlyphs = {}", font.maxp.num_glyphs());
            for i in 0.. font.maxp.num_glyphs() {
                println!("glyph {}", i);
                match font.get_glyph(i) {
                    Some(g) => dump_glyph(g),
                    None => println!("glyph {} error", i)
                }
            }
        },
        _ => ()
    }
}
*/

fn draw_path<I: Iterator<Item = PathOp>>(r: &mut Raster, z: &Affine, path: &mut I) {
    let mut lastp = Point::new(0i16, 0i16);
    for op in path {
        match op {
            MoveTo(p) => lastp = p,
            LineTo(p) => {
                r.draw_line(&affine_pt(z, &lastp), &affine_pt(z, &p));
                lastp = p
            }
            QuadTo(p1, p2) => {
                r.draw_quad(
                    &affine_pt(z, &lastp),
                    &affine_pt(z, &p1),
                    &affine_pt(z, &p2),
                );
                lastp = p2;
            }
        }
    }
}

pub struct GlyphBitmap {
    pub width: usize,
    pub height: usize,
    pub left: i32,
    pub top: i32,
    pub data: Vec<u8>,
}

#[cfg(test)]
mod tests {

    use font::parse;

    static FONT_DATA: &'static [u8] =
        include_bytes!("../fonts/notomono-hinted/NotoMono-Regular.ttf");

    #[test]
    fn test_cmap_format_4() {
        let font = parse(&FONT_DATA).unwrap();
        let cmap = font.cmap.as_ref().unwrap();
        assert!(cmap.get_encoding_record(cmap.get_num_tables()).is_none());
        assert!(cmap.get_encoding(cmap.get_num_tables()).is_none());
        assert_eq!(font.lookup_glyph_id('A' as u32).unwrap(), 36);
        assert_eq!(font.lookup_glyph_id(0x3c8).unwrap(), 405);
        assert_eq!(font.lookup_glyph_id(0xfffd).unwrap(), 589);
        assert_eq!(font.lookup_glyph_id(0x232B).is_none(), true);
        assert_eq!(font.lookup_glyph_id(0x1000232B).is_none(), true);
        // test for panics
        for i in 0..0x1ffff {
            font.lookup_glyph_id(i);
        }
    }
}
