// Copyright 2020 The Fuchsia 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 fuzzer_corpus

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"

	gidlconfig "go.fuchsia.dev/fuchsia/tools/fidl/gidl/config"
	gidlir "go.fuchsia.dev/fuchsia/tools/fidl/gidl/ir"
	"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)

type distributionEntry struct {
	Source      string `json:"source"`
	Destination string `json:"destination"`
}

type testCase struct {
	name        string
	objectTypes []fidlgen.ObjectType
	bytes       []byte
}

func getEncoding(encodings []gidlir.Encoding) (gidlir.Encoding, bool) {
	for _, encoding := range encodings {
		// Only supported encoding wire format: v1.
		if encoding.WireFormat == gidlir.V1WireFormat {
			return encoding, true
		}
	}

	return gidlir.Encoding{}, false
}

func getHandleDispositionEncoding(encodings []gidlir.HandleDispositionEncoding) (gidlir.HandleDispositionEncoding, bool) {
	for _, encoding := range encodings {
		// Only supported encoding wire format: v1.
		if encoding.WireFormat == gidlir.V1WireFormat {
			return encoding, true
		}
	}

	return gidlir.HandleDispositionEncoding{}, false
}

func getHandleObjectTypes(handles []gidlir.Handle, defs []gidlir.HandleDef) []fidlgen.ObjectType {
	var objectTypes []fidlgen.ObjectType
	for _, h := range handles {
		objectTypes = append(objectTypes, fidlgen.ObjectTypeFromHandleSubtype(defs[h].Subtype))
	}
	return objectTypes
}

func convertEncodeSuccesses(gtcs []gidlir.EncodeSuccess) []testCase {
	var tcs []testCase
	for _, gtc := range gtcs {
		encoding, ok := getHandleDispositionEncoding(gtc.Encodings)
		if !ok {
			continue
		}

		tcs = append(tcs, testCase{
			name:        fmt.Sprintf("EncodeSuccess_%s", gtc.Name),
			objectTypes: getHandleObjectTypes(gidlir.GetHandlesFromHandleDispositions(encoding.HandleDispositions), gtc.HandleDefs),
			bytes:       encoding.Bytes,
		})
	}

	return tcs
}

func convertDecodeSuccesses(gtcs []gidlir.DecodeSuccess) (tcs []testCase) {
	for _, gtc := range gtcs {
		encoding, ok := getEncoding(gtc.Encodings)
		if !ok {
			continue
		}

		tcs = append(tcs, testCase{
			name:        fmt.Sprintf("DecodeSuccess_%s", gtc.Name),
			objectTypes: getHandleObjectTypes(encoding.Handles, gtc.HandleDefs),
			bytes:       encoding.Bytes,
		})
	}

	return tcs
}

func convertDecodeFailures(gtcs []gidlir.DecodeFailure) (tcs []testCase) {
	for _, gtc := range gtcs {
		encoding, ok := getEncoding(gtc.Encodings)
		if !ok {
			continue
		}

		tcs = append(tcs, testCase{
			name:        fmt.Sprintf("DecodeFailure_%s", gtc.Name),
			objectTypes: getHandleObjectTypes(encoding.Handles, gtc.HandleDefs),
			bytes:       encoding.Bytes,
		})
	}

	return tcs
}

func getData(tc testCase) []byte {
	var buf bytes.Buffer

	// Put handle and message data at head of fuzzer input.
	for _, objectType := range tc.objectTypes {
		binary.Write(&buf, binary.LittleEndian, uint32(objectType))
	}
	buf.Write(tc.bytes)

	// Put length-encoding at the tail of fuzzer input.
	binary.Write(&buf, binary.LittleEndian, uint64(len(tc.objectTypes)))

	return buf.Bytes()
}

func writeTestCase(hostDir string, packageDataDir string, tc testCase) (distributionEntry, error) {
	data := getData(tc)

	filePath := path.Join(hostDir, tc.name)
	file, err := os.Create(filePath)
	if err != nil {
		return distributionEntry{}, err
	}
	defer file.Close()

	if _, err = file.Write(data); err != nil {
		return distributionEntry{}, err
	}

	return distributionEntry{
		Source:      filePath,
		Destination: path.Join("data", packageDataDir, tc.name),
	}, err
}

func GenerateConformanceTests(gidl gidlir.All, _ fidlgen.Root, config gidlconfig.GeneratorConfig) ([]byte, error) {
	if config.FuzzerCorpusHostDir == "" {
		return nil, errors.New("Must specify --fuzzer-corpus-host-dir when generating fuzzer_corpus")
	}
	if config.FuzzerCorpusPackageDataDir == "" {
		return nil, errors.New("Must specify --fuzzer-corpus-package-data-dir when generating fuzzer_corpus")
	}

	os.RemoveAll(config.FuzzerCorpusHostDir)
	err := os.MkdirAll(config.FuzzerCorpusHostDir, os.ModePerm)
	if err != nil {
		return nil, err
	}

	var manifest []distributionEntry

	for _, tcs := range [][]testCase{
		convertEncodeSuccesses(gidl.EncodeSuccess),
		convertDecodeSuccesses(gidl.DecodeSuccess),
		convertDecodeFailures(gidl.DecodeFailure),
	} {
		for _, tc := range tcs {
			entry, err := writeTestCase(config.FuzzerCorpusHostDir, config.FuzzerCorpusPackageDataDir, tc)
			if err != nil {
				return nil, err
			}
			manifest = append(manifest, entry)
		}
	}

	return json.Marshal(manifest)
}
