package org.openrs2.crypto

public class IsaacRandom : StreamCipher {
    private var count = 0
    private val rsl: IntArray
    private val mem = IntArray(SIZE)
    private var a = 0
    private var b = 0
    private var c = 0

    public constructor() {
        rsl = IntArray(SIZE)
        init(false)
    }

    public constructor(seed: IntArray) {
        require(seed.size <= SIZE)

        rsl = seed.copyOf(SIZE)
        init(true)
    }

    private fun init(flag: Boolean) {
        var a = GOLDEN_RATIO
        var b = GOLDEN_RATIO
        var c = GOLDEN_RATIO
        var d = GOLDEN_RATIO
        var e = GOLDEN_RATIO
        var f = GOLDEN_RATIO
        var g = GOLDEN_RATIO
        var h = GOLDEN_RATIO

        for (i in 0 until 4) {
            a = a xor (b shl 11)
            d += a
            b += c

            b = b xor (c ushr 2)
            e += b
            c += d

            c = c xor (d shl 8)
            f += c
            d += e

            d = d xor (e ushr 16)
            g += d
            e += f

            e = e xor (f shl 10)
            h += e
            f += g

            f = f xor (g ushr 4)
            a += f
            g += h

            g = g xor (h shl 8)
            b += g
            h += a

            h = h xor (a ushr 9)
            c += h
            a += b
        }

        for (i in 0 until SIZE step 8) {
            if (flag) {
                a += rsl[i]
                b += rsl[i + 1]
                c += rsl[i + 2]
                d += rsl[i + 3]
                e += rsl[i + 4]
                f += rsl[i + 5]
                g += rsl[i + 6]
                h += rsl[i + 7]
            }

            a = a xor (b shl 11)
            d += a
            b += c

            b = b xor (c ushr 2)
            e += b
            c += d

            c = c xor (d shl 8)
            f += c
            d += e

            d = d xor (e ushr 16)
            g += d
            e += f

            e = e xor (f shl 10)
            h += e
            f += g

            f = f xor (g ushr 4)
            a += f
            g += h

            g = g xor (h shl 8)
            b += g
            h += a

            h = h xor (a ushr 9)
            c += h
            a += b

            mem[i] = a
            mem[i + 1] = b
            mem[i + 2] = c
            mem[i + 3] = d
            mem[i + 4] = e
            mem[i + 5] = f
            mem[i + 6] = g
            mem[i + 7] = h
        }

        if (flag) {
            for (i in 0 until SIZE step 8) {
                a += mem[i]
                b += mem[i + 1]
                c += mem[i + 2]
                d += mem[i + 3]
                e += mem[i + 4]
                f += mem[i + 5]
                g += mem[i + 6]
                h += mem[i + 7]

                a = a xor (b shl 11)
                d += a
                b += c

                b = b xor (c ushr 2)
                e += b
                c += d

                c = c xor (d shl 8)
                f += c
                d += e

                d = d xor (e ushr 16)
                g += d
                e += f

                e = e xor (f shl 10)
                h += e
                f += g

                f = f xor (g ushr 4)
                a += f
                g += h

                g = g xor (h shl 8)
                b += g
                h += a

                h = h xor (a ushr 9)
                c += h
                a += b

                mem[i] = a
                mem[i + 1] = b
                mem[i + 2] = c
                mem[i + 3] = d
                mem[i + 4] = e
                mem[i + 5] = f
                mem[i + 6] = g
                mem[i + 7] = h
            }
        }

        isaac()
        count = SIZE
    }

    private fun isaac() {
        var a = this.a
        var b = this.b + ++this.c

        var i = 0
        var j = SIZE / 2
        var x: Int
        var y: Int
        while (i < SIZE / 2) {
            x = mem[i]
            a = a xor (a shl 13)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b

            x = mem[i]
            a = a xor (a ushr 6)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b

            x = mem[i]
            a = a xor (a shl 2)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b

            x = mem[i]
            a = a xor (a ushr 16)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b
        }

        j = 0
        while (j < SIZE / 2) {
            x = mem[i]
            a = a xor (a shl 13)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b

            x = mem[i]
            a = a xor (a ushr 6)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b

            x = mem[i]
            a = a xor (a shl 2)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b

            x = mem[i]
            a = a xor (a ushr 16)
            a += mem[j++]
            y = mem[(x and MASK) shr 2] + a + b
            mem[i] = y
            b = mem[((y shr SIZEL) and MASK) shr 2] + x
            rsl[i++] = b
        }

        this.b = b
        this.a = a
    }

    public override fun nextInt(): Int {
        if (count-- == 0) {
            isaac()
            count = SIZE - 1
        }

        return rsl[count]
    }

    private companion object {
        private const val SIZEL = 8
        private const val SIZE = 1 shl SIZEL
        private const val MASK = (SIZE - 1) shl 2
        private const val GOLDEN_RATIO = 0x9E3779B9.toInt()
    }
}
