// Copyright ©2018 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 sigmajs

import (
	"encoding/json"
	"os"
	"path/filepath"
	"reflect"
	"testing"
)

var sigmajsExampleTests = []struct {
	path           string
	wantNodes      int
	wantEdges      int
	wantGraph      *Graph
	wantAttributes map[string]bool
}{
	{
		path:      "geolocalized.json",
		wantNodes: 17,
		wantEdges: 35,
		wantGraph: &Graph{
			Nodes: []Node{
				{
					ID: "n1",
					Attributes: map[string]interface{}{
						"label":     "n1",
						"longitude": 2.48,
						"latitude":  50.93,
						"size":      "5.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n2",
					Attributes: map[string]interface{}{
						"label":     "n2",
						"latitude":  50.88,
						"longitude": 2.0,
						"size":      "5.0",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n4",
					Attributes: map[string]interface{}{
						"label":     "n4",
						"latitude":  49.4,
						"longitude": 0.19,
						"size":      "6.0",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n5",
					Attributes: map[string]interface{}{
						"label":     "n5",
						"latitude":  48.49,
						"longitude": -1.92,
						"size":      "6.0",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n6",
					Attributes: map[string]interface{}{
						"label":     "n6",
						"latitude":  48.26,
						"longitude": -4.38,
						"size":      "4.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n7",
					Attributes: map[string]interface{}{
						"label":     "n7",
						"latitude":  47.15,
						"longitude": -2.09,
						"size":      "6.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n8",
					Attributes: map[string]interface{}{
						"label":     "n8",
						"latitude":  46.02,
						"longitude": -1.04,
						"size":      "6.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n9",
					Attributes: map[string]interface{}{
						"label":     "n9",
						"latitude":  43.22,
						"longitude": -1.85,
						"size":      "5.0",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n10",
					Attributes: map[string]interface{}{
						"label":     "n10",
						"latitude":  42.38,
						"longitude": 3.18,
						"color":     "rgb(1,179,255)",
						"size":      "4.0",
					},
				},
				{
					ID: "n11",
					Attributes: map[string]interface{}{
						"label":     "n11",
						"latitude":  43.47,
						"longitude": 4.04,
						"size":      "5.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n12",
					Attributes: map[string]interface{}{
						"label":     "n12",
						"latitude":  42.9,
						"longitude": 6.59,
						"size":      "5.0",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n13",
					Attributes: map[string]interface{}{
						"label":     "n13",
						"latitude":  43.62,
						"longitude": 7.66,
						"size":      "6.0",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n14",
					Attributes: map[string]interface{}{
						"label":     "n14",
						"latitude":  46.05,
						"longitude": 6.19,
						"size":      "6.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n15",
					Attributes: map[string]interface{}{
						"label":     "n15",
						"latitude":  47.43,
						"longitude": 7.65,
						"size":      "6.0",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n16",
					Attributes: map[string]interface{}{
						"label":     "n16",
						"latitude":  48.9,
						"longitude": 8.32,
						"size":      "5.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "n17",
					Attributes: map[string]interface{}{
						"label":     "n17",
						"latitude":  49.83,
						"longitude": 4.94,
						"size":      "6.5",
						"color":     "rgb(1,179,255)",
					},
				},
				{
					ID: "Paris",
					Attributes: map[string]interface{}{
						"label":     "Paris",
						"latitude":  48.72,
						"longitude": 2.46,
						"size":      "9.0",
						"color":     "rgb(1,179,255)",
					},
				},
			},
			Edges: []Edge{
				{ID: "8", Source: "n1", Target: "Paris"},
				{ID: "7", Source: "n2", Target: "n4"},
				{ID: "28", Source: "n4", Target: "n1"},
				{ID: "30", Source: "n4", Target: "n7"},
				{ID: "26", Source: "n5", Target: "n1"},
				{ID: "27", Source: "n5", Target: "n2"},
				{ID: "0", Source: "n6", Target: "n5"},
				{ID: "29", Source: "n7", Target: "n5"},
				{ID: "1", Source: "n7", Target: "n8"},
				{ID: "17", Source: "n7", Target: "Paris"},
				{ID: "10", Source: "n8", Target: "n13"},
				{ID: "18", Source: "n8", Target: "Paris"},
				{ID: "15", Source: "n9", Target: "n8"},
				{ID: "34", Source: "n10", Target: "n9"},
				{ID: "31", Source: "n10", Target: "n11"},
				{ID: "11", Source: "n11", Target: "n13"},
				{ID: "13", Source: "n11", Target: "n14"},
				{ID: "32", Source: "n12", Target: "n10"},
				{ID: "12", Source: "n12", Target: "n11"},
				{ID: "23", Source: "n12", Target: "n13"},
				{ID: "33", Source: "n13", Target: "n10"},
				{ID: "25", Source: "n13", Target: "n14"},
				{ID: "14", Source: "n14", Target: "n9"},
				{ID: "5", Source: "n14", Target: "n17"},
				{ID: "19", Source: "n14", Target: "Paris"},
				{ID: "6", Source: "n15", Target: "n8"},
				{ID: "22", Source: "n15", Target: "n16"},
				{ID: "20", Source: "n15", Target: "Paris"},
				{ID: "4", Source: "n16", Target: "n15"},
				{ID: "24", Source: "n16", Target: "Paris"},
				{ID: "9", Source: "n17", Target: "n7"},
				{ID: "21", Source: "n17", Target: "n17"},
				{ID: "2", Source: "Paris", Target: "n4"},
				{ID: "3", Source: "Paris", Target: "n17"},
				{ID: "16", Source: "Paris", Target: "Paris"},
			},
		},
	},
	{
		path:      "arctic.json",
		wantNodes: 1715,
		wantEdges: 6676,
		wantAttributes: map[string]bool{
			"label":              true,
			"x":                  true,
			"y":                  true,
			"color":              true,
			"size":               true,
			"attributes":         true,
			"attributes.nodedef": true,
		},
	},
}

func TestUnmarshal(t *testing.T) {
	for _, test := range sigmajsExampleTests {
		data, err := os.ReadFile(filepath.Join("testdata", test.path))
		if err != nil {
			t.Errorf("failed to read %q: %v", test.path, err)
			continue
		}
		var got Graph
		err = json.Unmarshal(data, &got)
		if err != nil {
			t.Errorf("failed to unmarshal %q: %v", test.path, err)
			continue
		}
		if len(got.Nodes) != test.wantNodes {
			t.Errorf("unexpected result for order of %q: got:%d want:%d", test.path, len(got.Nodes), test.wantNodes)
		}
		if len(got.Edges) != test.wantEdges {
			t.Errorf("unexpected result for size of %q: got:%d want:%d", test.path, len(got.Edges), test.wantEdges)
		}
		if test.wantGraph != nil && !reflect.DeepEqual(&got, test.wantGraph) {
			t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, test.wantGraph)
		}
		if test.wantAttributes != nil {
			var paths []string
			for _, n := range got.Nodes {
				paths = attrPaths(paths, "", n.Attributes)
			}
			gotAttrs := make(map[string]bool)
			for _, p := range paths {
				gotAttrs[p] = true
			}
			if !reflect.DeepEqual(gotAttrs, test.wantAttributes) {
				t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, gotAttrs, test.wantAttributes)
			}
		}
	}
}

func attrPaths(dst []string, prefix string, m map[string]interface{}) []string {
	for k, v := range m {
		path := prefix
		if path != "" {
			path += "."
		}
		if v, ok := v.(map[string]interface{}); ok {
			dst = attrPaths(dst, path+k, v)
		}
		dst = append(dst, path+k)
	}
	return dst
}

func TestMarshal(t *testing.T) {
	for _, test := range sigmajsExampleTests {
		data, err := os.ReadFile(filepath.Join("testdata", test.path))
		if err != nil {
			t.Errorf("failed to read %q: %v", test.path, err)
			continue
		}
		var want Graph
		err = json.Unmarshal(data, &want)
		if err != nil {
			t.Errorf("failed to unmarshal %q: %v", test.path, err)
			continue
		}
		marshaled, err := json.Marshal(want)
		if err != nil {
			t.Errorf("failed to unmarshal %q: %v", test.path, err)
			continue
		}
		var got Graph
		err = json.Unmarshal(marshaled, &got)
		if err != nil {
			t.Errorf("failed to unmarshal %q: %v", test.path, err)
			continue
		}
		if !reflect.DeepEqual(got, want) {
			t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, want)
		}
	}
}
