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

// list-type.go
package expr

import (
	"fmt"
	"reflect"
	"strings"
)

type ListType []any

func newListA(listAny ...any) (list *ListType) {
	if listAny == nil {
		listAny = []any{}
	}
	return newList(listAny)
}

func newList(listAny []any) (list *ListType) {
	return NewList(listAny)
}

func NewList(listAny []any) (list *ListType) {
	if listAny != nil {
		ls := make(ListType, len(listAny))
//		for i, item := range listAny {
//			ls[i] = item
//		}
		copy(ls, listAny)
		list = &ls
	}
	return
}

func MakeList(length, capacity int) (list *ListType) {
	if capacity < length {
		capacity = length
	}
	ls := make(ListType, length, capacity)
	list = &ls
	return
}

func ListFromStrings(stringList []string) (list *ListType) {
	list = MakeList(len(stringList), 0)
	for i, s := range stringList {
		(*list)[i] = s
	}
	return
}

func (ls *ListType) ToString(opt FmtOpt) (s string) {
        indent := GetFormatIndent(opt)
        flags := GetFormatFlags(opt)

	var sb strings.Builder
	sb.WriteByte('[')
	if len(*ls) > 0 {
                innerOpt := MakeFormatOptions(flags, indent+1)
                nest := strings.Repeat("  ", indent+1)

		if flags&MultiLine != 0 {
			sb.WriteByte('\n')
			sb.WriteString(nest)
		}
		for i, item := range []any(*ls) {
			if i > 0 {
				if flags&MultiLine != 0 {
					sb.WriteString(",\n")
					sb.WriteString(nest)
				} else {
					sb.WriteString(", ")
				}
			}
			if s, ok := item.(string); ok {
				sb.WriteByte('"')
				sb.WriteString(s)
				sb.WriteByte('"')
			} else if formatter, ok := item.(Formatter); ok {
				sb.WriteString(formatter.ToString(innerOpt))
			} else {
				sb.WriteString(fmt.Sprintf("%v", item))
			}
		}
		if flags&MultiLine != 0 {
			sb.WriteByte('\n')
			sb.WriteString(strings.Repeat("  ", indent))
		}
	}
	sb.WriteByte(']')
	s = sb.String()
	if flags&Truncate != 0 && len(s) > TruncateSize {
		s = TruncateString(s)
	}
	return
}

func (ls *ListType) String() string {
	return ls.ToString(0)
}

func (ls *ListType) TypeName() string {
	return "list"
}

// func (list *ListType) indexDeepCmp(target any) (index int) {
// 	index = -1
// 	for i, item := range *list {
// 		if reflect.DeepEqual(item, target) {
// 			index = i
// 			break
// 		}
// 	}
// 	return
// }

func (ls *ListType) contains(t *ListType) (answer bool) {
	if len(*ls) >= len(*t) {
		answer = true
		for _, item := range *t {
			if answer = ls.indexDeepSameCmp(item) >= 0; !answer {
				break
			}
		}
	}
	return
}

func (list *ListType) indexDeepSameCmp(target any) (index int) {
	var eq bool
	var err error
	index = -1
	for i, item := range *list {
		if eq, err = deepSame(item, target, sameContent); err != nil {
			break
		} else if eq {
			index = i
			break
		}
	}
	return
}

func sameContent(a, b any) (same bool, err error) {
	la, _ := a.(*ListType)
	lb, _ := b.(*ListType)
	if len(*la) == len(*lb) {
		same = true
		for _, item := range *la {
			if pos := lb.indexDeepSameCmp(item); pos < 0 {
				same = false
				break
			}
		}
	}
	return
}

func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
	if isNumOrFract(a) && isNumOrFract(b) {
		if IsNumber(a) && IsNumber(b) {
			if IsInteger(a) && IsInteger(b) {
				li, _ := a.(int64)
				ri, _ := b.(int64)
				eq = li == ri
			} else {
				eq = numAsFloat(a) == numAsFloat(b)
			}
		} else {
			var cmp int
			if cmp, err = cmpAnyFract(a, b); err == nil {
				eq = cmp == 0
			}
		}
	} else if deepCmp != nil && IsList(a) && IsList(b) {
		eq, err = deepCmp(a, b)
	} else {
		eq = reflect.DeepEqual(a, b)
	}

	return
}

func (list *ListType) setItem(index int64, value any) (err error) {
	if index >= 0 && index < int64(len(*list)) {
		(*list)[index] = value
	} else {
		err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*list)-1)
	}
	return
}

