use frame_support::{ StorageMap, Parameter };
use sp_runtime::traits::Member;
use codec::{ Encode, Decode };

#[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))]
#[derive(Encode, Decode)]
pub struct LinkedItem<Item> {
    pub prev: Option<Item>,
    pub next: Option<Item>,
}

pub struct LinkedList<Storage, Key, Item>(sp_std::marker::PhantomData<(Storage, Key, Item)>);

impl<Storage, Key, Value> LinkedList<Storage, Key, Value>
where
    Value: Parameter + Member + Copy,
    Key: Parameter,
    Storage: StorageMap<(Key, Option<Value>), LinkedItem<Value>, Query = Option<LinkedItem<Value>>>,
{
    fn read_head(key: &Key) -> LinkedItem<Value> {
        Self::read(key, None)
    }

    fn write_head(account: &Key, item: LinkedItem<Value>) {
        Self::write(account, None, item);
    }

    fn read(key: &Key, value: Option<Value>) -> LinkedItem<Value> {
        Storage::get(&(key.clone(), value)).unwrap_or_else(|| LinkedItem {
            prev: None,
            next: None,
        })
    }

    fn write(key: &Key, value: Option<Value>, item: LinkedItem<Value>) {
        Storage::insert(&(key.clone(), value), item);
    }

    pub fn append(key: &Key, value: Value) {
        // 作业
        let head = Self::read_head(key);
        let n_head = LinkedItem {
            prev: Some(value),
            next: head.next,
        };

        Self::write_head(key, n_head);

        let prev = Self::read(key, head.prev);
        let new_prev = LinkedItem {
            prev: prev.prev,
            next: Some(value),
        };
        Self::write(key, head.prev, new_prev);

        let item = LinkedItem {
            prev: head.prev,
            next: None,
        };
        Self::write(key, Some(value), item);
    }

    pub fn remove(key: &Key, value: Value) {
        // 作业
        if let Some(item) = Storage::take(&(key.clone(), Some(value))) {
            let prev = Self::read(key, item.prev);
            let n_prev = LinkedItem {
                prev: prev.prev,
                next: item.next,
            };

            Self::write(key, item.prev, n_prev);

            let next = Self::read(key, item.next);
            let n_next = LinkedItem {
                prev: item.prev,
                next: next.next,
            };

            Self::write(key, item.next, n_next);
        }
    }
}