// Copyright 2019 Google LLC
//
// 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 testutil

import (
	"encoding/hex"
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"testing"
)

const (
	wycheproofDir = "wycheproof/testvectors"
)

// SkipTestIfTestSrcDirIsNotSet skips the test if TEST_SRCDIR is not set.
// This is necessary when not using Blaze/Bazel, as we don't have a solution for referencing non-Go
// resources that are external to the repository with Go tooling.
func SkipTestIfTestSrcDirIsNotSet(t *testing.T) {
	t.Helper()
	if _, ok := os.LookupEnv("TEST_SRCDIR"); !ok {
		t.Skip("TEST_SRCDIR not found")
	}
}

// WycheproofSuite represents the common elements of the top level
// object in a Wycheproof json file. Implementations should embed
// WycheproofSuite in a struct that strongly types the testGroups
// field. See wycheproofutil_test.go for an example.
type WycheproofSuite struct {
	Algorithm        string            `json:"algorithm"`
	GeneratorVersion string            `json:"generatorVersion"`
	NumberOfTests    int               `json:"numberOfTests"`
	Notes            map[string]string `json:"notes"`
}

// WycheproofGroup represents the common elements of a testGroups
// object in a Wycheproof suite. Implementations should embed
// WycheproofGroup in a struct that strongly types its list of cases.
// See wycheproofutil_test.go for an example.
type WycheproofGroup struct {
	Type string `json:"type"`
}

// WycheproofCase represents the common elements of a tests object
// in a Wycheproof group. Implementation should embed WycheproofCase
// in a struct that contains fields specific to the test type.
// See wycheproofutil_test.go for an example.
type WycheproofCase struct {
	CaseID  int      `json:"tcId"`
	Comment string   `json:"comment"`
	Result  string   `json:"result"`
	Flags   []string `json:"flags"`
}

// HexBytes is a helper type for unmarshalling a byte sequence represented as a
// hex encoded string.
type HexBytes []byte

// UnmarshalText converts a hex encoded string into a sequence of bytes.
func (a *HexBytes) UnmarshalText(text []byte) error {
	decoded, err := hex.DecodeString(string(text))
	if err != nil {
		return err
	}

	*a = decoded
	return nil
}

// PopulateSuite opens filename from the Wycheproof test vectors directory and
// populates suite with the decoded JSON data.
//
// When using this in a test function, the function should start with
// SkipTestIfTestSrcDirIsNotSet(), to expediently skip the test.
func PopulateSuite(suite interface{}, filename string) error {
	srcDir, ok := os.LookupEnv("TEST_SRCDIR")
	if !ok {
		return errors.New("TEST_SRCDIR not found")
	}
	f, err := os.Open(filepath.Join(srcDir, wycheproofDir, filename))
	if err != nil {
		return err
	}
	parser := json.NewDecoder(f)
	if err := parser.Decode(suite); err != nil {
		return err
	}
	return nil
}
