// Copyright (c) 2009, 2015 Kiel University and others.
// Copyright (c) 2009, 2017 Kiel University and others.
// Copyright (c) 2026 The elk-go Authors.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

package graph

import (
	
	
)

// Property identifies a typed graph property. Identity follows ELK and is
// based solely on ID.
type Property[ any] struct {
	ID                 string
	Default            func() 
	MaterializeDefault bool
}

func [ any]( string) Property[] { return Property[]{ID: } }

func [ any]( string,  func() ) Property[] {
	return Property[]{ID: , Default: }
}

func [ any]( string,  func() ) Property[] {
	return Property[]{ID: , Default: , MaterializeDefault: true}
}

// PropertyEntry is one insertion-ordered property map entry.
type PropertyEntry struct {
	ID    string
	Value any
}

// PropertyMap preserves Java LinkedHashMap insertion order while retaining
// constant-time lookup.
type PropertyMap struct {
	entries []PropertyEntry
	index   map[string]int
}

func ( *PropertyMap) () {
	if .index == nil {
		.index = make(map[string]int)
	}
}

func ( *PropertyMap) () int    { return len(.entries) }
func ( *PropertyMap) () bool { return len(.entries) == 0 }

func ( *PropertyMap) ( string) bool {
	if .index == nil {
		return false
	}
	,  := .index[]
	return 
}

func ( *PropertyMap) ( string) (any, bool) {
	if .index == nil {
		return nil, false
	}
	,  := .index[]
	if ! {
		return nil, false
	}
	return .entries[].Value, true
}

func ( *PropertyMap) ( string,  any) {
	.ensureIndex()
	if ,  := .index[];  {
		.entries[].Value = 
		return
	}
	.index[] = len(.entries)
	.entries = append(.entries, PropertyEntry{ID: , Value: })
}

func ( *PropertyMap) ( string) {
	if .index == nil {
		return
	}
	,  := .index[]
	if ! {
		return
	}
	copy(.entries[:], .entries[+1:])
	.entries = .entries[:len(.entries)-1]
	delete(.index, )
	for  := ;  < len(.entries); ++ {
		.index[.entries[].ID] = 
	}
}

func ( *PropertyMap) () []PropertyEntry {
	return append([]PropertyEntry(nil), .entries...)
}

func ( *PropertyMap) ( *PropertyMap) {
	for ,  := range .entries {
		.SetRaw(.ID, cloneValue(.Value))
	}
}

func ( *PropertyMap) () PropertyMap {
	var  PropertyMap
	.CopyFrom()
	return 
}

// Get returns an explicitly stored value, then the property's default, then
// T's zero value. Cloneable Java defaults are materialized on first read.
func [ any]( *PropertyMap,  Property[])  {
	if ,  := TryGet(, );  {
		return 
	}
	if .Default != nil {
		 := .Default()
		if .MaterializeDefault {
			.SetRaw(.ID, cloneValue())
		}
		return 
	}
	var  
	return 
}

func [ any]( *PropertyMap,  Property[]) (, bool) {
	,  := .Raw(.ID)
	if  {
		if ,  := .();  {
			return , true
		}
	}
	var  
	return , false
}

func [ any]( *PropertyMap,  Property[],  ) {
	.SetRaw(.ID, )
}

func [ any]( *PropertyMap,  Property[]) bool {
	return .Has(.ID)
}
func [ any]( *PropertyMap,  Property[]) { .Delete(.ID) }

type cloneable interface{ Clone() any }

func cloneValue( any) any {
	if  == nil {
		return nil
	}
	if ,  := .(cloneable);  {
		return .Clone()
	}
	switch typed := .(type) {
	case []string:
		return append([]string(nil), ...)
	case []int:
		return append([]int(nil), ...)
	case []float64:
		return append([]float64(nil), ...)
	default:
		return 
	}
}

// JavaString produces the value spelling consumed and emitted by ELK's
// layout option metadata layer.
func ( any) string {
	switch typed := .(type) {
	case nil:
		return "null"
	case string:
		return 
	case bool:
		return strconv.FormatBool()
	case int:
		return strconv.Itoa()
	case int32:
		return strconv.FormatInt(int64(), 10)
	case int64:
		return strconv.FormatInt(, 10)
	case float32:
		return strconv.FormatFloat(float64(), 'g', -1, 32)
	case float64:
		return strconv.FormatFloat(, 'g', -1, 64)
	case fmt.Stringer:
		return .String()
	default:
		return fmt.Sprint()
	}
}