// fpe is the  Format-Preserving Encryption.
package fpe

import (
	"errors"
	"fmt"
	"math/big"
	"strings"
)

type Numeral = uint16

type Char = uint16

// Alphabet is the interface for an alphabet.
type Alphabet interface {
	// fpe实际上是可对任意的字符表进行编码，比如JPEG2000，编码范围为0xff8f的序列，且不以0xff结尾。
	// 但是这里的Encode只处理utf-8的string
	Encode(s string) ([]Numeral, error)
	Decode(numString []Numeral) (string, error)

	// 如果是二进制类的数据
	// EncodeBlob(b []byte) ([]Numeral, error)
	// DecodeBlob(numString []Numeral) ([]byte, error)

	Radix() int
	Num(X []Numeral) *big.Int

	// X = Str_radix^m(C)
	Str(X []Numeral, x *big.Int)
}

var (
	// ASCII code 32-126, all printable characters.
	Printable = NewAlphabet(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
	Numeric   = NewAlphabet("0123456789")
	Lower     = NewAlphabet("abcdefghijklmnopqrstuvwxyz")
	Upper     = NewAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
	Alpha     = NewAlphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	NumAlpha  = NewAlphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
)

type FPE interface {
	Encrypt(tweak []byte, X []Numeral) ([]Numeral, error)
	Decrypt(tweak []byte, X []Numeral) ([]Numeral, error)
	Alphabet
}

// GenericAlphabet 可以处理asicc字符集
type GenericAlphabet struct {
	tblBuf [128]Char
	tbl    [][]Numeral

	r Radix
}

// Assume the characters in alphabet are all ASCII now.
// Unicode characters not supported now.
func NewAlphabet(alphabet string) Alphabet {
	r := len(alphabet)

	res := &GenericAlphabet{}
	res.r.Set(r)

	tblBuf := res.tblBuf
	for i := range tblBuf {
		tblBuf[i] = 128
	}
	for _, d := range alphabet {
		if d > 127 {
			panic("only ASCII characters are supported now")
		}
		tblBuf[d] = Char(d)
	}
	tbls := make([][]Numeral, 0)
	start := -1
	for i, d := range tblBuf {
		switch {
		case d == 128:
			if start >= 0 {
				tbls = append(tbls, tblBuf[start:i])
				start = -1
			}
		case d < 128:
			if start < 0 {
				start = i
			}
		}
	}
	res.tbl = tbls
	return res
}

var numSwitch = false

// Num implements Alphabet.
func (ga *GenericAlphabet) Num(X []Char) *big.Int {
	return ga.r.Num(X)
}

// x should < radix^(lenX)
func (ga *GenericAlphabet) Str(X []Numeral, x *big.Int) {
	ga.r.Str(X, x)
}

// FromString implements Alphabet.
func (ga *GenericAlphabet) Encode(s string) ([]Numeral, error) {
	res := make([]Numeral, 0, len(s))
	for _, c := range s {
		if c>>16 != 0 {
			return nil, fmt.Errorf("unsupported character %c", c)
		}

		if idx := index(Char(c), ga.tbl); idx >= 0 {
			res = append(res, Numeral(idx))
		} else {
			return nil, errors.New("bad characters")
		}
	}
	return res, nil
}

// // Radix implements Alphabet.
func (ga *GenericAlphabet) Radix() int {
	return int(ga.r.r)
}

// ToString implements Alphabet.
func (ga *GenericAlphabet) Decode(numString []Numeral) (string, error) {
	var sb strings.Builder
	radix := uint16(ga.r.r)
	for _, d := range numString {
		if d >= radix {
			return "", errors.New("bad numeric string")
		}
		if _, err := sb.WriteRune(rune(char(d, ga.tbl))); err != nil {
			return "", errors.New("bad rune")
		}
	}
	return sb.String(), nil
}

var _ Alphabet = &GenericAlphabet{}

// tbls[i] are continuous Char
// tbls[0][0], tbls[0][1], ..., tbls[0][n0],
// tbls[1][0], tbls[1][1], ..., tbls[1][n1],
// ...
func index(c Char, tbls [][]Char) int {
	n := 0
	for _, tbl := range tbls {
		n0 := int(c) - int(tbl[0])
		if n0 >= 0 && n0 < len(tbl) {
			return n + n0
		}
		n += len(tbl)
	}
	return -1
}

// Assume n < radix
func char(n Numeral, tbls [][]Char) Char {
	m := int(n)
	for _, tbl := range tbls {
		if m < len(tbl) {
			return tbl[m]
		}
		m -= len(tbl)
	}
	panic("numeric great than radix")
}
