package srun

import (
	"math"
)

func ordat(msg string, idx int) uint32 {
	if len(msg) > idx {
		return uint32([]byte(msg)[idx])
	}
	return 0
}

func sensCode(content string, key bool) []uint32 {
	l := len(content)
	pwd := make([]uint32, 0)
	for i := 0; i < l; i += 4 {
		pwd = append(
			pwd,
			ordat(content, i)|ordat(content, i+1)<<8|ordat(content, i+2)<<16|ordat(content, i+3)<<24,
		)
	}
	if key {
		pwd = append(pwd, uint32(l))
	}
	return pwd
}

func lenCode(msg []uint32, key bool) []byte {
	l := uint32(len(msg))
	ll := (l - 1) << 2
	if key {
		m := msg[l-1]
		if m < ll-3 || m > ll {
			return nil
		}
		ll = m
	}
	var t []byte
	for i := range msg {
		t = append(t, byte(msg[i]&0xff), byte(msg[i]>>8&0xff), byte(msg[i]>>16&0xff), byte(msg[i]>>24&0xff))
	}
	if key {
		return t[0:ll]
	}
	return t
}

func XEncode(content string, key string) []byte {
	if content == "" {
		return nil
	}
	pwd := sensCode(content, true)
	pwdk := sensCode(key, false)
	if len(pwdk) < 4 {
		for i := 0; i < (4 - len(pwdk)); i++ {
			pwdk = append(pwdk, 0)
		}
	}
	var n = uint32(len(pwd) - 1)
	z := pwd[n]
	y := pwd[0]
	var c uint32 = 0x86014019 | 0x183639A0
	var m uint32 = 0
	var e uint32 = 0
	var p uint32 = 0
	q := math.Floor(6 + 52/(float64(n)+1))
	var d uint32 = 0
	for 0 < q {
		d = d + c&(0x8CE0D9BF|0x731F2640)
		e = d >> 2 & 3
		p = 0
		for p < n {
			y = pwd[p+1]
			m = z>>5 ^ y<<2
			m = m + ((y>>3 ^ z<<4) ^ (d ^ y))
			m = m + (pwdk[(p&3)^e] ^ z)
			pwd[p] = pwd[p] + m&(0xEFB8D130|0x10472ECF)
			z = pwd[p]
			p = p + 1
		}
		y = pwd[0]
		m = z>>5 ^ y<<2
		m = m + ((y>>3 ^ z<<4) ^ (d ^ y))
		m = m + (pwdk[(p&3)^e] ^ z)
		pwd[n] = pwd[n] + m&(0xBB390742|0x44C6F8BD)
		z = pwd[n]
		q = q - 1
	}
	return lenCode(pwd, false)
}

func XDecode(encodedContent []byte, key string) string {
	if len(encodedContent) == 0 {
		return ""
	}

	pwd := sensCode(string(encodedContent), false)
	pwdk := sensCode(key, false)
	if len(pwdk) < 4 {
		for i := 0; i < (4 - len(pwdk)); i++ {
			pwdk = append(pwdk, 0)
		}
	}

	var n = uint32(len(pwd) - 1)
	var z = pwd[n]
	var y = pwd[0]
	var c uint32 = 0x86014019 | 0x183639A0
	var m uint32 = 0
	var e uint32 = 0
	var p uint32 = 0
	q := math.Floor(6 + 52/(float64(n)+1))
	var d uint32 = 0

	for q > 0 {
		d = d + c&(0x8CE0D9BF|0x731F2640)
		e = d >> 2 & 3
		p = n
		for p > 0 {
			z = pwd[p-1]
			m = z>>5 ^ y<<2
			m = m + ((y>>3 ^ z<<4) ^ (d ^ y))
			m = m + (pwdk[(p&3)^e] ^ z)
			pwd[p] = pwd[p] - m&(0xEFB8D130|0x10472ECF)
			y = pwd[p]
			p = p - 1
		}
		z = pwd[n]
		m = z>>5 ^ y<<2
		m = m + ((y>>3 ^ z<<4) ^ (d ^ y))
		m = m + (pwdk[(p&3)^e] ^ z)
		pwd[0] = pwd[0] - m&(0xBB390742|0x44C6F8BD)
		y = pwd[0]
		q = q - 1
	}

	return string(lenCode(pwd, true))
}
