use swf_tree as ast;
use nom::IResult;
use nom::{le_f32 as parse_le_f32, be_u16 as parse_be_u16, be_u32 as parse_be_u32, le_u8 as parse_u8, le_u32 as parse_le_u32};
use crate::parsers::basic_data_types::{parse_le_fixed8_p8, parse_le_fixed16_p16, parse_straight_s_rgba8};
#[allow(unused_variables)]
pub fn parse_blend_mode(input: &[u8]) -> IResult<&[u8], ast::BlendMode> {
  switch!(input, parse_u8,
    0 => value!(ast::BlendMode::Normal) |
    1 => value!(ast::BlendMode::Normal) |
    2 => value!(ast::BlendMode::Layer) |
    3 => value!(ast::BlendMode::Multiply) |
    4 => value!(ast::BlendMode::Screen) |
    5 => value!(ast::BlendMode::Lighten) |
    6 => value!(ast::BlendMode::Darken) |
    7 => value!(ast::BlendMode::Difference) |
    8 => value!(ast::BlendMode::Add) |
    9 => value!(ast::BlendMode::Subtract) |
    10 => value!(ast::BlendMode::Invert) |
    11 => value!(ast::BlendMode::Alpha) |
    12 => value!(ast::BlendMode::Erase) |
    13 => value!(ast::BlendMode::Overlay) |
    14 => value!(ast::BlendMode::Hardlight)
    
  )
}
pub fn parse_clip_actions_string(input: &[u8], extended_events: bool) -> IResult<&[u8], Vec<ast::ClipAction>> {
  let mut result: Vec<ast::ClipAction> = Vec::new();
  let mut current_input = input;
  
  loop {
    let head = if extended_events {
      parse_be_u32(current_input)
    } else {
      map!(current_input, parse_be_u16, |x| x as u32)
    };
    match head {
      Ok((next_input, event_flags)) => {
        if event_flags == 0 {
          current_input = next_input;
          break;
        }
      }
      Err(e) => return Err(e),
    };
    match parse_clip_actions(current_input, extended_events) {
      Ok((next_input, clip_actions)) => {
        result.push(clip_actions);
        current_input = next_input;
      }
      Err(e) => return Err(e),
    };
  }
  Ok((current_input, result))
}
#[allow(unused_variables)]
pub fn parse_clip_event_flags(input: &[u8], extended_events: bool) -> IResult<&[u8], ast::ClipEventFlags> {
  do_parse!(
    input,
    flags: switch!(value!(extended_events),
      true => call!(parse_be_u32) |
      false => map!(parse_be_u16, |x| (x as u32) << 16)
    ) >>
    (ast::ClipEventFlags {
      key_up: (flags & (1 << 31)) != 0,
      key_down: (flags & (1 << 30)) != 0,
      mouse_up: (flags & (1 << 29)) != 0,
      mouse_down: (flags & (1 << 28)) != 0,
      mouse_move: (flags & (1 << 27)) != 0,
      unload: (flags & (1 << 26)) != 0,
      enter_frame: (flags & (1 << 25)) != 0,
      load: (flags & (1 << 24)) != 0,
      drag_over: (flags & (1 << 23)) != 0,
      roll_out: (flags & (1 << 22)) != 0,
      roll_over: (flags & (1 << 21)) != 0,
      release_outside: (flags & (1 << 20)) != 0,
      release: (flags & (1 << 19)) != 0,
      press: (flags & (1 << 18)) != 0,
      initialize: (flags & (1 << 17)) != 0,
      data: (flags & (1 << 16)) != 0,
      construct: (flags & (1 << 10)) != 0,
      key_press: (flags & (1 << 9)) != 0,
      drag_out: (flags & (1 << 8)) != 0,
    })
  )
}
pub fn parse_clip_actions(input: &[u8], extended_events: bool) -> IResult<&[u8], ast::ClipAction> {
  do_parse!(
    input,
    events: apply!(parse_clip_event_flags, extended_events) >>
    actions_size: map!(parse_le_u32, |x| if events.key_press && x > 0 { x - 1 } else { x } as usize) >>
    key_code: cond!(events.key_press, parse_u8) >>
    actions: take!(actions_size) >>
    (ast::ClipAction {
      events: events,
      key_code: key_code,
      actions: actions.to_vec(),
    })
  )
}
pub fn parse_filter_list(input: &[u8]) -> IResult<&[u8], Vec<ast::Filter>> {
  length_count!(input, parse_u8, parse_filter)
}
#[allow(unused_variables)]
pub fn parse_filter(input: &[u8]) -> IResult<&[u8], ast::Filter> {
  switch!(input, parse_u8,
    0 => map!(parse_drop_shadow_filter, |f| ast::Filter::DropShadow(f)) |
    1 => map!(parse_blur_filter, |f| ast::Filter::Blur(f)) |
    2 => map!(parse_glow_filter, |f| ast::Filter::Glow(f)) |
    3 => map!(parse_bevel_filter, |f| ast::Filter::Bevel(f)) |
    4 => map!(parse_gradient_glow_filter, |f| ast::Filter::GradientGlow(f)) |
    5 => map!(parse_convolution_filter, |f| ast::Filter::Convolution(f)) |
    6 => map!(parse_color_matrix_filter, |f| ast::Filter::ColorMatrix(f)) |
    7 => map!(parse_gradient_bevel_filter, |f| ast::Filter::GradientBevel(f))
    
  )
}
pub fn parse_bevel_filter(input: &[u8]) -> IResult<&[u8], ast::filters::Bevel> {
  do_parse!(
    input,
    shadow_color: parse_straight_s_rgba8 >>
    highlight_color: parse_straight_s_rgba8 >>
    blur_x: parse_le_fixed16_p16 >>
    blur_y: parse_le_fixed16_p16 >>
    angle: parse_le_fixed16_p16 >>
    distance: parse_le_fixed16_p16 >>
    strength: parse_le_fixed8_p8 >>
    flags: parse_u8 >>
    passes: value!(flags & 0b1111) >>
    on_top: value!((flags & (1 << 4)) != 0) >>
    composite_source: value!((flags & (1 << 5)) != 0) >>
    knockout: value!((flags & (1 << 6)) != 0) >>
    inner: value!((flags & (1 << 7)) != 0) >>
    (ast::filters::Bevel {
      shadow_color: shadow_color,
      highlight_color: highlight_color,
      blur_x: blur_x,
      blur_y: blur_y,
      angle: angle,
      distance: distance,
      strength: strength,
      inner: inner,
      knockout: knockout,
      composite_source: composite_source,
      on_top: on_top,
      passes: passes,
    })
  )
}
pub fn parse_blur_filter(input: &[u8]) -> IResult<&[u8], ast::filters::Blur> {
  do_parse!(
    input,
    blur_x: parse_le_fixed16_p16 >>
    blur_y: parse_le_fixed16_p16 >>
    flags: parse_u8 >>
    
    passes: value!(flags >> 3) >>
    (ast::filters::Blur {
      blur_x: blur_x,
      blur_y: blur_y,
      passes: passes,
    })
  )
}
pub fn parse_color_matrix_filter(input: &[u8]) -> IResult<&[u8], ast::filters::ColorMatrix> {
  do_parse!(
    input,
    matrix: length_count!(value!(20), map!(parse_le_f32, |x| x)) >>
    (ast::filters::ColorMatrix {
      matrix: matrix,
    })
  )
}
pub fn parse_convolution_filter(input: &[u8]) -> IResult<&[u8], ast::filters::Convolution> {
  do_parse!(
    input,
    matrix_width: map!(parse_u8, |x| x as usize) >>
    matrix_height: map!(parse_u8, |x| x as usize) >>
    divisor: parse_le_f32 >>
    bias: parse_le_f32 >>
    matrix: length_count!(value!(matrix_width * matrix_height), parse_le_f32) >>
    default_color: parse_straight_s_rgba8 >>
    flags: parse_u8 >>
    preserve_alpha: value!((flags & (1 << 0)) != 0) >>
    clamp: value!((flags & (1 << 1)) != 0) >>
    
    (ast::filters::Convolution {
      matrix_width: matrix_width,
      matrix_height: matrix_height,
      divisor: divisor,
      bias: bias,
      matrix: matrix,
      default_color: default_color,
      clamp: clamp,
      preserve_alpha: preserve_alpha,
    })
  )
}
pub fn parse_drop_shadow_filter(input: &[u8]) -> IResult<&[u8], ast::filters::DropShadow> {
  do_parse!(
    input,
    color: parse_straight_s_rgba8 >>
    blur_x: parse_le_fixed16_p16 >>
    blur_y: parse_le_fixed16_p16 >>
    angle: parse_le_fixed16_p16 >>
    distance: parse_le_fixed16_p16 >>
    strength: parse_le_fixed8_p8 >>
    flags: parse_u8 >>
    passes: value!(flags & ((1 << 5) - 1)) >>
    composite_source: value!((flags & (1 << 5)) != 0) >>
    knockout: value!((flags & (1 << 6)) != 0) >>
    inner: value!((flags & (1 << 7)) != 0) >>
    (ast::filters::DropShadow {
      color: color,
      blur_x: blur_x,
      blur_y: blur_y,
      angle: angle,
      distance: distance,
      strength: strength,
      inner: inner,
      knockout: knockout,
      composite_source: composite_source,
      passes: passes,
    })
  )
}
pub fn parse_glow_filter(input: &[u8]) -> IResult<&[u8], ast::filters::Glow> {
  do_parse!(
    input,
    color: parse_straight_s_rgba8 >>
    blur_x: parse_le_fixed16_p16 >>
    blur_y: parse_le_fixed16_p16 >>
    strength: parse_le_fixed8_p8 >>
    flags: parse_u8 >>
    passes: value!(flags & ((1 << 5) - 1)) >>
    composite_source: value!((flags & (1 << 5)) != 0) >>
    knockout: value!((flags & (1 << 6)) != 0) >>
    inner: value!((flags & (1 << 7)) != 0) >>
    (ast::filters::Glow {
      color: color,
      blur_x: blur_x,
      blur_y: blur_y,
      strength: strength,
      inner: inner,
      knockout: knockout,
      composite_source: composite_source,
      passes: passes,
    })
  )
}
fn parse_filter_gradient(input: &[u8], color_count: usize) -> IResult<&[u8], Vec<ast::ColorStop>> {
  let mut result: Vec<ast::ColorStop> = Vec::with_capacity(color_count);
  let mut current_input = input;
  for _ in 0..color_count {
    match parse_straight_s_rgba8(current_input) {
      Ok((next_input, color)) => {
        result.push(ast::ColorStop { ratio: 0, color: color });
        current_input = next_input;
      }
      Err(e) => return Err(e),
    };
  }
  for mut color_stop in &mut result {
    match parse_u8(current_input) {
      Ok((next_input, ratio)) => {
        color_stop.ratio = ratio;
        current_input = next_input;
      }
      Err(e) => return Err(e),
    };
  }
  Ok((current_input, result))
}
pub fn parse_gradient_bevel_filter(input: &[u8]) -> IResult<&[u8], ast::filters::GradientBevel> {
  do_parse!(
    input,
    color_count: map!(parse_u8, |x| x as usize) >>
    gradient: apply!(parse_filter_gradient, color_count) >>
    blur_x: parse_le_fixed16_p16 >>
    blur_y: parse_le_fixed16_p16 >>
    angle: parse_le_fixed16_p16 >>
    distance: parse_le_fixed16_p16 >>
    strength: parse_le_fixed8_p8 >>
    flags: parse_u8 >>
    passes: value!(flags & ((1 << 4) - 1)) >>
    on_top: value!((flags & (1 << 4)) != 0) >>
    composite_source: value!((flags & (1 << 5)) != 0) >>
    knockout: value!((flags & (1 << 6)) != 0) >>
    inner: value!((flags & (1 << 7)) != 0) >>
    (ast::filters::GradientBevel {
      gradient: gradient,
      blur_x: blur_x,
      blur_y: blur_y,
      angle: angle,
      distance: distance,
      strength: strength,
      inner: inner,
      knockout: knockout,
      composite_source: composite_source,
      on_top: on_top,
      passes: passes,
    })
  )
}
pub fn parse_gradient_glow_filter(input: &[u8]) -> IResult<&[u8], ast::filters::GradientGlow> {
  do_parse!(
    input,
    color_count: map!(parse_u8, |x| x as usize) >>
    gradient: apply!(parse_filter_gradient, color_count) >>
    blur_x: parse_le_fixed16_p16 >>
    blur_y: parse_le_fixed16_p16 >>
    angle: parse_le_fixed16_p16 >>
    distance: parse_le_fixed16_p16 >>
    strength: parse_le_fixed8_p8 >>
    flags: parse_u8 >>
    passes: value!(flags & ((1 << 4) - 1)) >>
    on_top: value!((flags & (1 << 4)) != 0) >>
    composite_source: value!((flags & (1 << 5)) != 0) >>
    knockout: value!((flags & (1 << 6)) != 0) >>
    inner: value!((flags & (1 << 7)) != 0) >>
    (ast::filters::GradientGlow {
      gradient: gradient,
      blur_x: blur_x,
      blur_y: blur_y,
      angle: angle,
      distance: distance,
      strength: strength,
      inner: inner,
      knockout: knockout,
      composite_source: composite_source,
      on_top: on_top,
      passes: passes,
    })
  )
}