/*  OpenTally: Open-source election vote counting
 *  Copyright © 2021–2022 Lee Yingtong Li (RunasSudo)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use crate::election::Candidate;

use std::ops::Index;

/// Mimics a [HashMap] on [Candidate]s, but internally is a [Vec] based on [Candidate::index]
#[derive(Clone)]
pub struct CandidateMap<'e, V> {
	entries: Vec<Option<(&'e Candidate, V)>>
}

impl<'e, V> CandidateMap<'e, V> {
	/// See [HashMap::new]
	pub fn new() -> Self {
		Self {
			entries: Vec::new()
		}
	}
	
	/// See [HashMap::with_capacity]
	pub fn with_capacity(capacity: usize) -> Self {
		let mut ret = Self {
			entries: Vec::with_capacity(capacity)
		};
		ret.maybe_resize(capacity);
		return ret;
	}
	
	fn maybe_resize(&mut self, len: usize) {
		if len < self.entries.len() {
			return;
		}
		
		self.entries.resize_with(len, || None);
	}
	
	/// See [HashMap::len]
	#[inline]
	pub fn len(&self) -> usize {
		return self.entries.iter().filter(|e| e.is_some()).count();
	}
	
	/// See [HashMap::insert]
	#[inline]
	pub fn insert(&mut self, candidate: &'e Candidate, value: V) {
		self.maybe_resize(candidate.index + 1);
		self.entries[candidate.index] = Some((candidate, value));
	}
	
	/// See [HashMap::get]
	#[inline]
	pub fn get(&self, candidate: &'e Candidate) -> Option<&V> {
		return self.entries.get(candidate.index).unwrap_or(&None).as_ref().map(|(_, v)| v);
	}
	
	/// See [HashMap::get_mut]
	#[inline]
	pub fn get_mut(&mut self, candidate: &'e Candidate) -> Option<&mut V> {
		match self.entries.get_mut(candidate.index) {
			Some(v) => {
				return v.as_mut().map(|(_, v)| v);
			}
			None => {
				return None;
			}
		}
	}
	
	/// See [HashMap::iter]
	#[inline]
	pub fn iter(&self) -> Iter<'_, 'e, V> {
		return Iter { map: &self, index: 0 };
	}
	
	/// See [HashMap::iter_mut]
	#[inline]
	pub fn iter_mut(&mut self) -> IterMut<'_, 'e, V> {
		return IterMut { map: self, index: 0 };
	}
	
	/// See [HashMap::values]
	#[inline]
	pub fn values(&self) -> Values<'_, 'e, V> {
		return Values { map: &self, index: 0 };
	}
}

impl<'e, V> Index<&Candidate> for CandidateMap<'e, V> {
	type Output = V;
	
	fn index(&self, candidate: &Candidate) -> &Self::Output {
		return self.entries.get(candidate.index).unwrap_or(&None).as_ref().map(|(_, v)| v).unwrap();
	}
}

/// See [CandidateMap::iter]
pub struct Iter<'m, 'e, V> {
	map: &'m CandidateMap<'e, V>,
	index: usize
}

impl<'m, 'e, V> Iterator for Iter<'m, 'e, V> {
	type Item = (&'e Candidate, &'m V);
	
	fn next(&mut self) -> Option<Self::Item> {
		loop {
			match self.map.entries.get(self.index) {
				Some(e) => {
					// Key within range
					match e {
						Some((k, v)) => {
							// Key is set
							self.index += 1;
							return Some((k, v));
						}
						None => {
							// Key is unset
							self.index += 1;
							continue;
						}
					}
				}
				None => {
					// Key outside range
					return None;
				}
			}
		}
	}
}

/// See [CandidateMap::iter_mut]
pub struct IterMut<'m, 'e, V> {
	map: &'m mut CandidateMap<'e, V>,
	index: usize
}

impl<'m, 'e, V> Iterator for IterMut<'m, 'e, V> {
	type Item = (&'e Candidate, &'m mut V);
	
	fn next(&mut self) -> Option<Self::Item> {
		loop {
			match self.map.entries.get_mut(self.index) {
				Some(e) => {
					// Key within range
					match e {
						Some((k, v)) => {
							// Key is set
							let v_ptr = v as *mut V;
							
							// SAFETY: Need unsafe pointer magic for IterMut
							let vv = unsafe { &mut *v_ptr };
							
							self.index += 1;
							return Some((k, vv));
						}
						None => {
							// Key is unset
							self.index += 1;
							continue;
						}
					}
				}
				None => {
					// Key outside range
					return None;
				}
			}
		}
	}
}

/// See [CandidateMap::values]
pub struct Values<'m, 'e, V> {
	map: &'m CandidateMap<'e, V>,
	index: usize
}

impl<'m, 'e, V> Iterator for Values<'m, 'e, V> {
	type Item = &'m V;
	
	fn next(&mut self) -> Option<Self::Item> {
		loop {
			match self.map.entries.get(self.index) {
				Some(e) => {
					// Key within range
					match e {
						Some((_, v)) => {
							// Key is set
							self.index += 1;
							return Some(v);
						}
						None => {
							// Key is unset
							self.index += 1;
							continue;
						}
					}
				}
				None => {
					// Key outside range
					return None;
				}
			}
		}
	}
}

/// See [CandidateMap::into_iter]
pub struct IntoIter<'e, V> {
	map: CandidateMap<'e, V>,
	index: usize
}

impl<'e, V> Iterator for IntoIter<'e, V> {
	type Item = (&'e Candidate, V);
	
	fn next(&mut self) -> Option<Self::Item> {
		loop {
			match self.map.entries.get_mut(self.index) {
				Some(e) => {
					// Key within range
					match e.take() {
						Some((k, v)) => {
							// Key is set
							self.index += 1;
							return Some((k, v));
						}
						None => {
							// Key is unset
							self.index += 1;
							continue;
						}
					}
				}
				None => {
					// Key outside range
					return None;
				}
			}
		}
	}
}

impl<'e, V> IntoIterator for CandidateMap<'e, V> {
	type Item = (&'e Candidate, V);
	
	type IntoIter = IntoIter<'e, V>;
	
	fn into_iter(self) -> Self::IntoIter {
		return IntoIter { map: self, index: 0 };
	}
}
