extern crate zip;

use super::*;
use std::io::Write;

fn to_byte(bits: u8) -> u8 {
  if bits >= 0xa {
    bits + b'a' - 10
  } else {
    bits + b'0'
  }
}

fn number_to_hex(number: u8) -> Vec<u8> {
  vec![to_byte(number >> 4), to_byte(number & 0xf)]
}

fn hex_to_bytes(color: Color) -> Vec<u8> {
  let mut hex = vec![b'#'];

  hex.append(&mut number_to_hex(color[0]));
  hex.append(&mut number_to_hex(color[1]));
  hex.append(&mut number_to_hex(color[2]));
  hex.append(&mut number_to_hex(color[3]));

  hex
}

pub fn serialize_palette(variables: &Variables) -> Vec<u8> {
  let mut bytes = Vec::new();

  for (variable, value) in variables {
    bytes.append(&mut variable.as_bytes().to_vec());
    bytes.push(b':');
    bytes.push(b' ');

    match value {
      Value::Color(color) => bytes.append(&mut hex_to_bytes(*color)),
      Value::Link(link) => bytes.append(&mut link.as_bytes().to_vec()),
    }

    bytes.push(b';');
    bytes.push(b'\n');
  }

  bytes
}

pub fn serialize_theme(
  variables: &Variables,
  wallpaper: &Option<Wallpaper>,
) -> Result<Vec<u8>, zip::result::ZipError> {
  let mut buffer = Vec::new();

  {
    let cursor = std::io::Cursor::new(&mut buffer);
    let mut zip = zip::ZipWriter::new(cursor);
    let options = zip::write::FileOptions::default()
      // Somehow, the default Deflated distorted the background in some of our
      // tests. Probably it's `zip`'s bug, but anyway we preffered not to
      // compress the theme at all.
      .compression_method(zip::CompressionMethod::Stored);

    let palette = serialize_palette(variables);

    zip.start_file("colors.tdesktop-theme", options)?;
    zip.write_all(&palette[..])?;

    if let Some(wallpaper) = wallpaper {
      zip.start_file(wallpaper.get_filename(), options)?;
      zip.write_all(&wallpaper.bytes[..])?;
    }
  }

  Ok(buffer)
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn serializes_palettes_correctly() {
    assert_eq!(
      serialize_palette(&indexmap! {
        "windowBg".to_string() => Value::Color([0x10, 0x20, 0x30, 0x40]),
        "windowBoldFg".to_string() => Value::Link("windowFg".to_string()),
      }),
      b"windowBg: #10203040;
windowBoldFg: windowFg;
"
      .to_vec(),
    );
  }

  #[test]
  fn serializes_theme() {
    use std::fs::{read, write};

    let contents = read("./tests/assets/cortana.tdesktop-theme").unwrap();
    let theme = TdesktopTheme::from_bytes(&contents[..]).unwrap();

    let variables: Variables = theme
      .into_iter()
      .map(|(variable, value)| (variable.clone(), value.clone()))
      .collect();

    let theme = serialize_theme(&variables, &theme.wallpaper).unwrap();

    assert_ne!(theme, vec![]);

    // One has to manually check that Telegram recognizes this theme.
    write("./tests/ignore/for-validity-check.tdesktop-theme", theme).unwrap();
  }
}
