package decimal128

import "fmt"

const (
	payloadOpCompose Payload = iota + 1
	payloadOpFromFloat32
	payloadOpFromFloat64
	payloadOpMustParse
	payloadOpNaN
	payloadOpParse
	payloadOpScan
	payloadOpUnmarshalText

	payloadOpAdd
	payloadOpLog
	payloadOpLog10
	payloadOpLog1p
	payloadOpLog2
	payloadOpMul
	payloadOpPow
	payloadOpQuo
	payloadOpQuoRem
	payloadOpSqrt
	payloadOpSub
)

const (
	payloadValPosZero Payload = iota + 1
	payloadValNegZero
	payloadValPosFinite
	payloadValNegFinite
	payloadValPosInfinite
	payloadValNegInfinite
)

// Payload returns the first 64 bits of the payload of a NaN. If the NaN was
// generated by an operation in this package, the contents of the payload
// provides information about what kind of operation was being performed that
// lead to the NaN being generated. Payload panics if d is not a NaN.
func (d Decimal) Payload() Payload {
	if !d.IsNaN() {
		panic("Decimal(!NaN).Payload()")
	}

	return Payload(d.lo)
}

// Payload represents the payload value of a NaN decimal. This value can
// contain additional information about the operation that caused the value to
// be set to NaN.
type Payload uint64

// String returns a string representation of the payload.
func (p Payload) String() string {
	if p == 0 {
		return "Payload(0)"
	}

	if p > 0x00ff_ffff {
		return fmt.Sprintf("Payload(%d)", uint64(p))
	}

	switch p & 0xff {
	case payloadOpCompose:
		return "Compose()"
	case payloadOpFromFloat32:
		return "FromFloat32()"
	case payloadOpFromFloat64:
		return "FromFloat64()"
	case payloadOpMustParse:
		return "MustParse()"
	case payloadOpNaN:
		return "NaN()"
	case payloadOpParse:
		return "Parse()"
	case payloadOpScan:
		return "Scan()"
	case payloadOpUnmarshalText:
		return "UnmarshalText()"
	case payloadOpAdd:
		return "Add(" + p.argString(8) + ", " + p.argString(16) + ")"
	case payloadOpLog:
		return "Log(" + p.argString(8) + ")"
	case payloadOpLog10:
		return "Log10(" + p.argString(8) + ")"
	case payloadOpLog1p:
		return "Log1p(" + p.argString(8) + ")"
	case payloadOpLog2:
		return "Log2(" + p.argString(8) + ")"
	case payloadOpMul:
		return "Mul(" + p.argString(8) + ", " + p.argString(16) + ")"
	case payloadOpPow:
		return "Pow(" + p.argString(8) + ", " + p.argString(16) + ")"
	case payloadOpQuo:
		return "Quo(" + p.argString(8) + ", " + p.argString(16) + ")"
	case payloadOpQuoRem:
		return "QuoRem(" + p.argString(8) + ", " + p.argString(16) + ")"
	case payloadOpSqrt:
		return "Sqrt(" + p.argString(8) + ")"
	case payloadOpSub:
		return "Sub(" + p.argString(8) + ", " + p.argString(16) + ")"
	default:
		return fmt.Sprintf("Payload(%d)", uint64(p))
	}
}

func (p Payload) argString(offset int) string {
	switch p >> offset & 0xff {
	case payloadValPosZero:
		return "Zero"
	case payloadValNegZero:
		return "-Zero"
	case payloadValPosFinite:
		return "Finite"
	case payloadValNegFinite:
		return "-Finite"
	case payloadValPosInfinite:
		return "Infinite"
	case payloadValNegInfinite:
		return "-Infinite"
	default:
		return "Unknown"
	}
}
