use swf_tree as ast;
use nom::IResult;
use nom::{le_i16 as parse_le_i16, le_u8 as parse_u8, le_u16 as parse_le_u16, le_u32 as parse_le_u32};
use crate::parsers::basic_data_types::{
  parse_rect,
  parse_le_f16,
  parse_s_rgb8,
  parse_straight_s_rgba8,
  parse_i32_bits,
  parse_u32_bits,
};
use crate::parsers::shape::parse_glyph;
pub fn parse_grid_fitting_bits(input: (&[u8], usize)) -> IResult<(&[u8], usize), ast::text::GridFitting> {
  switch!(
    input,
    apply!(parse_u32_bits, 3),
    0 => value!(ast::text::GridFitting::None) |
    1 => value!(ast::text::GridFitting::Pixel) |
    2 => value!(ast::text::GridFitting::SubPixel)
    
  )
}
pub fn parse_csm_table_hint_bits(input: (&[u8], usize)) -> IResult<(&[u8], usize), ast::text::CsmTableHint> {
  switch!(
    input,
    apply!(parse_u32_bits, 2),
    0 => value!(ast::text::CsmTableHint::Thin) |
    1 => value!(ast::text::CsmTableHint::Medium) |
    2 => value!(ast::text::CsmTableHint::Thick)
    
  )
}
pub fn parse_text_renderer_bits(input: (&[u8], usize)) -> IResult<(&[u8], usize), ast::text::TextRenderer> {
  switch!(
    input,
    apply!(parse_u32_bits, 2),
    0 => value!(ast::text::TextRenderer::Normal) |
    1 => value!(ast::text::TextRenderer::Advanced)
    
  )
}
pub fn parse_font_alignment_zone(input: &[u8]) -> IResult<&[u8], ast::text::FontAlignmentZone> {
  do_parse!(
    input,
    data: length_count!(parse_u8, parse_font_alignment_zone_data) >>
    flags: parse_u8 >>
    (ast::text::FontAlignmentZone {
      data: data,
      has_x: (flags & (1 << 0)) != 0,
      has_y: (flags & (1 << 1)) != 0
    })
  )
}
pub fn parse_font_alignment_zone_data(input: &[u8]) -> IResult<&[u8], ast::text::FontAlignmentZoneData> {
  do_parse!(
    input,
    origin: parse_le_f16 >>
    size: parse_le_f16 >>
    (ast::text::FontAlignmentZoneData {
      origin,
      size,
    })
  )
}
pub fn parse_text_record_string(input: &[u8], has_alpha: bool, index_bits: usize, advance_bits: usize) -> IResult<&[u8], Vec<ast::text::TextRecord>> {
  let mut result: Vec<ast::text::TextRecord> = Vec::new();
  let mut current_input: &[u8] = input;
  while current_input.len() > 0 {
    
    if current_input[0] == 0 {
      current_input = ¤t_input[1..];
      return Ok((current_input, result));
    }
    match parse_text_record(current_input, has_alpha, index_bits, advance_bits) {
      Ok((next_input, text_record)) => {
        current_input = next_input;
        result.push(text_record);
      }
      Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(::nom::Needed::Unknown)),
      Err(e) => return Err(e),
    };
  }
  Err(::nom::Err::Incomplete(::nom::Needed::Unknown))
}
pub fn parse_text_record(input: &[u8], has_alpha: bool, index_bits: usize, advance_bits: usize) -> IResult<&[u8], ast::text::TextRecord> {
  do_parse!(
    input,
    flags: parse_u8 >>
    has_font: value!((flags & (1 << 3)) != 0) >>
    has_color: value!((flags & (1 << 2)) != 0) >>
    has_offset_x: value!((flags & (1 << 1)) != 0) >>
    has_offset_y: value!((flags & (1 << 0)) !=  0) >>
    font_id: cond!(has_font, parse_le_u16) >>
    color: cond!(has_color, switch!(value!(has_alpha),
      true => call!(parse_straight_s_rgba8) |
      false => map!(parse_s_rgb8, |c| ast::StraightSRgba8 {r: c.r, g: c.g, b: c.b, a: 255})
    )) >>
    offset_x: cond!(has_offset_x, parse_le_i16) >>
    offset_y: cond!(has_offset_y, parse_le_i16) >>
    font_size: cond!(has_font, parse_le_u16) >>
    entry_count: parse_u8 >>
    entries: bits!(apply!(parse_glyph_entries, entry_count, index_bits, advance_bits)) >>
    (ast::text::TextRecord {
      font_id: font_id,
      color: color,
      offset_x: offset_x.unwrap_or_default(),
      offset_y: offset_y.unwrap_or_default(),
      font_size: font_size,
      entries: entries,
    })
  )
}
pub fn parse_glyph_entries(input: (&[u8], usize), entry_count: u8, index_bits: usize, advance_bits: usize) -> IResult<(&[u8], usize), Vec<ast::text::GlyphEntry>> {
  length_count!(input,
    value!(entry_count),
    apply!(parse_glyph_entry, index_bits, advance_bits)
  )
}
pub fn parse_glyph_entry(input: (&[u8], usize), index_bits: usize, advance_bits: usize) -> IResult<(&[u8], usize), ast::text::GlyphEntry> {
  do_parse!(
    input,
    index: map!(apply!(parse_u32_bits, index_bits), |x| x as usize) >>
    advance: apply!(parse_i32_bits, advance_bits) >>
    (ast::text::GlyphEntry {
      index: index,
      advance: advance,
    })
  )
}
pub fn parse_offset_glyphs(input: &[u8], glyph_count: usize, use_wide_offsets: bool) -> IResult<&[u8], Vec<ast::Glyph>> {
  let parsed_offsets = if use_wide_offsets {
    pair!(
      input,
      length_count!(value!(glyph_count), map!(parse_le_u32, |x| x as usize)),
      map!(parse_le_u32, |x| x as usize)
    )
  } else {
    pair!(
      input,
      length_count!(value!(glyph_count), map!(parse_le_u16, |x| x as usize)),
      map!(parse_le_u16, |x| x as usize)
    )
  };
  let (offsets, end_offset) = match parsed_offsets {
    Ok((_, o)) => o,
    Err(e) => return Err(e),
  };
  let mut glyphs: Vec<ast::Glyph> = Vec::with_capacity(glyph_count);
  for i in 0..glyph_count {
    let start_offset = offsets[i];
    let end_offset = if i + 1 < glyph_count { offsets[i + 1] } else { end_offset };
    match parse_glyph(&input[start_offset..end_offset]) {
      Ok((_, o)) => glyphs.push(o),
      Err(e) => return Err(e),
    };
  }
  value!(&input[end_offset..], glyphs)
}
pub fn parse_kerning_record(input: &[u8]) -> IResult<&[u8], ast::text::KerningRecord> {
  do_parse!(
    input,
    left: parse_le_u16 >>
    right: parse_le_u16 >>
    adjustment: parse_le_i16 >>
    (ast::text::KerningRecord {
      left: left,
      right: right,
      adjustment: adjustment,
    })
  )
}
pub fn parse_font_layout(input: &[u8], glyph_count: usize) -> IResult<&[u8], ast::text::FontLayout> {
  do_parse!(input,
    ascent: parse_le_u16 >>
    descent: parse_le_u16 >>
    leading: parse_le_u16 >>
    advances: length_count!(value!(glyph_count), parse_le_u16) >>
    bounds: length_count!(value!(glyph_count), parse_rect) >>
    kerning: length_count!(parse_le_u16, parse_kerning_record) >>
    (ast::text::FontLayout {
      ascent: ascent,
      descent: descent,
      leading: leading,
      advances,
      bounds,
      kerning,
    })
  )
}
pub fn parse_text_alignment(input: &[u8]) -> IResult<&[u8], ast::text::TextAlignment> {
  switch!(
    input,
    parse_u8,
    0 => value!(ast::text::TextAlignment::Left) |
    1 => value!(ast::text::TextAlignment::Right) |
    2 => value!(ast::text::TextAlignment::Center) |
    3 => value!(ast::text::TextAlignment::Justify)
    
  )
}