use basic::{PageType, Encoding};
use errors::Result;
use file::metadata::ColumnChunkMetaData;
use file::statistics::Statistics;
use util::memory::ByteBufferPtr;
pub enum Page {
  DataPage {
    buf: ByteBufferPtr,
    num_values: u32,
    encoding: Encoding,
    def_level_encoding: Encoding,
    rep_level_encoding: Encoding,
    statistics: Option<Statistics>
  },
  DataPageV2 {
    buf: ByteBufferPtr,
    num_values: u32,
    encoding: Encoding,
    num_nulls: u32,
    num_rows: u32,
    def_levels_byte_len: u32,
    rep_levels_byte_len: u32,
    is_compressed: bool,
    statistics: Option<Statistics>
  },
  DictionaryPage {
    buf: ByteBufferPtr,
    num_values: u32,
    encoding: Encoding,
    is_sorted: bool
  }
}
impl Page {
  
  pub fn page_type(&self) -> PageType {
    match self {
      &Page::DataPage { .. } => PageType::DATA_PAGE,
      &Page::DataPageV2 { .. } => PageType::DATA_PAGE_V2,
      &Page::DictionaryPage { .. } => PageType::DICTIONARY_PAGE
    }
  }
  
  pub fn buffer(&self) -> &ByteBufferPtr {
    match self {
      &Page::DataPage { ref buf, .. } => &buf,
      &Page::DataPageV2 { ref buf, .. } => &buf,
      &Page::DictionaryPage { ref buf, .. } => &buf
    }
  }
  
  pub fn num_values(&self) -> u32 {
    match self {
      &Page::DataPage { num_values, .. } => num_values,
      &Page::DataPageV2 { num_values, .. } => num_values,
      &Page::DictionaryPage { num_values, .. } => num_values
    }
  }
  
  pub fn encoding(&self) -> Encoding {
    match self {
      &Page::DataPage { encoding, .. } => encoding,
      &Page::DataPageV2 { encoding, .. } => encoding,
      &Page::DictionaryPage { encoding, .. } => encoding
    }
  }
  
  pub fn statistics(&self) -> Option<&Statistics> {
    match self {
      &Page::DataPage { ref statistics, ..} => statistics.as_ref(),
      &Page::DataPageV2 { ref statistics, ..} => statistics.as_ref(),
      &Page::DictionaryPage { .. } => None
    }
  }
}
pub struct CompressedPage {
  compressed_page: Page,
  uncompressed_size: usize
}
impl CompressedPage {
  
  
  pub fn new(compressed_page: Page, uncompressed_size: usize) -> Self {
    Self {
      compressed_page: compressed_page,
      uncompressed_size: uncompressed_size
    }
  }
  
  pub fn page_type(&self) -> PageType {
    self.compressed_page.page_type()
  }
  
  pub fn compressed_page(&self) -> &Page {
    &self.compressed_page
  }
  
  pub fn uncompressed_size(&self) -> usize {
    self.uncompressed_size
  }
  
  
  
  
  pub fn compressed_size(&self) -> usize {
    self.compressed_page.buffer().len()
  }
  
  pub fn num_values(&self) -> u32 {
    self.compressed_page.num_values()
  }
  
  pub fn encoding(&self) -> Encoding {
    self.compressed_page.encoding()
  }
  
  pub fn data(&self) -> &[u8] {
    self.compressed_page.buffer().data()
  }
}
pub struct PageWriteSpec {
  pub page_type: PageType,
  pub uncompressed_size: usize,
  pub compressed_size: usize,
  pub num_values: u32,
  pub offset: u64,
  pub bytes_written: u64
}
impl PageWriteSpec {
  
  pub fn new() -> Self {
    Self {
      page_type: PageType::DATA_PAGE,
      uncompressed_size: 0,
      compressed_size: 0,
      num_values: 0,
      offset: 0,
      bytes_written: 0
    }
  }
}
pub trait PageReader {
  
  
  fn get_next_page(&mut self) -> Result<Option<Page>>;
}
pub trait PageWriter {
  
  
  
  
  
  
  fn write_page(&mut self, page: CompressedPage) -> Result<PageWriteSpec>;
  
  
  
  
  fn write_metadata(&mut self, metadata: &ColumnChunkMetaData) -> Result<()>;
  
  
  fn close(&mut self) -> Result<()>;
}
#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  fn test_page() {
    let data_page = Page::DataPage {
      buf: ByteBufferPtr::new(vec![0, 1, 2]),
      num_values: 10,
      encoding: Encoding::PLAIN,
      def_level_encoding: Encoding::RLE,
      rep_level_encoding: Encoding::RLE,
      statistics: Some(Statistics::int32(Some(1), Some(2), None, 1, true))
    };
    assert_eq!(data_page.page_type(), PageType::DATA_PAGE);
    assert_eq!(data_page.buffer().data(), vec![0, 1, 2].as_slice());
    assert_eq!(data_page.num_values(), 10);
    assert_eq!(data_page.encoding(), Encoding::PLAIN);
    assert_eq!(
      data_page.statistics(),
      Some(&Statistics::int32(Some(1), Some(2), None, 1, true))
    );
    let data_page_v2 = Page::DataPageV2 {
      buf: ByteBufferPtr::new(vec![0, 1, 2]),
      num_values: 10,
      encoding: Encoding::PLAIN,
      num_nulls: 5,
      num_rows: 20,
      def_levels_byte_len: 30,
      rep_levels_byte_len: 40,
      is_compressed: false,
      statistics: Some(Statistics::int32(Some(1), Some(2), None, 1, true))
    };
    assert_eq!(data_page_v2.page_type(), PageType::DATA_PAGE_V2);
    assert_eq!(data_page_v2.buffer().data(), vec![0, 1, 2].as_slice());
    assert_eq!(data_page_v2.num_values(), 10);
    assert_eq!(data_page_v2.encoding(), Encoding::PLAIN);
    assert_eq!(
      data_page_v2.statistics(),
      Some(&Statistics::int32(Some(1), Some(2), None, 1, true))
    );
    let dict_page = Page::DictionaryPage {
      buf: ByteBufferPtr::new(vec![0, 1, 2]),
      num_values: 10,
      encoding: Encoding::PLAIN,
      is_sorted: false
    };
    assert_eq!(dict_page.page_type(), PageType::DICTIONARY_PAGE);
    assert_eq!(dict_page.buffer().data(), vec![0, 1, 2].as_slice());
    assert_eq!(dict_page.num_values(), 10);
    assert_eq!(dict_page.encoding(), Encoding::PLAIN);
    assert_eq!(dict_page.statistics(), None);
  }
  #[test]
  fn test_compressed_page() {
    let data_page = Page::DataPage {
      buf: ByteBufferPtr::new(vec![0, 1, 2]),
      num_values: 10,
      encoding: Encoding::PLAIN,
      def_level_encoding: Encoding::RLE,
      rep_level_encoding: Encoding::RLE,
      statistics: Some(Statistics::int32(Some(1), Some(2), None, 1, true))
    };
    let cpage = CompressedPage::new(data_page, 5);
    assert_eq!(cpage.page_type(), PageType::DATA_PAGE);
    assert_eq!(cpage.uncompressed_size(), 5);
    assert_eq!(cpage.compressed_size(), 3);
    assert_eq!(cpage.num_values(), 10);
    assert_eq!(cpage.encoding(), Encoding::PLAIN);
    assert_eq!(cpage.data(), &[0, 1, 2]);
  }
}