
// encoding provides functions to encode and decode data in hex, base32, base64, and MessagePack

package encoding

// See what encodings are used for which types of data in Specification.md

import "bytes"
import "encoding/hex"
import "encoding/base64"
import "encoding/base32"
import "strings"
import "errors"

import messagepack "github.com/vmihailenco/msgpack/v5"


func DecodeHexStringToBytes(hexInput string)([]byte, error){

	decodedBytes, err := hex.DecodeString(hexInput)
	if (err != nil) { return nil, err }

	return decodedBytes, nil
}

func DecodeHexStringTo3ByteArray(hexInput string)([3]byte, error){

	decodedBytes, err := hex.DecodeString(hexInput)
	if (err != nil) { return [3]byte{}, err }

	if (len(decodedBytes) != 3){
		return [3]byte{}, errors.New("DecodeHexStringTo3ByteArray called with invalid length hex string: " + hexInput)
	}

	result := [3]byte(decodedBytes)

	return result, nil
}

func DecodeHexStringTo16ByteArray(hexInput string)([16]byte, error){

	decodedBytes, err := hex.DecodeString(hexInput)
	if (err != nil) { return [16]byte{}, err }

	if (len(decodedBytes) != 16){
		return [16]byte{}, errors.New("DecodeHexStringTo16ByteArray called with invalid length hex string: " + hexInput)
	}

	result := [16]byte(decodedBytes)

	return result, nil
}

func EncodeBytesToHexString(input []byte)string{

	result := hex.EncodeToString(input)

	return result
}

const base32Charset = "abcdefghijklmnopqrstuvwxyz234567"

func EncodeBytesToBase32String(input []byte)string{

	newEncodingObject := base32.NewEncoding(base32Charset)

	result := newEncodingObject.EncodeToString(input)

	return result
}

func DecodeBase32StringToBytes(input string)([]byte, error){	

	// We have to check for newlines because golang's base32 allows them

	for _, element := range input{

		if (element == '\r' || element == '\n') {
			return nil, errors.New("Invalid base32 input: contains newline.")
		}
	}

	newEncodingObject := base32.NewEncoding(base32Charset)

	decodedBytes, err := newEncodingObject.DecodeString(input)
	if (err != nil) { return nil, err }

	return decodedBytes, nil
}

// Verifies string is a base32 string
// Outputs:
//	-bool: String is base32 charset only
//	-string: Character that is not base32
func VerifyStringContainsOnlyBase32Charset(input string)(bool, string){

	// This iterates through the string's runes
	for _, element := range input {

		characterString := string(element)

		isBase32Character := strings.ContainsAny(characterString, base32Charset)		
		if (isBase32Character == false) {
			return false, characterString
		}
	}
	
	return true, ""
}

func EncodeBytesToBase64String(input []byte) string {

	result := base64.URLEncoding.EncodeToString(input)

	return result
}

func DecodeBase64StringToBytes(base64Input string) ([]byte, error) {

	decodedBytes, err := base64.URLEncoding.DecodeString(base64Input)
	if (err != nil) { return nil, err }

	return decodedBytes, nil
}

func DecodeBase64StringToUnicodeString(base64Input string)(string, error){

	decodedBytes, err := DecodeBase64StringToBytes(base64Input)
	if (err != nil) { return "", err }

	decodedString := string(decodedBytes)

	return decodedString, nil
}

// This will omit empty values
func EncodeMessagePackBytes(input interface{}) ([]byte, error) {

	encoder := messagepack.GetEncoder()
	encoder.SetOmitEmpty(true)
	encoder.UseCompactInts(true)

	var buffer bytes.Buffer
	encoder.Reset(&buffer)

	err := encoder.Encode(input)
	bytes := buffer.Bytes()

	messagepack.PutEncoder(encoder)

	if (err != nil) {
		return nil, err
	}

	return bytes, nil
}

