use anyhow::{Context, anyhow, bail};
use std::str;

use crate::term::{
  key::{
    Key, KeyCode, KeyEventKind, KeyEventState, KeyMods, MediaKeyCode,
    ModKeyCode,
  },
  mouse::{MouseButton, MouseEvent, MouseEventKind},
};

use super::internal::InternalTermEvent as E;

pub struct InputParser {
  buf: Vec<u8>,
  #[cfg(windows)]
  pub windows_mouse_buttons: u32,
}

impl InputParser {
  pub fn new() -> Self {
    Self {
      buf: Vec::new(),
      #[cfg(windows)]
      windows_mouse_buttons: 0,
    }
  }

  pub fn parse_input<F>(
    &mut self,
    input: &[u8],
    is_raw_mode: bool,
    more: bool,
    mut f: F,
  ) where
    F: FnMut(E),
  {
    let mut i = 0;
    let mut consumed = 0;

    use crate::term::key::KeyMods as Mods;

    self.buf.extend_from_slice(input);
    let buf = &self.buf;

    while i < buf.len() {
      let next_char = buf[i];
      i += 1;
      match next_char {
        b'\x1B' => {
          if i >= buf.len() {
            if more {
              break;
            } else {
              f(E::Key(Key::new(KeyCode::Esc, Mods::NONE)));
              consumed = i;
              continue;
            }
          }
          let next_char = buf[i];
          i += 1;
          match next_char {
            b'O' => {
              if i >= buf.len() {
                break;
              }
              let next_char = buf[i];
              i += 1;
              match next_char {
                b'A' => {
                  consumed = i;
                  f(E::Key(Key::new(KeyCode::Up, Mods::NONE)));
                }
                b'B' => {
                  consumed = i;
                  f(E::Key(Key::new(KeyCode::Down, Mods::NONE)));
                }
                b'C' => {
                  consumed = i;
                  f(E::Key(Key::new(KeyCode::Right, Mods::NONE)));
                }
                b'D' => {
                  consumed = i;
                  f(E::Key(Key::new(KeyCode::Left, Mods::NONE)));
                }
                b'F' => {
                  consumed = i;
                  f(E::Key(Key::new(KeyCode::End, Mods::NONE)));
                }
                b'H' => {
                  consumed = i;
                  f(E::Key(Key::new(KeyCode::Home, Mods::NONE)));
                }
                val @ b'P'..=b'S' => {
                  consumed = i;
                  f(E::Key(Key::new(KeyCode::F(1 + val - b'P'), Mods::NONE)));
                }
                _ => {
                  consumed = i;
                  log::debug!("Input parsing error.");
                }
              }
            }
            b'[' => {
              if let Some(b'M') = buf.get(i) {
                // ESC [ M CB Cx Cy
                // http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
                i += 1; // M
                if i + 3 >= buf.len() {
                  break;
                }
                let b = buf[i].saturating_sub(32);
                let x = u16::from(buf[i + 1].saturating_sub(32)) - 1;
                let y = u16::from(buf[i + 2].saturating_sub(32)) - 1;
                i += 3;
                consumed = i;
                match parse_cb(b) {
                  Ok((kind, mods)) => {
                    f(E::Mouse(MouseEvent {
                      kind,
                      mods,
                      x: x as i32,
                      y: y as i32,
                    }));
                  }
                  Err(err) => {
                    log::debug!("'CSI M bxy' error: {}", err);
                  }
                }
              } else {
                // Most of CSI
                let len = if let Some(len) =
                  parse_csi(&buf[i..], is_raw_mode, &mut f)
                {
                  len
                } else {
                  // Not enough input to complete CSI sequence.
                  break;
                };
                i += len;
                consumed = i;
              }
            }
            b'\x1B' => {
              f(E::Key(Key::new(KeyCode::Esc, Mods::NONE)));
              consumed = i;
            }
            c => {
              match parse_char_key(c, &buf[i - 1..]) {
                Ok((used, key)) => {
                  i += used;
                  if let Some(mut key) = key {
                    key.mods |= Mods::ALT;
                    f(E::Key(key));
                  }
                }
                Err(err) => {
                  log::debug!("Parse esc-char key error: {}", err);
                }
              }
              consumed = i;
            }
          }
        }
        c => {
          match parse_char_key(c, &buf[i - 1..]) {
            Ok((used, key)) => {
              i += used;
              if let Some(key) = key {
                f(E::Key(key));
              }
            }
            Err(err) => {
              log::debug!("Parse char key error: {}", err);
            }
          }
          consumed = i;
        }
      }
    }

    let buf_len = self.buf.len();
    self.buf.copy_within(consumed..buf_len, 0);
    self.buf.truncate(buf_len - consumed);
  }
}

