use std::io::{Write, Error};
use std::iter::FromIterator;
use std::slice::{Iter, IterMut};
use std::ops::{Index, IndexMut};
use super::Terminal;
use super::utils::NEWLINE;
use super::Cell;
use super::format::{TableFormat, ColumnPosition};
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Row {
    cells: Vec<Cell>,
}
impl Row {
    
    pub fn new(cells: Vec<Cell>) -> Row {
        Row { cells }
    }
    
    pub fn empty() -> Row {
        Self::new(vec![Cell::default(); 0])
    }
    
    
    
    
    pub (crate) fn column_count(&self) -> usize {
        self.cells.iter().map(|c| c.get_hspan()).sum()
    }
    
    pub fn len(&self) -> usize {
        self.cells.len()
        
    }
    
    pub fn is_empty(&self) -> bool {
        self.cells.is_empty()
    }
    
    
    fn get_height(&self) -> usize {
        let mut height = 1; 
        for cell in &self.cells {
            let h = cell.get_height();
            if h > height {
                height = h;
            }
        }
        height
    }
    
    
    
    pub (crate) fn get_column_width(&self, column: usize, format: &TableFormat) -> usize {
        let mut i = 0;
        for c in &self.cells {
            if i + c.get_hspan() > column {
                if c.get_hspan() == 1 {
                    return c.get_width();
                }
                let (lp, rp) = format.get_padding();
                let sep = format.get_column_separator(ColumnPosition::Intern).map(|_| 1).unwrap_or_default();
                let rem = lp + rp +sep;
                let mut w = c.get_width();
                if w > rem {
                    w -= rem;
                } else {
                    w = 0;
                }
                return (w as f64 / c.get_hspan() as f64).ceil() as usize;
            }
            i += c.get_hspan();
        }
        0
    }
    
    pub fn get_cell(&self, idx: usize) -> Option<&Cell> {
        self.cells.get(idx)
    }
    
    pub fn get_mut_cell(&mut self, idx: usize) -> Option<&mut Cell> {
        self.cells.get_mut(idx)
    }
    
    pub fn set_cell(&mut self, cell: Cell, idx: usize) -> Result<(), &str> {
        if idx >= self.len() {
            return Err("Cannot find cell");
        }
        self.cells[idx] = cell;
        Ok(())
    }
    
    pub fn add_cell(&mut self, cell: Cell) {
        self.cells.push(cell);
    }
    
    
    pub fn insert_cell(&mut self, index: usize, cell: Cell) {
        if index < self.cells.len() {
            self.cells.insert(index, cell);
        } else {
            self.add_cell(cell);
        }
    }
    
    pub fn remove_cell(&mut self, index: usize) {
        if index < self.cells.len() {
            self.cells.remove(index);
        }
    }
    
    pub fn iter(&self) -> Iter<Cell> {
        self.cells.iter()
    }
    
    pub fn iter_mut(&mut self) -> IterMut<Cell> {
        self.cells.iter_mut()
    }
    
    fn __print<T: Write + ?Sized, F>(&self,
                                     out: &mut T,
                                     format: &TableFormat,
                                     col_width: &[usize],
                                     f: F)
                                     -> Result<usize, Error>
        where F: Fn(&Cell, &mut T, usize, usize, bool) -> Result<(), Error>
    {
        let height = self.get_height();
        for i in 0..height {
            
            out.write_all(&vec![b' '; format.get_indent()])?;
            format.print_column_separator(out, ColumnPosition::Left)?;
            let (lp, rp) = format.get_padding();
            let mut j = 0;
            let mut hspan = 0; 
            while j+hspan < col_width.len() {
                out.write_all(&vec![b' '; lp])?; 
                
                
                let skip_r_fill = (j == col_width.len() - 1) &&
                                  format.get_column_separator(ColumnPosition::Right).is_none();
                match self.get_cell(j) {
                    Some(c) => {
                        
                        let mut w = col_width[j+hspan..j+hspan+c.get_hspan()].iter().sum();
                        let real_span = c.get_hspan()-1;
                        w += real_span * (lp + rp) + real_span * format.get_column_separator(ColumnPosition::Intern).map(|_| 1).unwrap_or_default();
                        
                        f(c, out, i, w, skip_r_fill)?;
                        hspan += real_span; 
                    },
                    None => f(&Cell::default(), out, i, col_width[j+hspan], skip_r_fill)?,
                };
                out.write_all(&vec![b' '; rp])?; 
                if j+hspan < col_width.len() - 1 {
                    format.print_column_separator(out, ColumnPosition::Intern)?;
                }
                j+=1;
            }
            format.print_column_separator(out, ColumnPosition::Right)?;
            out.write_all(NEWLINE)?;
        }
        Ok(height)
    }
    
    
    
    pub (crate) fn print<T: Write + ?Sized>(&self,
                                    out: &mut T,
                                    format: &TableFormat,
                                    col_width: &[usize])
                                    -> Result<usize, Error> {
        self.__print(out, format, col_width, Cell::print)
    }
    
    
    
    pub (crate) fn print_term<T: Terminal + ?Sized>(&self,
                                            out: &mut T,
                                            format: &TableFormat,
                                            col_width: &[usize])
                                            -> Result<usize, Error> {
        self.__print(out, format, col_width, Cell::print_term)
    }
    
    
    
