use core;
use super::super::alloc::SliceWrapper;
use super::super::alloc::SliceWrapperMut;
use super::interface::Freezable;
#[derive(Copy,Clone,Default,Debug)]
pub struct InputReference<'a> {
    pub data: &'a [u8],
    pub orig_offset: usize, // offset into the original slice of data
}
impl<'a> SliceWrapper<u8> for InputReference<'a> {
    fn slice(&self) -> & [u8] {
        self.data
    }
}

impl<'a> Freezable for InputReference<'a> {
    fn freeze(&self) -> super::interface::SliceOffset {
        debug_assert!(self.data.len() <= 0xffffffff);
        super::interface::SliceOffset(self.orig_offset, self.data.len() as u32)
    }
}

pub struct InputReferenceMut<'a> {
    pub data: &'a mut [u8],
    pub orig_offset: usize, // offset into the original slice of data
}

impl<'a> SliceWrapper<u8> for InputReferenceMut<'a> {
    fn slice(&self) -> & [u8] {
        self.data
    }
}
impl<'a> SliceWrapperMut<u8> for InputReferenceMut<'a> {
    fn slice_mut(&mut self) -> &mut [u8] {
        self.data
    }
}
impl <'a> Default for InputReferenceMut<'a> {
    fn default() -> Self {
        InputReferenceMut {
            data:&mut[],
            orig_offset:0,
        }
    }
}
impl <'a> From<InputReferenceMut<'a>> for InputReference<'a> {
   fn from(val: InputReferenceMut<'a>) -> InputReference<'a> {
       InputReference{
           data: val.data,
           orig_offset: val.orig_offset,
       }
   }
}

impl <'a> From<&'a InputReferenceMut<'a>> for InputReference<'a> {
   fn from(val: &'a InputReferenceMut<'a>) -> InputReference<'a> {
       InputReference{
           data: val.data,
           orig_offset: val.orig_offset,
       }
   }
}

#[derive(Clone, Debug,Copy)]
pub struct InputPair<'a>(pub InputReference<'a>, pub InputReference<'a>);

impl<'a> core::cmp::PartialEq for InputPair<'a> {
    fn eq<'b>(&self, other: &InputPair<'b>) -> bool {
        if self.0.len() + self.1.len() != other.0.len() + other.1.len() {
            return false;
        }
        for (a_iter, b_iter) in self.0.data.iter().chain(self.1.data.iter()).zip(other.0.data.iter().chain(other.1.data.iter())) {
            if *a_iter != *b_iter {
                return false;
            }
        }
        return true;
    }
}
impl<'a> core::ops::Index<usize> for InputPair<'a> {
  type Output = u8;
  fn index(&self, index:usize) -> &u8 {
    if index >= self.0.len() {
      &self.1.data[index - self.0.len()]
    } else {
      &self.0.data[index]
    }
  }
}
impl<'a> core::fmt::LowerHex for InputPair<'a> {
    fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
        for item in self.0.data {
            if let Err(e) = fmtr.write_fmt(format_args!("{:02x}", item)) {
                return Err(e)
            }
        }
        for item in self.1.data {
            if let Err(e) = fmtr.write_fmt(format_args!("{:02x}", item)) {
                return Err(e);
            }
        }
        Ok(())
    }
}

impl<'a> InputPair<'a> {
    pub fn split_at(&self, loc : usize) -> (InputPair<'a>, InputPair<'a>) {
        if loc >= self.0.len() {
            let offset_from_self_1 = loc - self.0.len();
            let (first, second) = self.1.data.split_at(core::cmp::min(offset_from_self_1,
                                                                 self.1.len()));
            return (InputPair::<'a>(self.0,
                                    InputReference::<'a>{data:first,
                                                   orig_offset:self.1.orig_offset}),
                    InputPair::<'a>(InputReference::<'a>::default(),
                                    InputReference::<'a>{data:second,
                                                   orig_offset:offset_from_self_1 + self.1.orig_offset,
                                    }),
                    );
        }
        let (first, second) = self.0.data.split_at(core::cmp::min(loc,
                                                                  self.0.len()));
        (InputPair::<'a>(InputReference::<'a>{data:first,
                                        orig_offset:self.0.orig_offset},
                         InputReference::<'a>::default()),
         InputPair::<'a>(InputReference::<'a>{data:second,
                                        orig_offset:self.0.orig_offset + loc},
                         self.1))
    }
    pub fn len(&self) -> usize {
        self.0.len() + self.1.len()
    }
}