fn parse_char_key(c: u8, buf: &[u8]) -> anyhow::Result<(usize, Option<Key>)> {
  use crate::term::key::KeyMods as Mods;

  let mut i = 0;
  let key = match c {
    b'\r' => Key::new(KeyCode::Enter, Mods::NONE),
    b'\n' => Key::new(KeyCode::Enter, Mods::NONE),
    b'\t' => Key::new(KeyCode::Tab, Mods::NONE),
    b'\x7F' => Key::new(KeyCode::Backspace, Mods::NONE),
    c @ b'\x01'..=b'\x1A' => {
      Key::new(KeyCode::Char((c - 0x1 + b'a') as char), Mods::CONTROL)
    }
    c @ b'\x1C'..=b'\x1F' => {
      Key::new(KeyCode::Char((c - 0x1C + b'4') as char), Mods::CONTROL)
    }
    b'\0' => Key::new(KeyCode::Char(' '), Mods::CONTROL),
    first_byte => {
      let char_len = utf8_char_len(first_byte);
      if char_len == 0 {
        // Ignore invalid byte.
        return Ok((0, None));
      } else if char_len - 1 <= buf.len() {
        let char = str::from_utf8(&buf[..char_len]);
        i = char_len - 1;
        match char {
          Ok(s) => {
            let char = s.chars().next().unwrap();
            Key::new(KeyCode::Char(char), Mods::NONE)
          }
          Err(_) => {
            bail!("Invalid utf-8 char");
          }
        }
      } else {
        bail!("Not enough bytes");
      }
    }
  };
  Ok((i, Some(key)))
}

/// Returns `None` is there is not enough input.
fn parse_csi<F>(buf: &[u8], is_raw_mode: bool, f: F) -> Option<usize>
where
  F: FnMut(E),
{
  let mut i = 0;
  while i < buf.len() && (0x30..=0x3f).contains(&buf[i]) {
    i += 1;
  }
  let params = &buf[..i];
  while i < buf.len() && (0x20..=0x2f).contains(&buf[i]) {
    i += 1;
  }
  let intermediates = &buf[params.len()..i];

  let final_ = if i < buf.len() {
    if (0x40..=0x7E).contains(&buf[i]) {
      i += 1;
      buf[i - 1]
    } else {
      log::debug!("CSI is incomplete. Ignoring.");
      return Some(i);
    }
  } else {
    // Not enough input to complete CSI sequence.
    return None;
  };

  match parse_csi_impl(buf, params, intermediates, final_, is_raw_mode, f) {
    Ok(()) => (),
    Err(err) => log::debug!("CSI error: {}", err),
  }

  Some(i)
}

