/*
NAME
  meta_test.go

DESCRIPTION
  See Readme.md

AUTHOR
  Saxon Nelson-Milton <saxon@ausocean.org>

LICENSE
  meta_test.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean)

  It is free software: you can redistribute it and/or modify them
  under the terms of the GNU General Public License as published by the
  Free Software Foundation, either version 3 of the License, or (at your
  option) any later version.

  It is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 for more details.

  You should have received a copy of the GNU General Public License
  along with revid in gpl.txt. If not, see http://www.gnu.org/licenses.
*/

package meta

import (
	"bytes"
	"encoding/binary"
	"reflect"
	"testing"
)

const (
	tstKey1  = "loc"
	tstData1 = "a,b,c"
	tstKey2  = "ts"
	tstData2 = "12345678"
	tstData3 = "d,e,f"
)

// TestAddAndGet ensures that we can add metadata and then successfully get it.
func TestAddAndGet(t *testing.T) {
	meta := New()
	meta.Add(tstKey1, tstData1)
	meta.Add(tstKey2, tstData2)
	if data, ok := meta.Get(tstKey1); !ok {
		t.Errorf("Could not get data for key: %v\n", tstKey1)
		if data != tstData1 {
			t.Error("Did not get expected data")
		}
	}

	if data, ok := meta.Get(tstKey2); !ok {
		t.Errorf("Could not get data for key: %v", tstKey2)
		if data != tstData2 {
			t.Error("Did not get expected data")
		}
	}
}

// TestUpdate checks that we can use Meta.Add to actually update metadata
// if it already exists in the Meta map.
func TestUpdate(t *testing.T) {
	meta := New()
	meta.Add(tstKey1, tstData1)
	meta.Add(tstKey1, tstData3)

	if data, ok := meta.Get(tstKey1); !ok {
		t.Errorf("Could not get data for key: %v\n", tstKey1)
		if data != tstData2 {
			t.Error(`Data did not correctly update for key "loc"`)
		}
	}
}

// TestAll ensures we can get a correct map using Meta.All() after adding some data
func TestAll(t *testing.T) {
	meta := New()
	tstMap := map[string]string{
		tstKey1: tstData1,
		tstKey2: tstData2,
	}

	meta.Add(tstKey1, tstData1)
	meta.Add(tstKey2, tstData2)
	metaMap := meta.All()

	if !reflect.DeepEqual(metaMap, tstMap) {
		t.Errorf("Map not correct. Got: %v, want: %v", metaMap, tstMap)
	}
}

// TestGetAbsentKey ensures that we get the expected error when we try to get with
// key that does not yet exist in the Meta map.
func TestGetAbsentKey(t *testing.T) {
	meta := New()

	if _, ok := meta.Get(tstKey1); ok {
		t.Error("Get for absent key incorrectly returned'ok'")
	}
}

// TestDelete ensures we can remove a data entry in the Meta map.
func TestDelete(t *testing.T) {
	meta := New()
	meta.Add(tstKey1, tstData1)
	meta.Delete(tstKey1)
	if _, ok := meta.Get(tstKey1); ok {
		t.Error("Get incorrectly returned okay for absent key")
	}
}

// TestEncode checks that we're getting the correct byte slice from Meta.Encode().
func TestEncode(t *testing.T) {
	meta := New()
	meta.Add(tstKey1, tstData1)
	meta.Add(tstKey2, tstData2)

	dataLen := len(tstKey1+tstData1+tstKey2+tstData2) + 3
	header := [4]byte{
		0x00,
		0x10,
	}
	binary.BigEndian.PutUint16(header[2:4], uint16(dataLen))
	expectedOut := append(header[:], []byte(
		tstKey1+"="+tstData1+"\t"+
			tstKey2+"="+tstData2)...)

	got := meta.Encode()
	if !bytes.Equal(expectedOut, got) {
		t.Errorf("Did not get expected out. \nGot : %v, \nwant: %v\n", got, expectedOut)
	}
}

// TestGetFrom checks that we can correctly obtain a value for a partiular key
// from a string of metadata using the ReadFrom func.
func TestGetFrom(t *testing.T) {
	tstMeta := append([]byte{0x00, 0x10, 0x00, 0x12}, "loc=a,b,c\tts=12345"...)

	tests := []struct {
		key  string
		want string
	}{
		{
			"loc",
			"a,b,c",
		},
		{
			"ts",
			"12345",
		},
	}

	for _, test := range tests {
		got, err := Get(test.key, []byte(tstMeta))
		if err != nil {
			t.Errorf("Unexpected err: %v\n", err)
		}
		if got != test.want {
			t.Errorf("Did not get expected out. \nGot : %v, \nwant: %v\n", got, test.want)
		}
	}
}

// TestGetAll checks that meta.GetAll can correctly get all metadata
// from descriptor data.
func TestGetAll(t *testing.T) {
	tstMeta := append([]byte{0x00, 0x10, 0x00, 0x12}, "loc=a,b,c\tts=12345"...)
	want := [][2]string{
		{
			"loc",
			"a,b,c",
		},
		{
			"ts",
			"12345",
		},
	}
	got, err := GetAll(tstMeta)
	if err != nil {
		t.Errorf("Unexpected error: %v\n", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Did not get expected out. \nGot : %v, \nWant: %v\n", got, want)
	}
}

// TestKeys checks that we can successfully get keys from some metadata using
// the meta.Keys method.
func TestKeys(t *testing.T) {
	tstMeta := append([]byte{0x00, 0x10, 0x00, 0x12}, "loc=a,b,c\tts=12345"...)
	want := []string{"loc", "ts"}
	got, err := Keys(tstMeta)
	if err != nil {
		t.Errorf("Unexpected error: %v\n", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Did not get expected out. \nGot : %v, \nWant: %v\n", got, want)
	}
}