package gerrors

import (
	"fmt"
	"io"

	"xdx.jelly/xgcl/gerrors/internal/errors" //github.com/pkg/errors
)

// exports github.com/pkg/errors's API
var As = errors.As
var Is = errors.Is
var Cause = errors.Cause
var Unwrap = errors.Unwrap
var WithMessage = errors.WithMessage
var WithMessagef = errors.WithMessagef
var WithStack = errors.WithStack
var Wrap = errors.Wrap
var Wrapf = errors.Wrapf

// Unused
// var Errorf = errors.Errorf
// var New = errors.New
// type Frame = errors.Frame
// type StackTrace = errors.StackTrace

func Format(code uint32, msg string) string {
	return fmt.Sprintf("[0x%08x]%s", code, msg)
}

type chainedError struct {
	err   error
	cause error
}

func (e *chainedError) Error() string {
	return fmt.Sprintf("%s, caused by %s", e.err, e.cause)
}
func (e *chainedError) Cause() error {
	return e.cause
}

func (e *chainedError) Unwrap() error {
	return e.cause
}

// 定义fmt.Printf("%+v", e)打印格式
func (e *chainedError) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			if e := e.Cause(); e != nil {
				fmt.Fprintf(s, "%+v\n", e)
			}
			_, _ = io.WriteString(s, e.err.Error())
			return
		}
		fallthrough
	case 's':
		_, _ = io.WriteString(s, e.Error())
	case 'q':
		fmt.Fprintf(s, "%q", e.Error())
	}
}

func (e *chainedError) Is(target error) bool {
	return Is(e.err, target) || Is(e.cause, target)
}

func ChainErrors(e error, cause error) error {
	return &chainedError{
		err:   e,
		cause: cause,
	}
}

// withAnnotating wraps an error with annotatings.
type withAnnotating struct {
	err        error
	annotating string
}

func (e *withAnnotating) Error() string {
	return fmt.Sprintf("%s(%s)", e.err, e.annotating)
}

func (e *withAnnotating) Cause() error {
	return Cause(e.err)
}

func (e *withAnnotating) Unwrap() error {
	return Unwrap(e.err)
}

func (e *withAnnotating) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			if e := e.Cause(); e != nil {
				fmt.Fprintf(s, "%+v\n", e)
			}
			io.WriteString(s, e.Error())
			return
		}
		fallthrough
	case 's':
		_, _ = io.WriteString(s, e.Error())
	case 'q':
		fmt.Fprintf(s, "%q", e.Error())
	}
}

func (e *withAnnotating) Is(target error) bool {
	return Is(e.err, target)
}

// WithAnnotating add an annotating to err
func WithAnnotating(err error, annotating string) error {
	switch err := err.(type) {
	case *chainedError:
		err.err = WithAnnotating(err.err, annotating)
		return err
	default:
		return &withAnnotating{
			err:        err,
			annotating: annotating,
		}
	}
}

// WithAnnotatingf add an format annotating to err
func WithAnnotatingf(err error, format string, args ...interface{}) error {
	return WithAnnotating(err, fmt.Sprintf(format, args...))
}

type ErrorCode uint32

func (e ErrorCode) Error() string {
	return Format(uint32(e), "")
}

func (e ErrorCode) Code() uint32 {
	return uint32(e)
}
