// Copyright ©2019 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package layout_test

import (
	"path/filepath"
	"testing"

	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/simple"
	"gonum.org/v1/gonum/spatial/r2"
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/vg"

	. "gonum.org/v1/gonum/graph/layout"
)

var (
	// tag is modified in isomap_noasm_test.go to "_noasm" when any
	// build tag prevents use of the assembly numerical kernels.
	tag string

	// arch is modified in isomap_arm64_test.go to "_arm64" on arm64
	// and "_386" on 386 to allow differences in numerical precision
	// to be allowed for.
	arch string
)

func TestIsomapR2(t *testing.T) {
	isomapR2Tests := []struct {
		name string
		g    graph.Graph
	}{
		{
			name: "line_isomap",
			g: func() graph.Graph {
				edges := []simple.Edge{
					{F: simple.Node(0), T: simple.Node(1)},
				}
				g := simple.NewUndirectedGraph()
				for _, e := range edges {
					g.SetEdge(e)
				}
				return orderedGraph{g}
			}(),
		},
		{
			name: "square_isomap",
			g: func() graph.Graph {
				edges := []simple.Edge{
					{F: simple.Node(0), T: simple.Node(1)},
					{F: simple.Node(0), T: simple.Node(2)},
					{F: simple.Node(1), T: simple.Node(3)},
					{F: simple.Node(2), T: simple.Node(3)},
				}
				g := simple.NewUndirectedGraph()
				for _, e := range edges {
					g.SetEdge(e)
				}
				return orderedGraph{g}
			}(),
		},
		{
			name: "tetrahedron_isomap",
			g: func() graph.Graph {
				edges := []simple.Edge{
					{F: simple.Node(0), T: simple.Node(1)},
					{F: simple.Node(0), T: simple.Node(2)},
					{F: simple.Node(0), T: simple.Node(3)},
					{F: simple.Node(1), T: simple.Node(2)},
					{F: simple.Node(1), T: simple.Node(3)},
					{F: simple.Node(2), T: simple.Node(3)},
				}
				g := simple.NewUndirectedGraph()
				for _, e := range edges {
					g.SetEdge(e)
				}
				return orderedGraph{g}
			}(),
		},
		{
			name: "sheet_isomap",
			g: func() graph.Graph {
				edges := []simple.Edge{
					{F: simple.Node(0), T: simple.Node(1)},
					{F: simple.Node(0), T: simple.Node(3)},
					{F: simple.Node(1), T: simple.Node(2)},
					{F: simple.Node(1), T: simple.Node(4)},
					{F: simple.Node(2), T: simple.Node(5)},
					{F: simple.Node(3), T: simple.Node(4)},
					{F: simple.Node(3), T: simple.Node(6)},
					{F: simple.Node(4), T: simple.Node(5)},
					{F: simple.Node(4), T: simple.Node(7)},
					{F: simple.Node(5), T: simple.Node(8)},
					{F: simple.Node(6), T: simple.Node(7)},
					{F: simple.Node(7), T: simple.Node(8)},
				}
				g := simple.NewUndirectedGraph()
				for _, e := range edges {
					g.SetEdge(e)
				}
				return orderedGraph{g}
			}(),
		},
		{
			name: "tube_isomap",
			g: func() graph.Graph {
				edges := []simple.Edge{
					{F: simple.Node(0), T: simple.Node(1)},
					{F: simple.Node(0), T: simple.Node(2)},
					{F: simple.Node(0), T: simple.Node(3)},
					{F: simple.Node(1), T: simple.Node(2)},
					{F: simple.Node(1), T: simple.Node(4)},
					{F: simple.Node(2), T: simple.Node(5)},
					{F: simple.Node(3), T: simple.Node(4)},
					{F: simple.Node(3), T: simple.Node(5)},
					{F: simple.Node(3), T: simple.Node(6)},
					{F: simple.Node(4), T: simple.Node(5)},
					{F: simple.Node(4), T: simple.Node(7)},
					{F: simple.Node(5), T: simple.Node(8)},
					{F: simple.Node(6), T: simple.Node(7)},
					{F: simple.Node(6), T: simple.Node(8)},
					{F: simple.Node(7), T: simple.Node(8)},
				}
				g := simple.NewUndirectedGraph()
				for _, e := range edges {
					g.SetEdge(e)
				}
				return orderedGraph{g}
			}(),
		},
		{
			name: "wp_page_isomap", // https://en.wikipedia.org/wiki/PageRank#/media/File:PageRanks-Example.jpg
			g: func() graph.Graph {
				edges := []simple.Edge{
					{F: simple.Node(0), T: simple.Node(3)},
					{F: simple.Node(1), T: simple.Node(2)},
					{F: simple.Node(1), T: simple.Node(3)},
					{F: simple.Node(1), T: simple.Node(4)},
					{F: simple.Node(1), T: simple.Node(5)},
					{F: simple.Node(1), T: simple.Node(6)},
					{F: simple.Node(1), T: simple.Node(7)},
					{F: simple.Node(1), T: simple.Node(8)},
					{F: simple.Node(3), T: simple.Node(4)},
					{F: simple.Node(4), T: simple.Node(5)},
					{F: simple.Node(4), T: simple.Node(6)},
					{F: simple.Node(4), T: simple.Node(7)},
					{F: simple.Node(4), T: simple.Node(8)},
					{F: simple.Node(4), T: simple.Node(9)},
					{F: simple.Node(4), T: simple.Node(10)},
				}
				g := simple.NewUndirectedGraph()
				for _, e := range edges {
					g.SetEdge(e)
				}
				return orderedGraph{g}
			}(),
		},
	}

	for _, test := range isomapR2Tests {
		o := NewOptimizerR2(test.g, IsomapR2{}.Update)
		var n int
		for o.Update() {
			n++
		}
		p := plot.New()
		p.Add(render{o})
		p.HideAxes()
		path := filepath.Join("testdata", test.name+tag+arch+".png")
		err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, path)
		if err != nil {
			t.Errorf("unexpected error: %v", err)
			continue
		}
		ok := checkRenderedLayout(t, path)
		if !ok {
			got := make(map[int64]r2.Vec)
			nodes := test.g.Nodes()
			for nodes.Next() {
				id := nodes.Node().ID()
				got[id] = o.Coord2(id)
			}
			t.Logf("got node positions: %#v", got)
		}
	}
}
