package base83

import (
	"fmt"
	"math"
	"strings"
)

const characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"

// An InvalidCharacterError occurs when a characters is found which is not part of the Base83 character set.
type InvalidCharacterError rune

func (e InvalidCharacterError) Error() string {
	return fmt.Sprintf("base83: invalid string (character %q out of range)", rune(e))
}

// An InvalidLengthError occurs when a given value cannot be encoded to a string of given length.
type InvalidLengthError int

func (e InvalidLengthError) Error() string {
	return fmt.Sprintf("base83: invalid length (%d)", int(e))
}

// Encode will encode the given integer value to a Base83 string with given length.
// If length is too short to encode the given value InvalidLengthError will be returned.
func Encode(value, length int) (string, error) {
	divisor := int(math.Pow(83, float64(length)))
	if value/divisor != 0 {
		return "", InvalidLengthError(length)
	}
	divisor /= 83

	var str strings.Builder
	str.Grow(length)
	for i := 0; i < length; i++ {
		if divisor <= 0 {
			return "", InvalidLengthError(length)
		}
		digit := (value / divisor) % 83
		divisor /= 83
		str.WriteRune(rune(characters[digit]))
	}

	return str.String(), nil
}

// Decode will decode the given Base83 string to an integer.
func Decode(str string) (value int, err error) {
	for _, r := range str {
		idx := strings.IndexRune(characters, r)
		if idx == -1 {
			return 0, InvalidCharacterError(r)
		}
		value = value*83 + idx
	}
	return value, nil
}