use crate::midi::messages::Message;
use crate::midi::types::{U14, U4, U7};

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DecodedMessage {
  Message(Message),

  /// This message type allows manufacturers to create their own messages (such
  /// as bulk dumps, patch parameters, and other non-spec data) and provides a mechanism for
  /// creating additional MIDI Specification messages.
  SysEx {
    data: Vec<U7>,
  },

  /// Unknown data. Used by the decoder when the data can not be decoded.
  Unknown(Vec<u8>), // TODO that should be a pointer to a vector
}

impl From<Message> for DecodedMessage {
  fn from(msg: Message) -> DecodedMessage {
    DecodedMessage::Message(msg)
  }
}

pub struct Decoder<'a> {
  pos: usize,
  start: usize,
  sysex_data: Vec<U7>,
  sysex_decoding: bool,
  data: &'a [u8],
}

impl<'a> Decoder<'a> {
  pub fn new(data: &'a [u8]) -> Decoder<'a> {
    Decoder {
      pos: 0,
      start: 0,
      sysex_data: Vec::new(),
      sysex_decoding: false,
      data,
    }
  }

  fn unknown(&self, end: usize) -> DecodedMessage {
    DecodedMessage::Unknown(self.data[self.start..end].to_vec())
  }

  fn next_data(&mut self) -> Result<U7, usize> {
    if self.pos < self.data.len() {
      let d1 = self.data[self.pos];
      if d1 & 0b1000_0000 == 0 {
        self.pos += 1;
        Ok(d1)
      } else {
        Err(self.pos)
      }
    } else {
      Err(self.pos)
    }
  }

  fn next_data2(&mut self) -> Result<(U7, U7), usize> {
    self
      .next_data()
      .and_then(|d1| self.next_data().and_then(|d2| Ok((d1, d2))))
  }

  fn decode_note(&mut self, channel: U4, is_on: bool) -> DecodedMessage {
    match self.next_data2() {
      Ok((key, velocity)) => {
        if is_on {
          Message::NoteOn {
            channel,
            key,
            velocity,
          }
          .into()
        } else {
          Message::NoteOff {
            channel,
            key,
            velocity,
          }
          .into()
        }
      }
      Err(end) => self.unknown(end),
    }
  }

  fn decode_polyphonic_key_pressure(&mut self, channel: U4) -> DecodedMessage {
    match self.next_data2() {
      Ok((key, pressure)) => Message::PolyphonicKeyPressure {
        channel,
        key,
        value: pressure,
      }
      .into(),
      Err(end) => self.unknown(end),
    }
  }

  fn decode_control_change(&mut self, channel: U4) -> DecodedMessage {
    match self.next_data2() {
      Ok((controller, value)) => match controller {
        120 => match value {
          0 => Message::AllSoundOff { channel }.into(),
          _ => self.unknown(self.pos),
        },
        121 => Message::ResetAllControllers { channel }.into(),
        122 => match value {
          0 => Message::LocalControlOff { channel }.into(),
          127 => Message::LocalControlOn { channel }.into(),
          _ => self.unknown(self.pos),
        },
        123 => match value {
          0 => Message::AllNotesOff { channel }.into(),
          _ => self.unknown(self.pos),
        },
        124 => match value {
          0 => Message::OmniModeOff { channel }.into(),
          _ => self.unknown(self.pos),
        },
        125 => match value {
          0 => Message::OmniModeOn { channel }.into(),
          _ => self.unknown(self.pos),
        },
        126 => Message::MonoModeOn {
          channel,
          num_channels: value,
        }
        .into(),
        127 => match value {
          0 => Message::PolyModeOn { channel }.into(),
          _ => self.unknown(self.pos),
        },
        _ => Message::ControlChange {
          channel,
          controller,
          value,
        }
        .into(),
      },
      Err(end) => self.unknown(end),
    }
  }

  fn decode_program_change(&mut self, channel: U4) -> DecodedMessage {
    match self.next_data() {
      Ok(program) => Message::ProgramChange {
        channel,
        value: program,
      }
      .into(),
      Err(end) => self.unknown(end),
    }
  }

  fn decode_channel_pressure(&mut self, channel: U4) -> DecodedMessage {
    match self.next_data() {
      Ok(pressure) => Message::ChannelPressure {
        channel,
        value: pressure,
      }
      .into(),
      Err(end) => self.unknown(end),
    }
  }

  fn decode_pitch_bend(&mut self, channel: U4) -> DecodedMessage {
    match self.next_data2() {
      Ok((lsb, msb)) => Message::PitchBend {
        channel,
        value: (U14::from(msb) << 7) | U14::from(lsb),
      }
      .into(),
      Err(end) => self.unknown(end),
    }
  }

  fn decode_mtc_quarter_frame(&mut self) -> DecodedMessage {
    match self.next_data() {
      Ok(data) => Message::MTCQuarterFrame {
        msg_type: (data >> 4) & 0x07,
        value: data & 0x0f,
      }
      .into(),
      Err(end) => self.unknown(end),
    }
  }

  fn decode_song_position_pointer(&mut self) -> DecodedMessage {
    match self.next_data2() {
      Ok((lsb, msb)) => Message::SongPositionPointer {
        beats: (U14::from(msb) << 7) | U14::from(lsb),
      }
      .into(),
      Err(end) => self.unknown(end),
    }
  }

  fn decode_song_select(&mut self) -> DecodedMessage {
    match self.next_data() {
      Ok(song) => Message::SongSelect { song }.into(),
      Err(end) => self.unknown(end),
    }
  }

  fn decode_sysex_start(&mut self) -> Option<DecodedMessage> {
    if !self.sysex_decoding {
      self.sysex_decoding = true;
      self.decode_sysex_data()
    } else {
      Some(self.unknown(self.pos))
    }
  }

  fn decode_sysex_end(&mut self) -> Option<DecodedMessage> {
    if self.sysex_decoding {
      self.sysex_decoding = false;
      let data = self.sysex_data.to_owned();
      self.sysex_data = Vec::new();
      Some(DecodedMessage::SysEx { data })
    } else {
      Some(self.unknown(self.pos))
    }
  }

  fn decode_sysex_data(&mut self) -> Option<DecodedMessage> {
    let start = self.pos;
    let mut pos = start;
    while pos < self.data.len() && (self.data[pos] & 0x80) == 0 {
      pos += 1
    }
    self.pos = pos;
    let data = &self.data[start..pos];
    self.sysex_data.extend(data);
    if pos < self.data.len() {
      let status = self.data[pos];
      self.start = self.pos;
      self.pos += 1;
      self.decode(status)
    } else {
      self.sysex_decoding = false;
      let mut data = self.sysex_data.to_owned();
      self.sysex_data = Vec::new();
      data.insert(0, 0b1111_0000);
      Some(DecodedMessage::Unknown(data))
    }
  }

  fn decode(&mut self, status: U7) -> Option<DecodedMessage> {
    match (status >> 4) & 0x0f {
      0b1000 => Some(self.decode_note(status & 0x0f, false)),
      0b1001 => Some(self.decode_note(status & 0x0f, true)),
      0b1010 => Some(self.decode_polyphonic_key_pressure(status & 0x0f)),
      0b1011 => Some(self.decode_control_change(status & 0x0f)),
      0b1100 => Some(self.decode_program_change(status & 0x0f)),
      0b1101 => Some(self.decode_channel_pressure(status & 0x0f)),
      0b1110 => Some(self.decode_pitch_bend(status & 0x0f)),
      0b1111 => match status & 0x0f {
        0b0000 => self.decode_sysex_start(),
        0b0001 => Some(self.decode_mtc_quarter_frame()),
        0b0010 => Some(self.decode_song_position_pointer()),
        0b0011 => Some(self.decode_song_select()),
        0b0100 => Some(self.unknown(self.pos)),
        0b0101 => Some(self.unknown(self.pos)),
        0b0110 => Some(Message::TuneRequest.into()),
        0b0111 => self.decode_sysex_end(),
        0b1000 => Some(Message::TimingClock.into()),
        0b1001 => Some(self.unknown(self.pos)),
        0b1010 => Some(Message::Start.into()),
        0b1011 => Some(Message::Continue.into()),
        0b1100 => Some(Message::Stop.into()),
        0b1101 => Some(self.unknown(self.pos)),
        0b1110 => Some(Message::ActiveSensing.into()),
        0b1111 => Some(Message::SystemReset.into()),
        _ => unreachable!(),
      },
      _ => Some(DecodedMessage::Unknown(vec![status])),
    }
  }
}

impl<'a> Iterator for Decoder<'a> {
  type Item = DecodedMessage;