fn parse_csi_impl<F>(
  buf: &[u8],
  params: &[u8],
  intermediates: &[u8],
  final_: u8,
  is_raw_mode: bool,
  mut f: F,
) -> anyhow::Result<()>
where
  F: FnMut(E),
{
  match final_ {
    b'A' | b'B' | b'C' | b'D' | b'F' | b'H' | b'P' | b'Q' | b'S' => {
      let code = match final_ {
        b'A' => KeyCode::Up,
        b'B' => KeyCode::Down,
        b'C' => KeyCode::Right,
        b'D' => KeyCode::Left,
        b'F' => KeyCode::End,
        b'H' => KeyCode::Home,
        b'P' => KeyCode::F(1),
        b'Q' => KeyCode::F(2),
        b'S' => KeyCode::F(4),
        _ => unreachable!("Unhanled CSI [A|B|C|...] key"),
      };

      let params = str::from_utf8(params)?;
      let mut params = params.split(';');

      params.next();

      let (mods, kind) = if let Some(mods_param) = params.next() {
        let mut mods_param = mods_param.split(':');
        let mods = parse_modifiers(
          mods_param.next().unwrap_or("0").parse::<u8>().unwrap_or(0),
        );
        let kind = parse_key_event_kind(
          mods_param.next().unwrap_or("").parse().unwrap_or(0),
        );
        (mods, kind)
      } else {
        (KeyMods::NONE, KeyEventKind::Press)
      };

      f(E::Key(Key::new_with_kind(code, mods, kind)));
    }
    b'c' if params.starts_with(b"?") => {
      f(E::PrimaryDeviceAttributes);
    }
    b'Z' => {
      f(E::Key(Key::new(KeyCode::Tab, KeyMods::SHIFT)));
    }
    b'I' => {
      f(E::FocusGained);
    }
    b'O' => {
      f(E::FocusLost);
    }
    b'u' => {
      // Kitty keyboard protocol reply.
      if params.starts_with(b"?") {
        if let Some(flags_char) = params.get(1) {
          let flags = flags_char.wrapping_sub(b'0');
          f(E::ReplyKittyKeyboard(flags));
        }
      } else {
        // CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u

        let (code, alt_code, mods_param, kind) =
          parse_csi_u(params).context("parse_csi_u")?;

        let mut mods = parse_modifiers(mods_param);
        let kind = parse_key_event_kind(kind);
        let state_from_mods = parse_modifiers_to_state(mods_param);

        let (mut keycode, state_from_keycode) = {
          if let Some((special_key_code, state)) =
            translate_functional_key_code(code)
          {
            (special_key_code, state)
          } else if let Some(c) = char::from_u32(code) {
            (
              match c {
                '\x1B' => KeyCode::Esc,
                '\r' => KeyCode::Enter,
                // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get
                // newlines as input is because the terminal converts \r into \n for us. When we
                // enter raw mode, we disable that, so \n no longer has any meaning - it's better to
                // use Ctrl+J. Waiting to handle it here means it gets picked up later
                '\n' if !is_raw_mode => KeyCode::Enter,
                '\t' => KeyCode::Tab,
                '\x7F' => KeyCode::Backspace,
                _ => KeyCode::Char(c),
              },
              KeyEventState::empty(),
            )
          } else {
            bail!(
              "Unhandled CSI-u {:?} {:?} {}",
              params,
              intermediates,
              final_
            );
          }
        };

        if let KeyCode::Modifier(modifier_keycode) = keycode {
          match modifier_keycode {
            ModKeyCode::LeftAlt | ModKeyCode::RightAlt => {
              mods.set(KeyMods::ALT, true)
            }
            ModKeyCode::LeftControl | ModKeyCode::RightControl => {
              mods.set(KeyMods::CONTROL, true)
            }
            ModKeyCode::LeftShift | ModKeyCode::RightShift => {
              mods.set(KeyMods::SHIFT, true)
            }
            ModKeyCode::LeftSuper | ModKeyCode::RightSuper => {
              mods.set(KeyMods::SUPER, true)
            }
            ModKeyCode::LeftHyper | ModKeyCode::RightHyper => {
              mods.set(KeyMods::HYPER, true)
            }
            ModKeyCode::LeftMeta | ModKeyCode::RightMeta => {
              mods.set(KeyMods::META, true)
            }
            _ => {}
          }
        }

        // When the "report alternate keys" flag is enabled in the Kitty Keyboard Protocol
        // and the terminal sends a keyboard event containing shift, the sequence will
        // contain an additional codepoint separated by a ':' character which contains
        // the shifted character according to the keyboard layout.
        if mods.contains(KeyMods::SHIFT) {
          if let Some(shifted_c) = alt_code.and_then(char::from_u32) {
            keycode = KeyCode::Char(shifted_c);
            mods.set(KeyMods::SHIFT, false);
          }
        }

        let key_event = Key {
          code: keycode,
          mods,
          kind,
          state: state_from_keycode | state_from_mods,
        };

        f(E::Key(key_event));
      }
    }
    b'm' | b'M' if params.starts_with(b"<") => {
      // SGR mouse
      // CSI < Cb ; Cx ; Cy (;) (M or m)
      let params_str = str::from_utf8(params)?;
      let mut params = params_str[1..].split(';');

      let b = params.next().ok_or_else(|| anyhow!("No Cb param"))?;
      let b = b.parse::<u8>()?;
      let (kind, mods) = parse_cb(b)?;
      let kind = if final_ == b'm' {
        match kind {
          MouseEventKind::Down(button) => MouseEventKind::Up(button),
          other => other,
        }
      } else {
        kind
      };

      let x = params.next().ok_or_else(|| anyhow!("No Cx param"))?;
      let x = x.parse::<u16>()? - 1;
      let y = params.next().ok_or_else(|| anyhow!("No Cy param"))?;
      let y = y.parse::<u16>()? - 1;

      f(E::Mouse(MouseEvent {
        kind,
        mods,
        x: x as i32,
        y: y as i32,
      }));
    }
    b'M' => {
      // rxvt mouse encoding:
      // CSI Cb ; Cx ; Cy ; M
      let params_str = str::from_utf8(params)?;
      let mut params = params_str.split(';');

      let b = params.next().ok_or_else(|| anyhow!("No rxvt Cb param"))?;
      let b = b.parse::<u8>()?.saturating_sub(32);
      let (kind, mods) = parse_cb(b)?;

      let x = params.next().ok_or_else(|| anyhow!("No rxvt Cx param"))?;
      let x = x.parse::<u16>()? - 1;
      let y = params.next().ok_or_else(|| anyhow!("No rxvt Cy param"))?;
      let y = y.parse::<u16>()? - 1;

      f(E::Mouse(MouseEvent {
        kind,
        mods,
        x: x as i32,
        y: y as i32,
      }));
    }
    b'R' => {
      // CSI Cy ; Cx R
      let params_str = str::from_utf8(params)?;
      let mut params = params_str.split(';');

      let y = params.next().ok_or_else(|| anyhow!("No CSI-R Cy param"))?;
      let y = y.parse::<u16>()?.saturating_sub(1);
      let x = params.next().ok_or_else(|| anyhow!("No CSI-R Cx param"))?;
      let x = x.parse::<u16>()?.saturating_sub(1);

      f(E::CursorPos(x, y));
    }
    b'~' => {
      let params_str = str::from_utf8(params)?;
      let mut params = params_str.split(';');

      let first = params
        .next()
        .ok_or_else(|| anyhow!("No key param in CSI ~"))?
        .parse::<u8>()?;

      let mods_param = params.next();
      let (mods, state) = if let Some(mods_param) = mods_param {
        let mask = mods_param.parse::<u8>()?;
        (parse_modifiers(mask), parse_modifiers_to_state(mask))
      } else {
        (KeyMods::NONE, KeyEventState::NONE)
      };

      if first == 27 {
        // modifyOtherKeys

        let code_param = params.next();
        let code = if let Some(code_param) = code_param {
          let code = code_param.parse::<u8>()?;
          match code {
            8 | 0x7f => KeyCode::Backspace,
            0x1b => KeyCode::Esc,
            9 => KeyCode::Tab,
            10 | 13 => KeyCode::Enter,
            _ => KeyCode::Char(code as char),
          }
        } else {
          bail!("Empty code in modifyOtherKeys")
        };

        f(E::Key(Key {
          code,
          mods,
          kind: KeyEventKind::Press,
          state,
        }));
      } else {
        let code = first;
        let code = match code {
          1 | 7 => KeyCode::Home,
          2 => KeyCode::Insert,
          3 => KeyCode::Delete,
          4 | 8 => KeyCode::End,
          5 => KeyCode::PageUp,
          6 => KeyCode::PageDown,
          v @ 11..=15 => KeyCode::F(v - 10),
          v @ 17..=21 => KeyCode::F(v - 11),
          v @ 23..=26 => KeyCode::F(v - 12),
          v @ 28..=29 => KeyCode::F(v - 15),
          v @ 31..=34 => KeyCode::F(v - 17),
          v => bail!("Wrong key param ({}) in CSI ~", v),
        };

        f(E::Key(Key {
          code,
          mods,
          kind: KeyEventKind::Press,
          state,
        }));
      }
    }
    _ => {
      log::debug!(
        "Unknown CSI: {} ({:?}, {:?}, {})",
        str::from_utf8(buf).unwrap_or("???"),
        params,
        intermediates,
        final_
      );
    }
  }
  Ok(())
}

