use crate::rstd::{cmp::*, fmt};
use super::{nibble_ops, NibbleSlice, NibbleSliceIterator, BackingByteVec};
use crate::node::NodeKey;
use crate::node_codec::Partial;
use hash_db::Prefix;
impl<'a> Iterator for NibbleSliceIterator<'a> {
	type Item = u8;
	fn next(&mut self) -> Option<u8> {
		self.i += 1;
		match self.i <= self.p.len() {
			true => Some(self.p.at(self.i - 1)),
			false => None,
		}
	}
}
impl<'a> NibbleSlice<'a> {
	
	pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_slice(data, 0) }
	
	pub fn new_offset(data: &'a [u8], offset: usize) -> Self {
		Self::new_slice(data, offset)
	}
	fn new_slice(data: &'a [u8], offset: usize) -> Self {
		NibbleSlice {
			data,
			offset,
		}
	}
	
	pub fn iter(&'a self) -> NibbleSliceIterator<'a> {
		NibbleSliceIterator { p: self, i: 0 }
	}
	
	pub fn from_stored(i: &NodeKey) -> NibbleSlice {
		NibbleSlice::new_offset(&i.1[..], i.0)
	}
	
	pub fn to_stored(&self) -> NodeKey {
		let split = self.offset / nibble_ops::NIBBLE_PER_BYTE;
		let offset = self.offset % nibble_ops::NIBBLE_PER_BYTE;
		(offset, self.data[split..].into())
	}
	
	
	
	
	pub fn to_stored_range(&self, nb: usize) -> NodeKey {
		if nb >= self.len() { return self.to_stored() }
		if (self.offset + nb) % nibble_ops::NIBBLE_PER_BYTE == 0 {
			
			let start = self.offset / nibble_ops::NIBBLE_PER_BYTE;
			let end = (self.offset + nb) / nibble_ops::NIBBLE_PER_BYTE;
			(
				self.offset % nibble_ops::NIBBLE_PER_BYTE,
				BackingByteVec::from_slice(&self.data[start..end]),
			)
		} else {
			
			let start = self.offset / nibble_ops::NIBBLE_PER_BYTE;
			let end = (self.offset + nb) / nibble_ops::NIBBLE_PER_BYTE;
			let ea = BackingByteVec::from_slice(&self.data[start..=end]);
			let ea_offset = self.offset % nibble_ops::NIBBLE_PER_BYTE;
			let n_offset = nibble_ops::number_padding(nb);
			let mut result = (ea_offset, ea);
			nibble_ops::shift_key(&mut result, n_offset);
			result.1.pop();
			result
		}
	}
	
	pub fn is_empty(&self) -> bool { self.len() == 0 }
	
	#[inline]
	pub fn len(&self) -> usize { self.data.len() * nibble_ops::NIBBLE_PER_BYTE - self.offset }
	
	#[inline(always)]
	pub fn at(&self, i: usize) -> u8 {
		nibble_ops::at(&self, i)
	}
	
	pub fn mid(&self, i: usize) -> NibbleSlice<'a> {
		NibbleSlice {
			data: self.data,
			offset: self.offset + i,
		}
	}
	
	pub fn advance(&mut self, i: usize) {
		debug_assert!(self.len() >= i);
		self.offset += i;
	}
	
	pub fn back(&self, i: usize) -> NibbleSlice<'a> {
		NibbleSlice {
			data: self.data,
			offset: i,
		}
	}
	
	pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() }
	
	pub fn common_prefix(&self, them: &Self) -> usize {
		let s = min(self.len(), them.len());
		let mut i = 0usize;
		while i < s {
			if self.at(i) != them.at(i) { break; }
			i += 1;
		}
		i
	}
	
	
	pub fn right(&'a self) -> Partial {
		let split = self.offset / nibble_ops::NIBBLE_PER_BYTE;
		let nb = (self.len() % nibble_ops::NIBBLE_PER_BYTE) as u8;
		if nb > 0 {
			((nb, nibble_ops::pad_right(self.data[split])), &self.data[split + 1 ..])
		} else {
			((0, 0), &self.data[split..])
		}
	}
	
	pub fn right_iter(&'a self) -> impl Iterator<Item = u8> + 'a {
		let (mut first, sl) = self.right();
		let mut ix = 0;
		crate::rstd::iter::from_fn(move || {
			if first.0 > 0 {
				first.0 = 0;
				Some(nibble_ops::pad_right(first.1))
			} else {
				if ix < sl.len() {
					ix += 1;
					Some(sl[ix - 1])
				} else {
					None
				}
			}
		})
	}
	
	
	pub fn right_range_iter(&'a self, to: usize) -> impl Iterator<Item = u8> + 'a {
		let mut nib_res = to % nibble_ops::NIBBLE_PER_BYTE;
		let aligned_i = (self.offset + to) % nibble_ops::NIBBLE_PER_BYTE;
		let aligned = aligned_i == 0;
		let mut ix = self.offset / nibble_ops::NIBBLE_PER_BYTE;
		let ix_lim = (self.offset + to) / nibble_ops::NIBBLE_PER_BYTE;
		crate::rstd::iter::from_fn( move || {
			if aligned {
				if nib_res > 0 {
					let v = nibble_ops::pad_right(self.data[ix]);
					nib_res = 0;
					ix += 1;
					Some(v)
				} else if ix < ix_lim {
					ix += 1;
					Some(self.data[ix - 1])
				} else {
					None
				}
			} else {
				let (s1, s2) = nibble_ops::SPLIT_SHIFTS;
				
				if nib_res > 0 {
					let v = self.data[ix] >> s1;
					let v = nibble_ops::pad_right(v);
					nib_res = 0;
					Some(v)
				} else if ix < ix_lim {
					ix += 1;
					let b1 = self.data[ix - 1] << s2;
					let b2 = self.data[ix] >> s1;
					Some(b1 | b2)
				} else {
					None
				}
			}
		})
	}
	
	
	
	pub fn left(&'a self) -> Prefix {
		let split = self.offset / nibble_ops::NIBBLE_PER_BYTE;
		let ix = (self.offset % nibble_ops::NIBBLE_PER_BYTE) as u8;
		if ix == 0 {
			(&self.data[..split], None)
		} else {
			(&self.data[..split], Some(nibble_ops::pad_left(self.data[split])))
		}
	}
	
	pub fn left_owned(&'a self) -> (BackingByteVec, Option<u8>) {
		let (a, b) = self.left();
		(a.into(), b)
	}
}
impl<'a> Into<NodeKey> for NibbleSlice<'a> {
	fn into(self) -> NodeKey {
		(self.offset, self.data.into())
	}
}
impl<'a> PartialEq for NibbleSlice<'a> {
	fn eq(&self, them: &Self) -> bool {
		self.len() == them.len() && self.starts_with(them)
	}
}
impl<'a> Eq for NibbleSlice<'a> { }
impl<'a> PartialOrd for NibbleSlice<'a> {
	fn partial_cmp(&self, them: &Self) -> Option<Ordering> {
		Some(self.cmp(them))
	}
}
impl<'a> Ord for NibbleSlice<'a> {
	fn cmp(&self, them: &Self) -> Ordering {
		let s = min(self.len(), them.len());
		let mut i = 0usize;
		while i < s {
			match self.at(i).partial_cmp(&them.at(i)).unwrap() {
				Ordering::Less => return Ordering::Less,
				Ordering::Greater => return Ordering::Greater,
				_ => i += 1,
			}
		}
		self.len().cmp(&them.len())
	}
}
#[cfg(feature = "std")]
impl<'a> fmt::Debug for NibbleSlice<'a> {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		for i in 0..self.len() {
			match i {
				0 => write!(f, "{:01x}", self.at(i))?,
				_ => write!(f, "'{:01x}", self.at(i))?,
			}
		}
		Ok(())
	}
}
#[cfg(test)]
mod tests {
	use crate::nibble::{NibbleSlice, BackingByteVec};
	static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45];
	#[test]
	fn basics() {
		let n = NibbleSlice::new(D);
		assert_eq!(n.len(), 6);
		assert!(!n.is_empty());
		let n = NibbleSlice::new_offset(D, 6);
		assert!(n.is_empty());
		let n = NibbleSlice::new_offset(D, 3);
		assert_eq!(n.len(), 3);
		for i in 0..3 {
			assert_eq!(n.at(i), i as u8 + 3);
		}
	}
	#[test]
	fn iterator() {
		let n = NibbleSlice::new(D);
		let mut nibbles: Vec<u8> = vec![];
		nibbles.extend(n.iter());
		assert_eq!(nibbles, (0u8..6).collect::<Vec<_>>())
	}
	#[test]
	fn mid() {
		let n = NibbleSlice::new(D);
		let m = n.mid(2);
		for i in 0..4 {
			assert_eq!(m.at(i), i as u8 + 2);
		}
		let m = n.mid(3);
		for i in 0..3 {
			assert_eq!(m.at(i), i as u8 + 3);
		}
	}
	#[test]
	fn encoded_pre() {
		let n = NibbleSlice::new(D);
		assert_eq!(n.to_stored(), (0, BackingByteVec::from_slice(&[0x01, 0x23, 0x45])));
		assert_eq!(n.mid(1).to_stored(), (1, BackingByteVec::from_slice(&[0x01, 0x23, 0x45])));
		assert_eq!(n.mid(2).to_stored(), (0, BackingByteVec::from_slice(&[0x23, 0x45])));
		assert_eq!(n.mid(3).to_stored(), (1, BackingByteVec::from_slice(&[0x23, 0x45])));
	}
	#[test]
	fn from_encoded_pre() {
		let n = NibbleSlice::new(D);
		let stored: BackingByteVec = [0x01, 0x23, 0x45][..].into();
		assert_eq!(n, NibbleSlice::from_stored(&(0, stored.clone())));
		assert_eq!(n.mid(1), NibbleSlice::from_stored(&(1, stored)));
	}
	#[test]
	fn range_iter() {
		let n = NibbleSlice::new(D);
		for i in [
			vec![],
			vec![0x00],
			vec![0x01],
			vec![0x00, 0x12],
			vec![0x01, 0x23],
			vec![0x00, 0x12, 0x34],
			vec![0x01, 0x23, 0x45],
		].iter().enumerate() {
			range_iter_test(n, i.0, None, &i.1[..]);
		}
		for i in [
			vec![],
			vec![0x01],
			vec![0x12],
			vec![0x01, 0x23],
			vec![0x12, 0x34],
			vec![0x01, 0x23, 0x45],
		].iter().enumerate() {
			range_iter_test(n, i.0, Some(1), &i.1[..]);
		}
		for i in [
			vec![],
			vec![0x02],
			vec![0x23],
			vec![0x02, 0x34],
			vec![0x23, 0x45],
		].iter().enumerate() {
			range_iter_test(n, i.0, Some(2), &i.1[..]);
		}
		for i in [
			vec![],
			vec![0x03],
			vec![0x34],
			vec![0x03, 0x45],
		].iter().enumerate() {
			range_iter_test(n, i.0, Some(3), &i.1[..]);
		}
	}
	fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option<usize>, res: &[u8]) {
		let n = if let Some(i) = mid {
			n.mid(i)
		} else { n };
		assert_eq!(&n.right_range_iter(nb).collect::<Vec<_>>()[..], res);
	}
	#[test]
	fn shared() {
		let n = NibbleSlice::new(D);
		let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67];
		let m = NibbleSlice::new(other);
		assert_eq!(n.common_prefix(&m), 4);
		assert_eq!(m.common_prefix(&n), 4);
		assert_eq!(n.mid(1).common_prefix(&m.mid(1)), 3);
		assert_eq!(n.mid(1).common_prefix(&m.mid(2)), 0);
		assert_eq!(n.common_prefix(&m.mid(4)), 6);
		assert!(!n.starts_with(&m.mid(4)));
		assert!(m.mid(4).starts_with(&n));
	}
	#[test]
	fn compare() {
		let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45];
		let n = NibbleSlice::new(D);
		let m = NibbleSlice::new(other);
		assert!(n != m);
		assert!(n > m);
		assert!(m < n);
		assert!(n == m.mid(4));
		assert!(n >= m.mid(4));
		assert!(n <= m.mid(4));
	}
}