// Copyright © 2019 The Knative Authors
//
// 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 util

type valueEntry struct {
	Index int
	Value interface{}
}

type orderedMapIterator struct {
	orderedMap *OrderedMap
	nextIndex  int
}

// OrderedMap is similar implementation of OrderedDict in Python.
type OrderedMap struct {
	Keys     []string
	ValueMap map[string]*valueEntry
}

// NewOrderedMap returns new empty ordered map
func NewOrderedMap() *OrderedMap {
	return &OrderedMap{
		Keys:     []string{},
		ValueMap: map[string]*valueEntry{},
	}
}

// NewOrderedMapWithKVStrings returns new empty ordered map
func NewOrderedMapWithKVStrings(kvList [][]string) *OrderedMap {
	o := &OrderedMap{
		Keys:     []string{},
		ValueMap: map[string]*valueEntry{},
	}

	for _, pair := range kvList {
		if len(pair) != 2 {
			return nil
		}

		o.Set(pair[0], pair[1])
	}
	return o
}

// Get returns a value corresponding the key
func (o *OrderedMap) Get(key string) (interface{}, bool) {
	ve, ok := o.ValueMap[key]
	if ve != nil {
		return ve.Value, ok
	} else {
		return nil, false
	}
}

// GetString returns a string value corresponding the key
func (o *OrderedMap) GetString(key string) (string, bool) {
	ve, ok := o.ValueMap[key]

	if ve != nil {
		return ve.Value.(string), ok
	} else {
		return "", false
	}
}

// GetStringWithDefault returns a string value corresponding the key if the key is existing.
// Otherwise, the default value is returned.
func (o *OrderedMap) GetStringWithDefault(key string, defaultValue string) string {
	if ve, ok := o.ValueMap[key]; ok {
		return ve.Value.(string)
	} else {
		return defaultValue
	}
}

// Set append the key and value if the key is not existing on the map
// Otherwise, the value does just replace the old value corresponding to the key.
func (o *OrderedMap) Set(key string, value interface{}) {
	if ve, ok := o.ValueMap[key]; !ok {
		o.Keys = append(o.Keys, key)
		o.ValueMap[key] = &valueEntry{
			Index: len(o.Keys) - 1,
			Value: value,
		}
	} else {
		ve.Value = value
	}
}

// Delete deletes the key and value from the map
func (o *OrderedMap) Delete(key string) {
	if ve, ok := o.ValueMap[key]; ok {
		delete(o.ValueMap, key)
		o.Keys = append(o.Keys[:ve.Index], o.Keys[ve.Index+1:]...)
	}
}

// Len returns a size of the ordered map
func (o *OrderedMap) Len() int {
	return len(o.Keys)
}

// Iterator creates a iterator object
func (o *OrderedMap) Iterator() *orderedMapIterator {
	return &orderedMapIterator{
		orderedMap: o,
		nextIndex:  0,
	}
}

// Next returns key and values on current iterating cursor.
// If the cursor moved over last entry, then the third return value will be false, otherwise true.
func (it *orderedMapIterator) Next() (string, interface{}, bool) {
	if it.nextIndex >= it.orderedMap.Len() {
		return "", nil, false
	}

	key := it.orderedMap.Keys[it.nextIndex]
	ve := it.orderedMap.ValueMap[key]

	it.nextIndex++

	return key, ve.Value, true
}

// NextString is the same with Next, but the value is returned as string
func (it *orderedMapIterator) NextString() (string, string, bool) {
	key, value, isValid := it.Next()
	if isValid {
		return key, value.(string), isValid
	} else {
		return "", "", isValid
	}
}
