// Copyright 2018 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_test

import (
	"fmt"

	"github.com/golang/geo/s1"
	"github.com/golang/geo/s2"
)

func ExampleRect_DistanceToLatLng() {
	r := s2.RectFromLatLng(s2.LatLngFromDegrees(-1, -1)).AddPoint(s2.LatLngFromDegrees(1, 1))

	printDist := func(lat, lng float64) {
		fmt.Printf("%f\n", r.DistanceToLatLng(s2.LatLngFromDegrees(lat, lng))/s1.Degree)
	}

	fmt.Println("Distances next to the rectangle.")
	printDist(-2, 0)
	printDist(0, -2)
	printDist(2, 0)
	printDist(0, 2)

	fmt.Println("Distances beyond the corners of the rectangle.")
	printDist(-2, -2)
	printDist(-2, 2)
	printDist(2, 2)
	printDist(2, -2)

	fmt.Println("Distance within the rectangle.")
	printDist(0, 0)
	printDist(0.5, 0)
	printDist(0, 0.5)
	printDist(-0.5, 0)
	printDist(0, -0.5)

	// Output:
	// Distances next to the rectangle.
	// 1.000000
	// 1.000000
	// 1.000000
	// 1.000000
	// Distances beyond the corners of the rectangle.
	// 1.413962
	// 1.413962
	// 1.413962
	// 1.413962
	// Distance within the rectangle.
	// 0.000000
	// 0.000000
	// 0.000000
	// 0.000000
	// 0.000000
}

func ExamplePolygonFromOrientedLoops() {
	// Let's define three loops, in format World Geodetic System 1984,
	// the format that geoJSON uses. The third loop is a hole in the second,
	// the first loop is remote from the others. Loops 1 and 2 are counter-clockwise,
	// while loop 3 is clockwise.
	l1 := [][]float64{
		{102.0, 2.0},
		{103.0, 2.0},
		{103.0, 3.0},
		{102.0, 3.0},
	}
	l2 := [][]float64{
		{100.0, 0.0},
		{101.0, 0.0},
		{101.0, 1.0},
		{100.0, 1.0},
	}
	l3 := [][]float64{
		{100.2, 0.2},
		{100.2, 0.8},
		{100.8, 0.8},
		{100.8, 0.2},
	}
	toLoop := func(points [][]float64) *s2.Loop {
		var pts []s2.Point
		for _, pt := range points {
			pts = append(pts, s2.PointFromLatLng(s2.LatLngFromDegrees(pt[1], pt[0])))
		}
		return s2.LoopFromPoints(pts)
	}
	// We can combine all loops into a single polygon:
	p := s2.PolygonFromOrientedLoops([]*s2.Loop{toLoop(l1), toLoop(l2), toLoop(l3)})

	for i, loop := range p.Loops() {
		fmt.Printf("loop %d is hole: %t\n", i, loop.IsHole())
	}
	fmt.Printf("Combined area: %.7f\n", p.Area())

	// Note how the area of the polygon is the area of l1 + l2 - invert(l3), because l3 is a hole:
	p12 := s2.PolygonFromOrientedLoops([]*s2.Loop{toLoop(l1), toLoop(l2)})
	p3 := s2.PolygonFromOrientedLoops([]*s2.Loop{toLoop(l3)})
	p3.Invert()
	fmt.Printf("l1+l2 = %.7f, inv(l3) = %.7f; l1+l2 - inv(l3) = %.7f\n", p12.Area(), p3.Area(), p12.Area()-p3.Area())
	// Output:
	// loop 0 is hole: false
	// loop 1 is hole: false
	// loop 2 is hole: true
	// Combined area: 0.0004993
	// l1+l2 = 0.0006089, inv(l3) = 0.0001097; l1+l2 - inv(l3) = 0.0004993
}