func DecodeRawMessagePackToString(data messagepack.RawMessage)(string, error){

	var outputString string

	err := DecodeMessagePackBytes(false, data, &outputString)
	if (err != nil) { return "", err }

	return outputString, nil
}

func DecodeRawMessagePackToByte(data messagepack.RawMessage)(byte, error){

	var outputByte byte

	err := DecodeMessagePackBytes(false, data, &outputByte)
	if (err != nil) { return 0, err }

	return outputByte, nil
}

func DecodeRawMessagePackToBytes(data messagepack.RawMessage)([]byte, error){

	var outputBytes []byte

	err := DecodeMessagePackBytes(false, data, &outputBytes)
	if (err != nil) { return nil, err }

	return outputBytes, nil
}

// This is used to decode inboxes
func DecodeRawMessagePackTo10ByteArray(data messagepack.RawMessage)([10]byte, error){

	var outputArray [10]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [10]byte{}, err }

	return outputArray, nil
}

// This is used to decode device identifiers
func DecodeRawMessagePackTo11ByteArray(data messagepack.RawMessage)([11]byte, error){

	var outputArray [11]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [11]byte{}, err }

	return outputArray, nil
}

// This is used to decode identity hashes
func DecodeRawMessagePackTo16ByteArray(data messagepack.RawMessage)([16]byte, error){

	var outputArray [16]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [16]byte{}, err }

	return outputArray, nil
}

func DecodeRawMessagePackTo22ByteArray(data messagepack.RawMessage)([22]byte, error){

	var outputArray [22]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [22]byte{}, err }

	return outputArray, nil
}

func DecodeRawMessagePackTo24ByteArray(data messagepack.RawMessage)([24]byte, error){

	var outputArray [24]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [24]byte{}, err }

	return outputArray, nil
}

// This is used to decode message cipher key hashes
func DecodeRawMessagePackTo25ByteArray(data messagepack.RawMessage)([25]byte, error){

	var outputArray [25]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [25]byte{}, err }

	return outputArray, nil
}

func DecodeRawMessagePackTo32ByteArray(data messagepack.RawMessage)([32]byte, error){

	var outputArray [32]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [32]byte{}, err }

	return outputArray, nil
}

func DecodeRawMessagePackTo64ByteArray(data messagepack.RawMessage)([64]byte, error){

	var outputArray [64]byte

	err := DecodeMessagePackBytes(false, data, &outputArray)
	if (err != nil) { return [64]byte{}, err }

	return outputArray, nil
}

func DecodeRawMessagePackToBool(data messagepack.RawMessage)(bool, error){

	var outputBool bool

	err := DecodeMessagePackBytes(false, data, &outputBool)
	if (err != nil) { return false, err }

	return outputBool, nil
}

func DecodeRawMessagePackToInt(data messagepack.RawMessage)(int, error){

	var outputInt int

	err := DecodeMessagePackBytes(false, data, &outputInt)
	if (err != nil) { return 0, err }

	return outputInt, nil
}

func DecodeRawMessagePackToInt64(data messagepack.RawMessage)(int64, error){

	var outputInt int64

	err := DecodeMessagePackBytes(false, data, &outputInt)
	if (err != nil) { return 0, err }

	return outputInt, nil
}

func DecodeRawMessagePackToFloat64(data messagepack.RawMessage)(float64, error){

	var outputFloat float64

	err := DecodeMessagePackBytes(false, data, &outputFloat)
	if (err != nil) { return 0, err }

	return outputFloat, nil
}


func DecodeMessagePackBytes(allowUnknownFields bool, data []byte, output interface{}) error {

	decoder := messagepack.GetDecoder()

	//TODO: Make allowUnknownFields also reject ignored fields

	decoder.DisallowUnknownFields(!allowUnknownFields)

	newReader := bytes.NewReader(data)

	decoder.Reset(newReader)

	err := decoder.Decode(output)

	messagepack.PutDecoder(decoder)

	return err
}