fn parse_csi_u(params: &[u8]) -> anyhow::Result<(u32, Option<u32>, u8, u8)> {
  let params = str::from_utf8(params)?;
  let mut params = params.split(';');

  let code_param = params.next().unwrap_or("");
  let mut code_param = code_param.split(':');
  let code = code_param.next().unwrap_or("0");
  let code = code.parse::<u32>()?;

  let alt_code = code_param.next().map(|c| c.parse::<u32>().ok()).flatten();

  let mods_param = params.next().unwrap_or("0");
  let mut mods_param = mods_param.split(':');
  let mods = mods_param.next().unwrap_or("0");
  let mods = mods.parse::<u8>().unwrap_or(0);
  let kind = mods_param.next().unwrap_or("1").parse::<u8>().unwrap_or(1);

  Ok((code, alt_code, mods, kind))
}

fn parse_modifiers(mask: u8) -> KeyMods {
  let modifier_mask = mask.saturating_sub(1);
  let mut modifiers = KeyMods::empty();
  if modifier_mask & 1 != 0 {
    modifiers |= KeyMods::SHIFT;
  }
  if modifier_mask & 2 != 0 {
    modifiers |= KeyMods::ALT;
  }
  if modifier_mask & 4 != 0 {
    modifiers |= KeyMods::CONTROL;
  }
  if modifier_mask & 8 != 0 {
    modifiers |= KeyMods::SUPER;
  }
  if modifier_mask & 16 != 0 {
    modifiers |= KeyMods::HYPER;
  }
  if modifier_mask & 32 != 0 {
    modifiers |= KeyMods::META;
  }
  modifiers
}

