package subspace

import (
	"reflect"
)

type attribute struct {
	ty reflect.Type
}

// KeyTable subspaces appropriate type for each parameter key
type KeyTable struct {
	m map[string]attribute
}

// Constructs new table
func NewKeyTable(keytypes ...interface{}) (res KeyTable) {
	if len(keytypes)%2 != 0 {
		panic("odd number arguments in NewTypeKeyTable")
	}

	res = KeyTable{
		m: make(map[string]attribute),
	}

	for i := 0; i < len(keytypes); i += 2 {
		res = res.RegisterType(keytypes[i].([]byte), keytypes[i+1])
	}

	return
}

func isAlphaNumeric(key []byte) bool {
	for _, b := range key {
		if !((48 <= b && b <= 57) || // numeric
			(65 <= b && b <= 90) || // upper case
			(97 <= b && b <= 122)) { // lower case
			return false
		}
	}

	return true
}

// Register single key-type pair
func (t KeyTable) RegisterType(key []byte, ty interface{}) KeyTable {
	if len(key) == 0 {
		panic("cannot register empty key")
	}

	if !isAlphaNumeric(key) {
		panic("non alphanumeric parameter key")
	}

	keystr := string(key)
	if _, ok := t.m[keystr]; ok {
		panic("duplicate parameter key")
	}

	rty := reflect.TypeOf(ty)

	// Indirect rty if it is ptr
	if rty.Kind() == reflect.Ptr {
		rty = rty.Elem()
	}

	t.m[keystr] = attribute{
		ty: rty,
	}

	return t
}

// RegisterParamSet registers multiple pairs from ParamSet
func (t KeyTable) RegisterParamSet(ps ParamSet) KeyTable {
	for _, kvp := range ps.ParamSetPairs() {
		t = t.RegisterType(kvp.Key, kvp.Value)
	}

	return t
}

func (t KeyTable) maxKeyLength() (res int) {
	for k := range t.m {
		l := len(k)
		if l > res {
			res = l
		}
	}

	return
}
