// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.

package graphson

import (
	"reflect"

	jsoniter "github.com/json-iterator/go"
	"github.com/modern-go/reflect2"
)

var (
	typeEncoders = map[string]jsoniter.ValEncoder{}
	typeDecoders = map[string]jsoniter.ValDecoder{}
)

// RegisterTypeEncoder register type encoder for typ.
func RegisterTypeEncoder(typ string, enc jsoniter.ValEncoder) {
	typeEncoders[typ] = enc
}

// RegisterTypeDecoder register type decoder for typ.
func RegisterTypeDecoder(typ string, dec jsoniter.ValDecoder) {
	typeDecoders[typ] = dec
}

type registeredEncoder struct{ jsoniter.ValEncoder }

// EncoderOfNative returns a value encoder of a registered type.
func (encodeExtension) EncoderOfRegistered(typ reflect2.Type) jsoniter.ValEncoder {
	enc := typeEncoders[typ.String()]
	if enc != nil {
		return registeredEncoder{enc}
	}
	if typ.Kind() == reflect.Ptr {
		ptrType := typ.(reflect2.PtrType)
		enc := typeEncoders[ptrType.Elem().String()]
		if enc != nil {
			return registeredEncoder{
				ValEncoder: &jsoniter.OptionalEncoder{
					ValueEncoder: enc,
				},
			}
		}
	}
	return nil
}

// DecoratorOfRegistered decorates a value encoder of a registered type.
func (encodeExtension) DecoratorOfRegistered(enc jsoniter.ValEncoder) jsoniter.ValEncoder {
	if _, ok := enc.(registeredEncoder); ok {
		return enc
	}
	return nil
}

type registeredDecoder struct{ jsoniter.ValDecoder }

// DecoratorOfRegistered returns a value decoder of a registered type.
func (decodeExtension) DecoderOfRegistered(typ reflect2.Type) jsoniter.ValDecoder {
	dec := typeDecoders[typ.String()]
	if dec != nil {
		return registeredDecoder{dec}
	}
	if typ.Kind() == reflect.Ptr {
		ptrType := typ.(reflect2.PtrType)
		dec := typeDecoders[ptrType.Elem().String()]
		if dec != nil {
			return registeredDecoder{
				ValDecoder: &jsoniter.OptionalDecoder{
					ValueType:    ptrType.Elem(),
					ValueDecoder: dec,
				},
			}
		}
	}
	return nil
}

// DecoratorOfNative decorates a value decoder of a registered type.
func (decodeExtension) DecoratorOfRegistered(dec jsoniter.ValDecoder) jsoniter.ValDecoder {
	if _, ok := dec.(registeredDecoder); ok {
		return dec
	}
	return nil
}