fn parse_modifiers_to_state(mask: u8) -> KeyEventState {
  let modifier_mask = mask.saturating_sub(1);
  let mut state = KeyEventState::empty();
  if modifier_mask & 64 != 0 {
    state |= KeyEventState::CAPS_LOCK;
  }
  if modifier_mask & 128 != 0 {
    state |= KeyEventState::NUM_LOCK;
  }
  state
}

fn parse_key_event_kind(kind: u8) -> KeyEventKind {
  match kind {
    1 => KeyEventKind::Press,
    2 => KeyEventKind::Repeat,
    3 => KeyEventKind::Release,
    _ => KeyEventKind::Press,
  }
}

fn translate_functional_key_code(
  codepoint: u32,
) -> Option<(KeyCode, KeyEventState)> {
  if let Some(keycode) = match codepoint {
    57399 => Some(KeyCode::Char('0')),
    57400 => Some(KeyCode::Char('1')),
    57401 => Some(KeyCode::Char('2')),
    57402 => Some(KeyCode::Char('3')),
    57403 => Some(KeyCode::Char('4')),
    57404 => Some(KeyCode::Char('5')),
    57405 => Some(KeyCode::Char('6')),
    57406 => Some(KeyCode::Char('7')),
    57407 => Some(KeyCode::Char('8')),
    57408 => Some(KeyCode::Char('9')),
    57409 => Some(KeyCode::Char('.')),
    57410 => Some(KeyCode::Char('/')),
    57411 => Some(KeyCode::Char('*')),
    57412 => Some(KeyCode::Char('-')),
    57413 => Some(KeyCode::Char('+')),
    57414 => Some(KeyCode::Enter),
    57415 => Some(KeyCode::Char('=')),
    57416 => Some(KeyCode::Char(',')),
    57417 => Some(KeyCode::Left),
    57418 => Some(KeyCode::Right),
    57419 => Some(KeyCode::Up),
    57420 => Some(KeyCode::Down),
    57421 => Some(KeyCode::PageUp),
    57422 => Some(KeyCode::PageDown),
    57423 => Some(KeyCode::Home),
    57424 => Some(KeyCode::End),
    57425 => Some(KeyCode::Insert),
    57426 => Some(KeyCode::Delete),
    57427 => Some(KeyCode::KeypadBegin),
    _ => None,
  } {
    return Some((keycode, KeyEventState::KEYPAD));
  }

  if let Some(keycode) = match codepoint {
    57358 => Some(KeyCode::CapsLock),
    57359 => Some(KeyCode::ScrollLock),
    57360 => Some(KeyCode::NumLock),
    57361 => Some(KeyCode::PrintScreen),
    57362 => Some(KeyCode::Pause),
    57363 => Some(KeyCode::Menu),
    57376 => Some(KeyCode::F(13)),
    57377 => Some(KeyCode::F(14)),
    57378 => Some(KeyCode::F(15)),
    57379 => Some(KeyCode::F(16)),
    57380 => Some(KeyCode::F(17)),
    57381 => Some(KeyCode::F(18)),
    57382 => Some(KeyCode::F(19)),
    57383 => Some(KeyCode::F(20)),
    57384 => Some(KeyCode::F(21)),
    57385 => Some(KeyCode::F(22)),
    57386 => Some(KeyCode::F(23)),
    57387 => Some(KeyCode::F(24)),
    57388 => Some(KeyCode::F(25)),
    57389 => Some(KeyCode::F(26)),
    57390 => Some(KeyCode::F(27)),
    57391 => Some(KeyCode::F(28)),
    57392 => Some(KeyCode::F(29)),
    57393 => Some(KeyCode::F(30)),
    57394 => Some(KeyCode::F(31)),
    57395 => Some(KeyCode::F(32)),
    57396 => Some(KeyCode::F(33)),
    57397 => Some(KeyCode::F(34)),
    57398 => Some(KeyCode::F(35)),
    57428 => Some(KeyCode::Media(MediaKeyCode::Play)),
    57429 => Some(KeyCode::Media(MediaKeyCode::Pause)),
    57430 => Some(KeyCode::Media(MediaKeyCode::PlayPause)),
    57431 => Some(KeyCode::Media(MediaKeyCode::Reverse)),
    57432 => Some(KeyCode::Media(MediaKeyCode::Stop)),
    57433 => Some(KeyCode::Media(MediaKeyCode::FastForward)),
    57434 => Some(KeyCode::Media(MediaKeyCode::Rewind)),
    57435 => Some(KeyCode::Media(MediaKeyCode::Next)),
    57436 => Some(KeyCode::Media(MediaKeyCode::Prev)),
    57437 => Some(KeyCode::Media(MediaKeyCode::Record)),
    57438 => Some(KeyCode::Media(MediaKeyCode::VolumeDown)),
    57439 => Some(KeyCode::Media(MediaKeyCode::VolumeUp)),
    57440 => Some(KeyCode::Media(MediaKeyCode::VolumeMute)),
    57441 => Some(KeyCode::Modifier(ModKeyCode::LeftShift)),
    57442 => Some(KeyCode::Modifier(ModKeyCode::LeftControl)),
    57443 => Some(KeyCode::Modifier(ModKeyCode::LeftAlt)),
    57444 => Some(KeyCode::Modifier(ModKeyCode::LeftSuper)),
    57445 => Some(KeyCode::Modifier(ModKeyCode::LeftHyper)),
    57446 => Some(KeyCode::Modifier(ModKeyCode::LeftMeta)),
    57447 => Some(KeyCode::Modifier(ModKeyCode::RightShift)),
    57448 => Some(KeyCode::Modifier(ModKeyCode::RightControl)),
    57449 => Some(KeyCode::Modifier(ModKeyCode::RightAlt)),
    57450 => Some(KeyCode::Modifier(ModKeyCode::RightSuper)),
    57451 => Some(KeyCode::Modifier(ModKeyCode::RightHyper)),
    57452 => Some(KeyCode::Modifier(ModKeyCode::RightMeta)),
    // 57453 => Some(KeyCode::Modifier(ModKeyCode::IsoLevel3Shift)),
    // 57454 => Some(KeyCode::Modifier(ModKeyCode::IsoLevel5Shift)),
    _ => None,
  } {
    return Some((keycode, KeyEventState::empty()));
  }

  None
}