func ExampleEdgeQuery_FindEdges_findClosestEdges() {
	// Let's start with one or more Polylines that we wish to compare against.
	polylines := []s2.Polyline{
		// This is an iteration = 3 Koch snowflake centered at the
		// center of the continental US.
		//
		// This was generated as:
		//
		// fractal := newFractal()
		// fractal.setLevelForApproxMaxEdges(48)
		// fractal.makeLoop(randomFrameAtPoint(
		//	PointFromLatLng(LatLngFromDegrees(39.833, -98.55))), s1.Angle(0.15))
		{
			s2.PointFromLatLng(s2.LatLngFromDegrees(47.5467, -103.6035)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.9214, -103.7320)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.1527, -105.8000)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(44.2866, -103.8538)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(42.6450, -103.9695)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.8743, -105.9314)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(42.7141, -107.8226)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.0743, -107.8377)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(40.2486, -109.6869)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(39.4333, -107.8521)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.7936, -107.8658)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(38.5849, -106.0503)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.7058, -104.2841)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(36.0638, -104.3793)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(35.3062, -106.1585)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.4284, -104.4703)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.8024, -104.5573)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(33.5273, -102.8163)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.6053, -101.1982)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.2313, -101.0361)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.9120, -99.2189)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(33.9382, -97.6134)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.3185, -97.8489)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.9481, -96.0510)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(31.9449, -94.5321)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(33.5521, -94.2263)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.1285, -92.3780)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(35.1678, -93.9070)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(36.7893, -93.5734)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.3529, -91.6381)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(36.2777, -90.1050)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.8824, -89.6824)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(38.3764, -87.7108)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(39.4869, -89.2407)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.0883, -88.7784)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(40.5829, -90.8289)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.6608, -92.4765)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(43.2777, -92.0749)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(43.7961, -89.9408)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(44.8865, -91.6533)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(46.4844, -91.2100)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.9512, -93.4327)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(46.9863, -95.2792)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.3722, -95.6237)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(44.7496, -97.7776)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.7189, -99.6629)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(47.3422, -99.4244)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(46.6523, -101.6056)),
		},
	}

	// We will use a point that we wish to find the edges which are closest to it.
	point := s2.PointFromLatLng(s2.LatLngFromDegrees(37.7, -122.5))

	// Load them into a ShapeIndex.
	index := s2.NewShapeIndex()
	for _, l := range polylines {
		index.Add(&l)
	}

	// Create a ClosestEdgeQuery and specify that we want the 7 closest.
	//
	// Note that if you were to request all results, and compare to the results
	// of a FurthestEdgeQuery, the results will not be a complete reversal. This
	// is because the distances being reported are to the closest end of a given
	// edge, while the Furthest query is reporting distances to the farthest end
	// of a given edge.
	q := s2.NewClosestEdgeQuery(index, s2.NewClosestEdgeQueryOptions().MaxResults(7))
	target := s2.NewMinDistanceToPointTarget(point)

	for _, result := range q.FindEdges(target) {
		polylineIndex := result.ShapeID()
		edgeIndex := result.EdgeID()
		distance := result.Distance()
		fmt.Printf("Polyline %d, Edge %d is %0.4f degrees from Point (%0.6f, %0.6f, %0.6f)\n",
			polylineIndex, edgeIndex,
			distance.Angle().Degrees(), point.X, point.Y, point.Z)
	}
	// Output:
	// Polyline 0, Edge 7 is 10.2718 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 8 is 10.2718 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 9 is 11.5362 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 10 is 11.5602 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 6 is 11.8071 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 5 is 12.2577 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 11 is 12.9502 degrees from Point (-0.425124, -0.667311, 0.611527)

}

func ExampleEdgeQuery_FindEdges_findFurthestEdges() {
	// Let's start with one or more Polylines that we wish to compare against.
	polylines := []s2.Polyline{
		// This is an iteration = 3 Koch snowflake centered at the
		// center of the continental US.
		{
			s2.PointFromLatLng(s2.LatLngFromDegrees(47.5467, -103.6035)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.9214, -103.7320)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.1527, -105.8000)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(44.2866, -103.8538)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(42.6450, -103.9695)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.8743, -105.9314)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(42.7141, -107.8226)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.0743, -107.8377)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(40.2486, -109.6869)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(39.4333, -107.8521)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.7936, -107.8658)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(38.5849, -106.0503)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.7058, -104.2841)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(36.0638, -104.3793)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(35.3062, -106.1585)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.4284, -104.4703)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.8024, -104.5573)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(33.5273, -102.8163)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.6053, -101.1982)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.2313, -101.0361)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.9120, -99.2189)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(33.9382, -97.6134)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.3185, -97.8489)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(32.9481, -96.0510)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(31.9449, -94.5321)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(33.5521, -94.2263)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(34.1285, -92.3780)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(35.1678, -93.9070)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(36.7893, -93.5734)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.3529, -91.6381)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(36.2777, -90.1050)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(37.8824, -89.6824)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(38.3764, -87.7108)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(39.4869, -89.2407)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.0883, -88.7784)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(40.5829, -90.8289)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(41.6608, -92.4765)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(43.2777, -92.0749)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(43.7961, -89.9408)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(44.8865, -91.6533)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(46.4844, -91.2100)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.9512, -93.4327)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(46.9863, -95.2792)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.3722, -95.6237)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(44.7496, -97.7776)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(45.7189, -99.6629)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(47.3422, -99.4244)),
			s2.PointFromLatLng(s2.LatLngFromDegrees(46.6523, -101.6056)),
		},
	}

	// We will use a point that we want to find the edges which are furthest from it.
	point := s2.PointFromLatLng(s2.LatLngFromDegrees(37.7, -122.5))

	// Load them into a ShapeIndex
	index := s2.NewShapeIndex()
	for _, l := range polylines {
		index.Add(&l)
	}

	// Create a FurthestEdgeQuery and specify that we want the 3 furthest.
	q := s2.NewFurthestEdgeQuery(index, s2.NewFurthestEdgeQueryOptions().MaxResults(3))
	target := s2.NewMaxDistanceToPointTarget(point)

	for _, result := range q.FindEdges(target) {
		polylineIndex := result.ShapeID()
		edgeIndex := result.EdgeID()
		distance := result.Distance()
		fmt.Printf("Polyline %d, Edge %d is %0.3f degrees from Point (%0.6f, %0.6f, %0.6f)\n",
			polylineIndex, edgeIndex,
			distance.Angle().Degrees(), point.X, point.Y, point.Z)
	}
	// Output:
	// Polyline 0, Edge 31 is 27.245 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 32 is 27.245 degrees from Point (-0.425124, -0.667311, 0.611527)
	// Polyline 0, Edge 33 is 26.115 degrees from Point (-0.425124, -0.667311, 0.611527)

}
