// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
	"container/list"
	"fmt"
	"reflect"
)

// Elements of abstraction the key.
type K struct {
	Element interface{}
}

/*
// Elements of abstraction the key.
type V struct {
	Element interface{}
}
*/
/*	This method may be required
func (e *E) Elem() interface{} {
	return e.Element
}
*/

// Structure for extending the map collection.
//
// keyType : Key type of Map .
// keyType : Value type of Map.
// TMap      : Map of the object to be extended.
// TOrder    : Insertion order list of map.
type OrderedMap struct {
	keyType interface{}
	valType interface{}
	TMap    map[K]interface{}
	TOrder  *list.List
}

// Constructor of OrderedMap.
func NewOrderedMap() *OrderedMap {
	om := &OrderedMap{}
	om.TMap = make(map[K]interface{})
	om.TOrder = list.New()
	return om
}

// Check whether the same of type of Map and List
//
// Returns: - error contents
//                 and nil if no error occurred.
func (om *OrderedMap) checkType(keyInfs interface{}, valInfs interface{}) (e error) {
	if om.keyType == nil && om.valType == nil {
		om.keyType = keyInfs
		om.valType = valInfs
		return nil
	} else {
		if reflect.TypeOf(om.keyType) != reflect.TypeOf(keyInfs) {
			return fmt.Errorf("Map Key Type mismatch [ %s ] and [ %s ].", reflect.TypeOf(om.keyType), reflect.TypeOf(keyInfs))
		}
	}
	return nil
}

// Check whether the same key exists in the map
//
// Returns: - error contents
//                 and nil if no error occurred.
func (om *OrderedMap) checkDuplicate(keyInfs interface{}) (e error) {
	for elem := om.TOrder.Front(); elem != nil; elem = elem.Next() {
		if elem.Value == keyInfs {
			return fmt.Errorf("Map key Duplicated [%s].", elem.Value)
		}
	}
	return
}

// Append Elements to Map
//
// Returns: - error contents
//                 and nil if no error occurred.
func (om *OrderedMap) Append(keyInfs interface{}, valInfs interface{}) (e error) {
	e = om.checkType(keyInfs, valInfs)
	if e != nil {
		return e
	}
	// Append key Elements to Map
	om.TMap[K{Element: keyInfs}] = valInfs
	e = om.checkDuplicate(K{Element: keyInfs})
	if e != nil {
		/*
			for elem := om.TOrder.Front(); elem != nil; elem = elem.Next() {
				if elem.Value == (K{Element: keyInfs}) {
					tmp := elem.Next()
					om.TOrder.Remove(elem)
					elem = tmp
				}
			}*/
		return nil
	}
	// Append Elements to List
	om.TOrder.PushBack(K{Element: keyInfs})
	return nil
}

// Get Elements from receive parameter.
//
// Returns: - Value of Map
//                 Return the interface that value has entered the Map.
func (om *OrderedMap) Get(keyInfs interface{}) interface{} {
	elem := om.TMap[K{Element: keyInfs}]
	return elem
}

// Convert Map keys to List.
//
// Returns: - List of Map Keys
func (om *OrderedMap) KeyLists() *list.List {
	keys := list.New()
	for key := om.TOrder.Front(); key != nil; key = key.Next() {
		keyElem := key.Value.(K).Element
		keys.PushBack(keyElem)
	}
	return keys
}

// Convert Map values to List.
//
// Returns: - List of Map Values
func (om *OrderedMap) ValueLists() *list.List {
	vals := list.New()
	for key := om.TOrder.Front(); key != nil; key = key.Next() {
		keyElem := key.Value.(K).Element
		value := om.Get(keyElem)
		vals.PushBack(value)
	}
	return vals
}

// Get Map length
//
// Returns: - Length of the map Element.
func (om *OrderedMap) Len() int {
	return om.TOrder.Len()
}

// Delete Map Element
//
// Returns: - error contents
//                 and nil if no error occurred.
func (om *OrderedMap) Delete(keyInfs interface{}) (e error) {
	// Delete key Elements from Map
	delete(om.TMap, K{Element: keyInfs})
	// Delete key Elements from List
	for elem := om.TOrder.Front(); elem != nil; elem = elem.Next() {
		if elem.Value == (K{Element: keyInfs}) {
			tmp := elem.Next()
			if tmp == nil {
				break
			}
			om.TOrder.Remove(elem)
			elem = tmp
		}
	}
	return
}

// Get Elements from Map and delete from List
//
// Returns: - Value of Map
//                 Return the interface that value has entered the Map.
func (om *OrderedMap) Pop(keyInfs interface{}) interface{} {
	elem := om.TMap[K{Element: keyInfs}]
	key := (K{Element: keyInfs}).Element
	om.Delete(key)
	return elem
}

// Clear Map and List
func (om *OrderedMap) Clear() {
	om.keyType = nil
	om.valType = nil
	om.TMap = make(map[K]interface{})
	om.TOrder = list.New()
}