  fn next(&mut self) -> Option<DecodedMessage> {
    if self.sysex_decoding {
      self.decode_sysex_data()
    } else if self.pos < self.data.len() {
      let status = self.data[self.pos];
      self.start = self.pos;
      self.pos += 1;
      self.decode(status)
    } else {
      None
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use crate::midi::messages::Message;

  #[test]
  fn decode_empty_vec() {
    let data = &Vec::new();
    let mut dec = Decoder::new(data);
    assert_eq!(dec.next(), None);
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn next_data_unknown() {
    let data = &vec![0b1000_0000, 64, 0b1000_0001, 0b1000_0010, 12];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(DecodedMessage::Unknown(vec![0b1000_0000, 64]))
    );
    assert_eq!(dec.next(), Some(DecodedMessage::Unknown(vec![0b1000_0001])));
    assert_eq!(
      dec.next(),
      Some(DecodedMessage::Unknown(vec![0b1000_0010, 12]))
    );
  }

  #[test]
  fn decode_notes() {
    let data = &vec![0b1000_0101u8, 64, 127, 0b1001_1010, 0, 127];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::NoteOff {
          channel: 0b0101,
          key: 64,
          velocity: 127
        }
        .into()
      )
    );
    assert_eq!(
      dec.next(),
      Some(
        Message::NoteOn {
          channel: 0b1010,
          key: 0,
          velocity: 127
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_polyphonic_key_pressure() {
    let data = &vec![0b1010_0101u8, 64, 127];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::PolyphonicKeyPressure {
          channel: 0b0101,
          key: 64,
          value: 127
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_control_change() {
    let data = &vec![0b1011_0101u8, 64, 127];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::ControlChange {
          channel: 0b0101,
          controller: 64,
          value: 127
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_program_change() {
    let data = &vec![0b1100_0101u8, 0b0_1010101];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::ProgramChange {
          channel: 0b0101,
          value: 0b0_1010101
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_channel_pressure() {
    let data = &vec![0b1101_0101u8, 0b0_1010101];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::ChannelPressure {
          channel: 0b0101,
          value: 0b0_1010101
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_pitch_bend() {
    let data = &vec![0b1110_0101u8, 0b0_1010101, 0b0_0101010];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::PitchBend {
          channel: 0b0101,
          value: 0b0_01010101010101
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  #[allow(clippy::inconsistent_digit_grouping)]
  fn decode_mtc_quarter_frame() {
    let data = &vec![0b1111_0001u8, 0b0_101_1010];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::MTCQuarterFrame {
          msg_type: 0b101,
          value: 0b1010
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_song_position_pointer() {
    let data = &vec![
      0b1111_0010u8,
      0b0_1010101,
      0b0_0101010,
      0b1111_0010u8,
      0b0_0101010,
      0b0_1010101,
    ];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::SongPositionPointer {
          beats: 0b01_0101_0101_0101
        }
        .into()
      )
    );
    assert_eq!(
      dec.next(),
      Some(
        Message::SongPositionPointer {
          beats: 0b10_1010_1010_1010
        }
        .into()
      )
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_song_select() {
    let data = &vec![0b1111_0011u8, 0b0_1010101];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(Message::SongSelect { song: 0b101_0101 }.into())
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_tune_request() {
    let data = &vec![0b1111_0110u8];
    let mut dec = Decoder::new(data);
    assert_eq!(dec.next(), Some(Message::TuneRequest.into()));
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_timing_clock() {
    let data = &vec![0b1111_1000u8];
    let mut dec = Decoder::new(data);
    assert_eq!(dec.next(), Some(Message::TimingClock.into()));
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_start_continue_stop() {
    let data = &vec![0b1111_1010u8, 0b1111_1011, 0b1111_1100];
    let mut dec = Decoder::new(data);
    assert_eq!(dec.next(), Some(Message::Start.into()));
    assert_eq!(dec.next(), Some(Message::Continue.into()));
    assert_eq!(dec.next(), Some(Message::Stop.into()));
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_active_sensing() {
    let data = &vec![0b1111_1110u8];
    let mut dec = Decoder::new(data);
    assert_eq!(dec.next(), Some(Message::ActiveSensing.into()));
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_system_reset() {
    let data = &vec![0b1111_1111u8];
    let mut dec = Decoder::new(data);
    assert_eq!(dec.next(), Some(Message::SystemReset.into()));
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_reserved() {
    let data = &vec![0b1111_0100u8, 0b1111_0101, 0b1111_1001, 0b1111_1101];
    let mut dec = Decoder::new(data);
    assert_eq!(dec.next(), Some(DecodedMessage::Unknown(vec![0b1111_0100])));
    assert_eq!(dec.next(), Some(DecodedMessage::Unknown(vec![0b1111_0101])));
    assert_eq!(dec.next(), Some(DecodedMessage::Unknown(vec![0b1111_1001])));
    assert_eq!(dec.next(), Some(DecodedMessage::Unknown(vec![0b1111_1101])));
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_sysex_continuous() {
    let data = &vec![0b1111_0000u8, 1, 2, 3, 4, 0b1111_0111];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(DecodedMessage::SysEx {
        data: vec![1u8, 2, 3, 4]
      })
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_sysex_interleaved() {
    let data = &vec![
      0b1111_0000u8,
      1,
      2,
      0b1000_0101u8,
      64,
      127,
      3,
      4,
      0b1111_0111,
    ];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(
        Message::NoteOff {
          channel: 0b0101,
          key: 64,
          velocity: 127
        }
        .into()
      )
    );
    assert_eq!(
      dec.next(),
      Some(DecodedMessage::SysEx {
        data: vec![1u8, 2, 3, 4]
      })
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_sysex_unexpected_end_data() {
    let data = &vec![0b1111_0000u8, 1, 2];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(DecodedMessage::Unknown(vec![0b1111_0000u8, 1, 2]))
    );
    assert_eq!(dec.next(), None);
  }

  #[test]
  fn decode_sysex_unexpected_end_interleaved() {
    let data = &vec![0b1111_0000u8, 1, 2, 0b1000_0000u8, 64];
    let mut dec = Decoder::new(data);
    assert_eq!(
      dec.next(),
      Some(DecodedMessage::Unknown(vec![0b1000_0000u8, 64]))
    );
    assert_eq!(
      dec.next(),
      Some(DecodedMessage::Unknown(vec![0b1111_0000u8, 1, 2]))
    );
    assert_eq!(dec.next(), None);
  }
}
