use super::Cell;

#[derive(Clone, Debug)]
pub struct Row {
  pub cells: Vec<super::cell::Cell>,
  size: u16,
  wrapped: bool,
}

impl Row {
  pub fn new(cols: u16) -> Self {
    Self {
      cells: vec![super::cell::Cell::default(); usize::from(cols)],
      size: 0,
      wrapped: false,
    }
  }

  pub fn new_with_attrs(cols: u16, attrs: super::attrs::Attrs) -> Self {
    let mut cell = super::cell::Cell::default();
    cell.set_attrs(attrs);
    Self {
      cells: vec![cell; usize::from(cols)],
      size: 0,
      wrapped: false,
    }
  }

  pub fn cols(&self) -> u16 {
    self
      .cells
      .len()
      .try_into()
      // we limit the number of cols to a u16 (see Size)
      .unwrap()
  }

  pub fn clear(&mut self, attrs: super::attrs::Attrs) {
    for cell in &mut self.cells {
      cell.clear(attrs);
    }
    self.size = 0;
    self.wrapped = false;
  }

  fn cells(&self) -> impl Iterator<Item = &super::cell::Cell> {
    self.cells.iter()
  }

  pub fn get(&self, col: u16) -> Option<&super::cell::Cell> {
    self.cells.get(usize::from(col))
  }

  pub fn get_mut(&mut self, col: u16) -> Option<&mut super::cell::Cell> {
    self.size = self.size.max(col + 1);
    self.cells.get_mut(usize::from(col))
  }

  pub fn insert(&mut self, i: u16, cell: super::cell::Cell) {
    self.cells.insert(usize::from(i), cell);
    self.wrapped = false;
  }

  pub fn remove(&mut self, i: u16) {
    self.clear_wide(i);
    self.cells.remove(usize::from(i));
    self.wrapped = false;
  }

  pub fn erase(&mut self, i: u16, attrs: super::attrs::Attrs) {
    let wide = self.cells[usize::from(i)].is_wide();
    self.clear_wide(i);
    self.cells[usize::from(i)].clear(attrs);
    if i == self.cols() - if wide { 2 } else { 1 } {
      self.wrapped = false;
    }
  }

  pub fn truncate(&mut self, len: u16) {
    self.cells.truncate(usize::from(len));
    self.wrapped = false;
    let last_cell = &mut self.cells[usize::from(len) - 1];
    if last_cell.is_wide() {
      last_cell.clear(*last_cell.attrs());
    }
  }

  pub fn resize(&mut self, len: u16, cell: super::cell::Cell) {
    self.cells.resize(usize::from(len), cell);
    self.wrapped = false;
  }

  pub fn wrap(&mut self, wrap: bool) {
    self.wrapped = wrap;
  }

  pub fn wrapped(&self) -> bool {
    self.wrapped
  }

  pub fn clear_wide(&mut self, col: u16) {
    let cell = &self.cells[usize::from(col)];
    let other = if cell.is_wide() {
      self.cells.get_mut(usize::from(col + 1))
    } else if self.is_wide_continuation(col) {
      self.cells.get_mut(usize::from(col - 1))
    } else {
      return;
    };
    if let Some(other) = other {
      other.clear(*other.attrs());
    }
  }

  pub fn take_cells(&self, vec: &mut Vec<Cell>) {
    vec.extend(self.cells.iter().take(self.size.into()).cloned());
  }

  pub fn write_contents(
    &self,
    contents: &mut String,
    start: u16,
    width: u16,
    wrapping: bool,
  ) {
    let mut prev_was_wide = false;

    let mut prev_col = start;
    for (col, cell) in self
      .cells()
      .enumerate()
      .skip(usize::from(start))
      .take(usize::from(width))
    {
      if prev_was_wide {
        prev_was_wide = false;
        continue;
      }
      prev_was_wide = cell.is_wide();

      // we limit the number of cols to a u16 (see Size)
      let col: u16 = col.try_into().unwrap();
      if cell.has_contents() {
        for _ in 0..(col - prev_col) {
          contents.push(' ');
        }
        prev_col += col - prev_col;

        contents.push_str(cell.contents());
        prev_col += if cell.is_wide() { 2 } else { 1 };
      }
    }
    if prev_col == start && wrapping {
      contents.push('\n');
    }
  }

  pub(crate) fn is_wide_continuation(&self, col: u16) -> bool {
    if col == 0 {
      return false;
    }

    self
      .cells
      .get(Into::<usize>::into(col) - 1)
      .is_some_and(super::cell::Cell::is_wide)
  }
}
