#region COPYRIGHT
//
//     THIS IS GENERATED BY TEMPLATE
//     
//     AUTHOR  :     ROYE
//     DATE       :     2011
//
//     COPYRIGHT (C) 2011, TIANXIAHOTEL TECHNOLOGIES CO., LTD. ALL RIGHTS RESERVED.
//
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace System.Collections.Generic
{
    [Serializable, DebuggerDisplay("Count = {Count}")]
    public class TopDeque<TKey, TValue> : IEnumerable<TValue>
    {
        // Fields
        private readonly LinkedList<Pair<TKey, TValue>> list;
        private readonly Dictionary<TKey, LinkedListNode<Pair<TKey, TValue>>> map;

        // Constructors
        public TopDeque()
        {
            this.list = new LinkedList<Pair<TKey, TValue>>();
            this.map = new Dictionary<TKey, LinkedListNode<Pair<TKey, TValue>>>();
        }

        public TopDeque(int capacity)
        {
            this.list = new LinkedList<Pair<TKey, TValue>>();
            this.map = new Dictionary<TKey, LinkedListNode<Pair<TKey, TValue>>>(capacity);
        }

        public TopDeque(IEqualityComparer<TKey> keyComparer)
        {
            this.list = new LinkedList<Pair<TKey, TValue>>();
            this.map = new Dictionary<TKey, LinkedListNode<Pair<TKey, TValue>>>(keyComparer);
        }

        public TopDeque(IEqualityComparer<TKey> keyComparer, int capacity)
        {
            this.list = new LinkedList<Pair<TKey, TValue>>();
            this.map = new Dictionary<TKey, LinkedListNode<Pair<TKey, TValue>>>(capacity, keyComparer);
        }

        // Methods
        public void AddToTop(TKey key, TValue value)
        {
            var node = this.list.AddFirst(new Pair<TKey, TValue>(key, value));
            try
            {
                this.map.Add(key, node);
            }
            catch (Exception exception)
            {
                this.list.RemoveFirst();
                if (exception is ArgumentException)
                {
                    throw new InvalidOperationException("CollectionAlreadyContainsItemWithSpecifiedKey");
                }
                throw;
            }
        }

        public void AddToBottom(TKey key, TValue value)
        {
            var node = this.list.AddLast(new Pair<TKey, TValue>(key, value));
            try
            {
                this.map.Add(key, node);
            }
            catch (Exception exception)
            {
                this.list.RemoveLast();
                if (exception is ArgumentException)
                {
                    throw new InvalidOperationException("CollectionAlreadyContainsItemWithSpecifiedKey");
                }
                throw;
            }
        }

        public bool Contains(TKey key)
        {
            return this.map.ContainsKey(key);
        }

        public void MoveToBottom(TKey key)
        {
            LinkedListNode<Pair<TKey, TValue>> node;
            if (!this.map.TryGetValue(key, out node))
            {
                throw new KeyNotFoundException("NoObjectWithSpecifiedKey");
            }
            this.list.Remove(node);
            this.list.AddLast(node);
        }

        public void MoveToTop(TKey key)
        {
            LinkedListNode<Pair<TKey, TValue>> node;
            if (!this.map.TryGetValue(key, out node))
            {
                throw new KeyNotFoundException("NoObjectWithSpecifiedKey");
            }
            this.list.Remove(node);
            this.list.AddFirst(node);
        }

        public TValue PopBottom()
        {
            if (this.list.Count == 0)
            {
                throw new InvalidOperationException("CollectionIsEmpty");
            }
            var last = this.list.Last;
            this.list.Remove(last);
            var pair = last.Value;
            this.map.Remove(pair.First);
            return pair.Second;
        }

        public TValue PopTop()
        {
            if (this.list.Count == 0)
            {
                throw new InvalidOperationException("CollectionIsEmpty");
            }
            var first = this.list.First;
            this.list.Remove(first);
            var pair = first.Value;
            this.map.Remove(pair.First);
            return pair.Second;
        }

        public void Remove(TKey key)
        {
            LinkedListNode<Pair<TKey, TValue>> node;
            if (this.map.TryGetValue(key, out node))
            {
                this.list.Remove(node);
                this.map.Remove(key);
            }
        }

        public bool TryChangeValue(TKey key, TValue value, bool moveToTop, bool replaceIfExists, out TValue oldValue)
        {
            LinkedListNode<Pair<TKey, TValue>> node;
            if (this.map.TryGetValue(key, out node))
            {
                oldValue = node.Value.Second;
                if (moveToTop)
                {
                    this.list.Remove(node);
                    this.list.AddFirst(node);
                }
                if (replaceIfExists)
                {
                    node.Value = new Pair<TKey, TValue>(key, value);
                }
                return true;
            }
            oldValue = default(TValue);
            node = this.list.AddFirst(new Pair<TKey, TValue>(key, value));
            try
            {
                this.map.Add(key, node);
            }
            catch (Exception)
            {
                this.list.RemoveFirst();
                throw;
            }
            return false;
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            LinkedListNode<Pair<TKey, TValue>> node;
            if (this.map.TryGetValue(key, out node))
            {
                value = node.Value.Second;
                return true;
            }
            value = default(TValue);
            return false;
        }

        public bool TryGetValue(TKey key, bool moveToTop, out TValue value)
        {
            LinkedListNode<Pair<TKey, TValue>> node;
            if (this.map.TryGetValue(key, out node))
            {
                if (moveToTop)
                {
                    this.list.Remove(node);
                    this.list.AddFirst(node);
                }
                value = node.Value.Second;
                return true;
            }
            value = default(TValue);
            return false;
        }

        public void Clear()
        {
            this.list.Clear();
            this.map.Clear();
        }

        public IEnumerator<TValue> GetEnumerator()
        {
            foreach (var item in this.list)
            {
                yield return item.Second;
            }
        }

        [DebuggerStepThrough]
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        // Properties
        public TValue Bottom
        {
            get
            {
                if (this.list.Count == 0)
                {
                    throw new InvalidOperationException("CollectionIsEmpty");
                }
                return this.list.Last.Value.Second;
            }
        }

        public TKey BottomKey
        {
            get
            {
                if (this.list.Count == 0)
                {
                    throw new InvalidOperationException("CollectionIsEmpty");
                }
                return this.list.Last.Value.First;
            }
        }

        public int Count
        {
            [DebuggerStepThrough]
            get
            {
                return this.list.Count;
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                LinkedListNode<Pair<TKey, TValue>> node;
                if (!this.map.TryGetValue(key, out node))
                {
                    throw new KeyNotFoundException("NoObjectWithSpecifiedKey");
                }
                return node.Value.Second;
            }
            set
            {
                LinkedListNode<Pair<TKey, TValue>> node;
                if (this.map.TryGetValue(key, out node))
                {
                    node.Value = new Pair<TKey, TValue>(key, value);
                }
                throw new KeyNotFoundException("NoObjectWithSpecifiedKey");
            }
        }

        public TValue Top
        {
            get
            {
                if (this.list.Count == 0)
                {
                    throw new InvalidOperationException("CollectionIsEmpty");
                }
                return this.list.First.Value.Second;
            }
        }

        public TKey TopKey
        {
            get
            {
                if (this.list.Count == 0)
                {
                    throw new InvalidOperationException("CollectionIsEmpty");
                }
                return this.list.First.Value.First;
            }
        }
    }
}
