/*
 * Copyright 2021 CloudWeGo Authors
 *
 * 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 descriptor

import (
	"reflect"

	"github.com/cloudwego/gopkg/protocol/thrift"
)

// Type constants in the Thrift protocol
type Type byte

// Types
const (
	STOP   Type = 0
	VOID   Type = 1
	BOOL   Type = 2
	BYTE   Type = 3
	I08    Type = 3
	DOUBLE Type = 4
	I16    Type = 6
	I32    Type = 8
	I64    Type = 10
	STRING Type = 11
	UTF7   Type = 11
	STRUCT Type = 12
	MAP    Type = 13
	SET    Type = 14
	LIST   Type = 15
	UTF8   Type = 16
	UTF16  Type = 17
	// BINARY Type = 18   wrong and unused
	JSON Type = 19
)

var typeNames = map[Type]string{
	STOP:   "STOP",
	VOID:   "VOID",
	BOOL:   "BOOL",
	BYTE:   "BYTE",
	DOUBLE: "DOUBLE",
	I16:    "I16",
	I32:    "I32",
	I64:    "I64",
	STRING: "STRING",
	STRUCT: "STRUCT",
	MAP:    "MAP",
	SET:    "SET",
	LIST:   "LIST",
	UTF8:   "UTF8",
	UTF16:  "UTF16",
}

// String for format and print
func (p Type) String() string {
	if s, ok := typeNames[p]; ok {
		return s
	}
	return "Unknown"
}

// ToThriftTType convert to thrift.TType
//
// Deprecated: use thrift.TType(t) directly
func (p Type) ToThriftTType() thrift.TType {
	return thrift.TType(p)
}

// FromThriftTType ...
//
// This func was used to convert apache thrift.Type to descriptor.Type.
// We should use descriptor.Type(t) directly without binding apache thrift
//
// Deprecated: use descriptor.Type(t)
func FromThriftTType(v interface{}) Type {
	rv := reflect.ValueOf(v)
	if rv.CanUint() { // byte
		return Type(rv.Uint())
	}
	if rv.CanInt() { // int8
		return Type(rv.Int())
	}
	panic(rv.Type().String())
}

// Void use empty struct as void instead of `nil`, because sometimes `nil` was used as optional none
type Void struct{}