/// Cb is the byte of a mouse input that contains the button being used, the key modifiers being
/// held and whether the mouse is dragging or not.
///
/// Bit layout of cb, from low to high:
///
/// - button number
/// - button number
/// - shift
/// - meta (alt)
/// - control
/// - mouse is dragging
/// - button number
/// - button number
fn parse_cb(cb: u8) -> anyhow::Result<(MouseEventKind, KeyMods)> {
  let button_number = (cb & 0b0000_0011) | ((cb & 0b1100_0000) >> 4);
  let dragging = cb & 0b0010_0000 == 0b0010_0000;

  let kind = match (button_number, dragging) {
    (0, false) => MouseEventKind::Down(MouseButton::Left),
    (1, false) => MouseEventKind::Down(MouseButton::Middle),
    (2, false) => MouseEventKind::Down(MouseButton::Right),
    (0, true) => MouseEventKind::Drag(MouseButton::Left),
    (1, true) => MouseEventKind::Drag(MouseButton::Middle),
    (2, true) => MouseEventKind::Drag(MouseButton::Right),
    (3, false) => MouseEventKind::Up(MouseButton::Left),
    (3, true) | (4, true) | (5, true) => MouseEventKind::Moved,
    (4, false) => MouseEventKind::ScrollUp,
    (5, false) => MouseEventKind::ScrollDown,
    (6, false) => MouseEventKind::ScrollLeft,
    (7, false) => MouseEventKind::ScrollRight,
    // We do not support other buttons.
    _ => bail!("Failed to parse Cb param button"),
  };

  let mut modifiers = KeyMods::empty();

  if cb & 0b0000_0100 == 0b0000_0100 {
    modifiers |= KeyMods::SHIFT;
  }
  if cb & 0b0000_1000 == 0b0000_1000 {
    modifiers |= KeyMods::ALT;
  }
  if cb & 0b0001_0000 == 0b0001_0000 {
    modifiers |= KeyMods::CONTROL;
  }

  Ok((kind, modifiers))
}

fn utf8_char_len(first_byte: u8) -> usize {
  match first_byte {
    // https://en.wikipedia.org/wiki/UTF-8#Description
    (0x00..=0x7F) => 1, // 0xxxxxxx
    (0xC0..=0xDF) => 2, // 110xxxxx 10xxxxxx
    (0xE0..=0xEF) => 3, // 1110xxxx 10xxxxxx 10xxxxxx
    (0xF0..=0xF7) => 4, // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    (0x80..=0xBF) | (0xF8..=0xFF) => 0,
  }
}
