// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// dict-type.go
package dict

import (
	"fmt"
	"reflect"
	"strings"

	"git.portale-stac.it/go-pkg/expr/kern"
)

const DictTypeName = "dict"

type DictType map[any]any

func IsDict(v any) (ok bool) {
	_, ok = v.(*DictType)
	return ok
}

func MakeDict() (dict *DictType) {
	d := make(DictType)
	dict = &d
	return
}

func NewDict(dictAny map[any]any) (dict *DictType) {
	var d DictType
	if dictAny != nil {
		d = make(DictType, len(dictAny))
		for i, item := range dictAny {
			d[i] = item
		}
	} else {
		d = make(DictType)
	}
	dict = &d
	return
}

func newDict(dictAny map[any]kern.Term) (dict *DictType) {
	// TODO Change with a call to NewDict()
	var d DictType
	if dictAny != nil {
		d = make(DictType, len(dictAny))
		for i, item := range dictAny {
			d[i] = item
		}
	} else {
		d = make(DictType)
	}
	dict = &d
	return
}

func (dict *DictType) toMultiLine(sb *strings.Builder, opt kern.FmtOpt) {
	indent := kern.GetFormatIndent(opt)
	flags := kern.GetFormatFlags(opt)
	//sb.WriteString(strings.Repeat("  ", indent))
	sb.WriteByte('{')

	if len(*dict) > 0 {
		innerOpt := kern.MakeFormatOptions(flags, indent+1)
		nest := strings.Repeat("  ", indent+1)
		sb.WriteByte('\n')

		first := true
		for name, value := range *dict {
			if first {
				first = false
			} else {
				sb.WriteByte(',')
				sb.WriteByte('\n')
			}

			sb.WriteString(nest)
			if key, ok := name.(string); ok {
				sb.WriteByte('"')
				sb.WriteString(key)
				sb.WriteByte('"')
			} else {
				fmt.Fprintf(sb, "%v", name)
			}
			sb.WriteString(": ")
			if f, ok := value.(kern.Formatter); ok {
				sb.WriteString(f.ToString(innerOpt))
			} else if _, ok = value.(kern.Functor); ok {
				sb.WriteString("func(){}")
			} else {
				fmt.Fprintf(sb, "%v", value)
			}
		}
		sb.WriteByte('\n')
		sb.WriteString(strings.Repeat("  ", indent))
	}
	sb.WriteString("}")
}

func (dict *DictType) ToString(opt kern.FmtOpt) string {
	var sb strings.Builder
	flags := kern.GetFormatFlags(opt)
	if flags&kern.MultiLine != 0 {
		dict.toMultiLine(&sb, opt)
	} else {
		sb.WriteByte('{')
		first := true
		for key, value := range *dict {
			if first {
				first = false
			} else {
				sb.WriteString(", ")
			}
			if s, ok := key.(string); ok {
				sb.WriteByte('"')
				sb.WriteString(s)
				sb.WriteByte('"')
			} else {
				fmt.Fprintf(&sb, "%v", key)
			}
			sb.WriteString(": ")
			if formatter, ok := value.(kern.Formatter); ok {
				sb.WriteString(formatter.ToString(opt))
			} else if t, ok := value.(kern.Term); ok {
				sb.WriteString(t.String())
			} else {
				fmt.Fprintf(&sb, "%#v", value)
			}
		}
		sb.WriteByte('}')
	}
	return sb.String()
}

func (dict *DictType) String() string {
	return dict.ToString(0)
}

func (dict *DictType) TypeName() string {
	return DictTypeName
}

func (dict *DictType) HasKey(target any) (ok bool) {
	for key := range *dict {
		if ok = reflect.DeepEqual(key, target); ok {
			break
		}
	}
	return
}

func (dict *DictType) SetItem(key any, value any) {
	(*dict)[key] = value
}

func (dict *DictType) GetItem(key any) (value any, exists bool) {
	value, exists = (*dict)[key]
	return
}

func (dict *DictType) FindKey(item any) (key any, exists bool) {
	for k, v := range *dict {
		if kern.Equal(v, item) {
			key = k
			exists = true
			break
		}
	}
	return
}

func (dict *DictType) Clone() kern.Cloner {
	c := newDict(nil)
	for k, v := range *dict {
		(*c)[k] = kern.Clone(v)
	}
	return c
}

func (dict *DictType) Merge(second *DictType) {
	if second != nil {
		for k, v := range *second {
			(*dict)[k] = v
		}
	}
}

func (dict *DictType) EqualTo(other kern.Equaler) (answer bool) {
	if dict2, ok := other.(*DictType); ok {
		answer = dict.Equals(*dict2)
	}
	return
}

func (dict *DictType) Equals(dict2 DictType) (answer bool) {
	if dict2 != nil && len(*dict) == len(dict2) {
		answer = true
		for key, value1 := range *dict {
			if value2, exists := dict2.GetItem(key); exists {
				if !kern.Equal(value1, value2) {
					answer = false
					break
				}
			} else {
				answer = false
				break
			}
		}
	}
	return
}

////////////////

// type DictFormat interface {
// 	ToDict() *DictType
// }
