// Copyright (c) 2016 Kiel University and others.
//
// 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 p3order

import (
	
	

	basegraph 
	
)

// SweepCopy stores the node order and each node's current port order. Port
// orders are indexed by layer and position, just as in ELK 0.8.1; this means
// they travel with a candidate sweep order rather than with arena IDs.
type SweepCopy struct {
	nodeOrder  [][]layered.LNodeID
	portOrders [][][]layered.LPortID
}

func ( *layered.LGraphArena,  [][]layered.LNodeID) *SweepCopy {
	 := &SweepCopy{
		nodeOrder:  cloneNodeOrder(),
		portOrders: make([][][]layered.LPortID, len()),
	}
	for ,  := range  {
		.portOrders[] = make([][]layered.LPortID, len())
		for ,  := range  {
			.portOrders[][] = append([]layered.LPortID(nil), .Node().Ports...)
		}
	}
	return 
}

func ( *SweepCopy) () *SweepCopy {
	if  == nil {
		return nil
	}
	 := &SweepCopy{
		nodeOrder:  cloneNodeOrder(.nodeOrder),
		portOrders: make([][][]layered.LPortID, len(.portOrders)),
	}
	for  := range .portOrders {
		.portOrders[] = make([][]layered.LPortID, len(.portOrders[]))
		for  := range .portOrders[] {
			.portOrders[][] = append(
				[]layered.LPortID(nil), .portOrders[][]...,
			)
		}
	}
	return 
}

// Nodes returns the saved order. Callers must treat it as read-only, matching
// SweepCopy.nodes()'s contract in Java.
func ( *SweepCopy) () [][]layered.LNodeID { return .nodeOrder }

func ( *SweepCopy) (
	 *layered.LGraphArena,
	 layered.LGraphID,
	 bool,
) {
	 := make([]layered.LNodeID, 0)
	 := make([]layered.LNodeID, 0)
	 := make(map[layered.LNodeID]bool)
	 := append([]layered.LayerID(nil), .Graph().Layers...)
	for ,  := range  {
		 = [:0]
		for  := range .Layer().Nodes {
			 := .nodeOrder[][]
			.Node().ID = 
			if .Node().Type == layered.NorthSouthPortNode {
				 = append(, )
			}
			.Layer().Nodes[] = 
			.Node().Ports = append([]layered.LPortID(nil), .portOrders[][]...)
			.InvalidatePortSideCache()
			if  {
				 := basegraph.Get(&.Node().Properties, layered.PortConstraintsOption)
				if !.IsOrderFixed() {
					basegraph.Set(&.Node().Properties, layered.PortConstraintsOption, layered.PortConstraintsFixedOrder)
				}
			}
		}

		for ,  := range  {
			,  := basegraph.TryGet(&.Node().Properties, layered.InLayerLayoutUnit)
			if ! {
				continue
			}
			.assertCorrectPortSides(, , )
			for ,  := range []layered.LNodeID{, } {
				if ![] {
					[] = true
					 = append(, )
				}
			}
		}
	}

	for ,  := range  {
		 := .Node().Ports
		sort.SliceStable(, func(,  int) bool {
			return comparePortsCombined(, [], []) < 0
		})
		.Node().Ports = 
		.CachePortSides()
	}
}

func ( *SweepCopy) (
	 *layered.LGraphArena,
	,  layered.LNodeID,
) {
	if len(.Node().Ports) == 0 {
		return
	}
	 := .Node().Ports[0]
	,  := basegraph.TryGet(&.Port().Properties, layered.OriginalElement)
	if ! || .Kind != layered.OriginLPort {
		return
	}
	for ,  := range .Node().Ports {
		if  != .LPort {
			continue
		}
		 := .Port()
		switch {
		case .Side == layered.NorthSide && .Node().ID > .Node().ID:
			.SetPortSide(, layered.SouthSide)
			if .ExplicitAnchor {
				.Anchor.Y = .Size.Y - .Anchor.Y
			}
		case .Side == layered.SouthSide && .Node().ID > .Node().ID:
			.SetPortSide(, layered.NorthSide)
			if .ExplicitAnchor {
				.Anchor.Y = -(.Size.Y - .Anchor.Y)
			}
		}
		return
	}
}

func cloneNodeOrder( [][]layered.LNodeID) [][]layered.LNodeID {
	if  == nil {
		return nil
	}
	 := make([][]layered.LNodeID, len())
	for  := range  {
		[] = append([]layered.LNodeID(nil), []...)
	}
	return 
}

func comparePortsCombined( *layered.LGraphArena, ,  layered.LPortID) int {
	,  := .Port(), .Port()
	if .Side != .Side {
		return cmp.Compare(.Side, .Side)
	}
	if .Node == nil {
		return 0
	}
	 := basegraph.Get(&.Node(*.Node).Properties, layered.PortConstraintsOption)
	if !.IsOrderFixed() {
		return 0
	}
	if  == layered.PortConstraintsFixedOrder {
		,  := rawInt(&.Properties, "org.eclipse.elk.port.index")
		,  := rawInt(&.Properties, "org.eclipse.elk.port.index")
		if  &&  &&  !=  {
			return cmp.Compare(, )
		}
	}
	switch .Side {
	case layered.NorthSide:
		return compareFloat(.Position.X, .Position.X)
	case layered.EastSide:
		return compareFloat(.Position.Y, .Position.Y)
	case layered.SouthSide:
		return compareFloat(.Position.X, .Position.X)
	case layered.WestSide:
		return compareFloat(.Position.Y, .Position.Y)
	default:
		panic("p3order: fixed-order port has undefined side")
	}
}

func rawInt( rawPropertyMap,  string) (int, bool) {
	,  := .Raw()
	if ! {
		return 0, false
	}
	switch typed := .(type) {
	case int:
		return , true
	case int32:
		return int(), true
	case int64:
		return int(), true
	default:
		return 0, false
	}
}

func compareFloat(,  float64) int {
	if  <  {
		return -1
	}
	if  >  {
		return 1
	}
	return 0
}