// Copyright 2023 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package s2

// Shape interface enforcement
var _ Shape = (*LaxLoop)(nil)

// LaxLoop represents a closed loop of edges surrounding an interior
// region. It is similar to Loop except that this class allows
// duplicate vertices and edges. Loops may have any number of vertices,
// including 0, 1, or 2. (A one-vertex loop defines a degenerate edge
// consisting of a single point.)
//
// Note that LaxLoop is faster to initialize and more compact than
// Loop, but does not support the same operations as Loop.
type LaxLoop struct {
	numVertices int
	vertices    []Point
}

// LaxLoopFromPoints creates a LaxLoop from the given points.
func LaxLoopFromPoints(vertices []Point) *LaxLoop {
	l := &LaxLoop{
		numVertices: len(vertices),
		vertices:    make([]Point, len(vertices)),
	}
	copy(l.vertices, vertices)
	return l
}

// LaxLoopFromLoop creates a LaxLoop from the given Loop, copying its points.
func LaxLoopFromLoop(loop *Loop) *LaxLoop {
	if loop.IsFull() {
		panic("FullLoops are not yet supported")
	}
	if loop.IsEmpty() {
		return &LaxLoop{}
	}

	l := &LaxLoop{
		numVertices: len(loop.vertices),
		vertices:    make([]Point, len(loop.vertices)),
	}
	copy(l.vertices, loop.vertices)
	return l
}

func (l *LaxLoop) vertex(i int) Point { return l.vertices[i] }
func (l *LaxLoop) NumEdges() int      { return l.numVertices }
func (l *LaxLoop) Edge(e int) Edge {
	e1 := e + 1
	if e1 == l.numVertices {
		e1 = 0
	}
	return Edge{l.vertices[e], l.vertices[e1]}

}
func (l *LaxLoop) Dimension() int                 { return 2 }
func (l *LaxLoop) ReferencePoint() ReferencePoint { return referencePointForShape(l) }
func (l *LaxLoop) NumChains() int                 { return min(1, l.numVertices) }
func (l *LaxLoop) Chain(i int) Chain              { return Chain{0, l.numVertices} }
func (l *LaxLoop) ChainEdge(i, j int) Edge {
	var k int
	if j+1 == l.numVertices {
		k = j + 1
	}
	return Edge{l.vertices[j], l.vertices[k]}
}
func (l *LaxLoop) ChainPosition(e int) ChainPosition { return ChainPosition{0, e} }
func (l *LaxLoop) IsEmpty() bool                     { return defaultShapeIsEmpty(l) }
func (l *LaxLoop) IsFull() bool                      { return defaultShapeIsFull(l) }
func (l *LaxLoop) typeTag() typeTag                  { return typeTagNone }
func (l *LaxLoop) privateInterface()                 {}

// TODO(roberts): Remaining to be ported from C++:
// LaxClosedPolyline
// VertexIDLaxLoop
