use frame_support::{
	parameter_types,
	traits::{Get, Time},
};
use orml_traits::CombineData;
use rstd::prelude::Vec;

use crate::{MomentOf, TimestampedValueOf, Trait};

/// Sort by value and returns median timestamped value.
/// Returns prev_value if not enough valid values.
pub struct DefaultCombineData<T: Trait>(rstd::marker::PhantomData<T>);

impl<T: Trait> CombineData<T::OracleKey, TimestampedValueOf<T>> for DefaultCombineData<T> {
	fn combine_data(
		_key: &T::OracleKey,
		values: Vec<TimestampedValueOf<T>>,
		prev_value: Option<TimestampedValueOf<T>>,
	) -> Option<TimestampedValueOf<T>> {
		let expires_in: MomentOf<T> = <Self as Parameters<MomentOf<T>>>::expires_in::get().into();
		let now = T::Time::now();
		let mut valid_values = values
			.into_iter()
			.filter_map(|x| {
				if x.timestamp + expires_in > now {
					return Some(x);
				}
				None
			})
			.collect::<Vec<TimestampedValueOf<T>>>();

		let count = valid_values.len();
		let minimum_count = <Self as Parameters<MomentOf<T>>>::minimum_count::get();
		if count < minimum_count {
			return prev_value;
		}

		valid_values.sort_by(|a, b| a.value.cmp(&b.value));

		let median_index = count / 2;
		return Some(valid_values[median_index].clone());
	}
}

parameter_types! {
	pub const MinimumCount: usize = 3;
	pub const ExpiresIn: u32 = 600;
}

trait Parameters<Moment> {
	type minimum_count: Get<usize>;
	type expires_in: Get<Moment>;
}

impl<T: Trait> Parameters<MomentOf<T>> for DefaultCombineData<T> {
	type minimum_count = MinimumCount;
	type expires_in = ExpiresIn;
}