// 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) {
	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
		}
		list = &ls
	}
	return
}

func (ls *ListType) ToString(opt FmtOpt) (s string) {
	var sb strings.Builder
	sb.WriteByte('[')
	if len(*ls) > 0 {
		if opt&MultiLine != 0 {
			sb.WriteString("\n  ")
		}
		for i, item := range []any(*ls) {
			if i > 0 {
				if opt&MultiLine != 0 {
					sb.WriteString(",\n  ")
				} else {
					sb.WriteString(", ")
				}
			}
			if s, ok := item.(string); ok {
				sb.WriteByte('"')
				sb.WriteString(s)
				sb.WriteByte('"')
			} else {
				sb.WriteString(fmt.Sprintf("%v", item))
			}
		}
		if opt&MultiLine != 0 {
			sb.WriteByte('\n')
		}
	}
	sb.WriteByte(']')
	s = sb.String()
	if opt&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
}