    pub fn print_html<T: Write + ?Sized>(&self, out: &mut T, col_num: usize) -> Result<(), Error> {
        let mut printed_columns = 0;
        for cell in self.iter() {
            printed_columns += cell.print_html(out)?;
        }
        
        for _ in 0..col_num - printed_columns {
            Cell::default().print_html(out)?;
        }
        Ok(())
    }
}
impl Default for Row {
    fn default() -> Row {
        Row::empty()
    }
}
impl Index<usize> for Row {
    type Output = Cell;
    fn index(&self, idx: usize) -> &Self::Output {
        &self.cells[idx]
    }
}
impl IndexMut<usize> for Row {
    fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
        &mut self.cells[idx]
    }
}
impl<A: ToString> FromIterator<A> for Row {
    fn from_iter<T>(iterator: T) -> Row
        where T: IntoIterator<Item = A>
    {
        Self::new(iterator.into_iter().map(|ref e| Cell::from(e)).collect())
    }
}
impl<T, A> From<T> for Row
    where A: ToString,
          T: IntoIterator<Item = A>
{
    fn from(it: T) -> Row {
        Self::from_iter(it)
    }
}
impl<'a> IntoIterator for &'a Row {
    type Item = &'a Cell;
    type IntoIter = Iter<'a, Cell>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}
impl<'a> IntoIterator for &'a mut Row {
    type Item = &'a mut Cell;
    type IntoIter = IterMut<'a, Cell>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter_mut()
    }
}
impl <S: ToString> Extend<S> for Row {
    fn extend<T: IntoIterator<Item=S>>(&mut self, iter: T) {
        self.cells.extend(iter.into_iter().map(|s| Cell::new(&s.to_string())));
    }
}
#[macro_export]
macro_rules! row {
    (($($out:tt)*);) => (vec![$($out)*]);
    (($($out:tt)*); $value:expr) => (vec![$($out)* $crate::cell!($value)]);
    (($($out:tt)*); $value:expr, $($n:tt)*) => ($crate::row!(($($out)* $crate::cell!($value),); $($n)*));
    (($($out:tt)*); $style:ident -> $value:expr) => (vec![$($out)* $crate::cell!($style -> $value)]);
    (($($out:tt)*); $style:ident -> $value:expr, $($n: tt)*) => ($crate::row!(($($out)* $crate::cell!($style -> $value),); $($n)*));
    ($($content:expr), *) => ($crate::Row::new(vec![$($crate::cell!($content)), *])); 
    ($style:ident => $($content:expr), *) => ($crate::Row::new(vec![$($crate::cell!($style -> $content)), *]));
    ($style:ident => $($content:expr,) *) => ($crate::Row::new(vec![$($crate::cell!($style -> $content)), *]));
    ($($content:tt)*) => ($crate::Row::new($crate::row!((); $($content)*)));
}
#[cfg(test)]
mod tests {
    use super::*;
    use Cell;
    #[test]
    fn row_default_empty() {
        let row1 = Row::default();
        assert_eq!(row1.len(), 0);
        assert!(row1.is_empty());
    }
    #[test]
    fn get_add_set_cell() {
        let mut row = Row::from(vec!["foo", "bar", "foobar"]);
        assert_eq!(row.len(), 3);
        assert!(row.get_mut_cell(12).is_none());
        let c1 = row.get_mut_cell(0).unwrap().clone();
        assert_eq!(c1.get_content(), "foo");
        let c1 = Cell::from(&"baz");
        assert!(row.set_cell(c1.clone(), 1000).is_err());
        assert!(row.set_cell(c1.clone(), 0).is_ok());
        assert_eq!(row.get_cell(0).unwrap().get_content(), "baz");
        row.add_cell(c1.clone());
        assert_eq!(row.len(), 4);
        assert_eq!(row.get_cell(3).unwrap().get_content(), "baz");
    }
    #[test]
    fn insert_cell() {
        let mut row = Row::from(vec!["foo", "bar", "foobar"]);
        assert_eq!(row.len(), 3);
        let cell = Cell::new("baz");
        row.insert_cell(1000, cell.clone());
        assert_eq!(row.len(), 4);
        assert_eq!(row.get_cell(3).unwrap().get_content(), "baz");
        row.insert_cell(1, cell.clone());
        assert_eq!(row.len(), 5);
        assert_eq!(row.get_cell(1).unwrap().get_content(), "baz");
    }
    #[test]
    fn remove_cell() {
        let mut row = Row::from(vec!["foo", "bar", "foobar"]);
        assert_eq!(row.len(), 3);
        row.remove_cell(1000);
        assert_eq!(row.len(), 3);
        row.remove_cell(1);
        assert_eq!(row.len(), 2);
        assert_eq!(row.get_cell(0).unwrap().get_content(), "foo");
        assert_eq!(row.get_cell(1).unwrap().get_content(), "foobar");
    }
    #[test]
    fn extend_row() {
        let mut row = Row::from(vec!["foo", "bar", "foobar"]);
        row.extend(vec!["A", "B", "C"]);
        assert_eq!(row.len(), 6);
        assert_eq!(row.get_cell(3).unwrap().get_content(), "A");
        assert_eq!(row.get_cell(4).unwrap().get_content(), "B");
        assert_eq!(row.get_cell(5).unwrap().get_content(), "C");
    }
}