/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 */

package brotli4cj

import std.io.*

public class Transform {
    static let NUM_RFC_TRANSFORMS: Int32 = 121

    static let RFC_TRANSFORMS = Transform_Transforms(121, 167, 50)

    private static let OMIT_FIRST_LAST_LIMIT: Int32 = 9

    private static let IDENTITY: Int32 = 0

    private static let OMIT_LAST_BASE: Int32 = IDENTITY + 1 - 1  //0

    private static let UPPERCASE_FIRST: Int32 = OMIT_LAST_BASE + OMIT_FIRST_LAST_LIMIT + 1 //10

    private static let UPPERCASE_ALL: Int32 = UPPERCASE_FIRST + 1 //11

    private static let OMIT_FIRST_BASE: Int32 = UPPERCASE_ALL + 1 - 1 //11

    private static let SHIFT_FIRST: Int32 = OMIT_FIRST_BASE + OMIT_FIRST_LAST_LIMIT + 1 //21

    private static let SHIFT_ALL: Int32 = SHIFT_FIRST + 1 //22

    private static let PREFIX_SUFFIX_SRC = "# #s #, #e #.# the #.com/#\u{00C2}\u{00A0}# of # and # in # to #\"#\">#\n#]# for # a # that #. # with #'# from # by #. The # on # as # is #ing #\n\t#:#ed #(# at #ly #=\"# of the #. This #,# not #er #al #='#ful #ive #less #est #ize #ous #"

    private static let TRANSFORMS_SRC = "     !! ! ,  *!  &!  \" !  ) *   * -  ! # !  #!*!  +  ,$ !  -  %  .  / #   0  1 .  \"   2  3!*   4%  ! # /   5  6  7  8 0  1 &   $   9 +   :  ;  < '  !=  >  ?! 4  @ 4  2  &   A *# (   B  C& ) %  ) !*# *-% A +! *.  D! %'  & E *6  F  G% ! *A *%  H! D  I!+!  J!+   K +- *4! A  L!*4  M  N +6  O!*% +.! K *G  P +%(  ! G *D +D  Q +# *K!*G!+D!+# +G +A +4!+% +K!+4!*D!+K!*K"

    static func unpackTransforms(prefixSuffix: Array<UInt8>, prefixSuffixHeads: Array<Int32>, transforms: Array<Int32>, prefixSuffixSrc: String, transformsSrc: String): Unit {
        let prefixSuffixSrcArray: Array<UInt8> = Utils.changeSourceArray(prefixSuffixSrc.toArray())
        let n = Int32(prefixSuffixSrcArray.size)
        var index: Int32 = 1
        var j: Int32 = 0
        for (i in 0..n) {
            let c = Int32((prefixSuffixSrcArray[Int64(i)]))
            if (c == 35) {
                prefixSuffixHeads[Int64(index)] = j
                index++
            } else {
                prefixSuffix[Int64(j)] = UInt8(c)
                j++
            }
        }
        for (i in 0..NUM_RFC_TRANSFORMS * 3) {
            transforms[Int64(i)] = Int32(UInt64(transformsSrc[Int64(i)])) - 32
        }
    }

    static init () {
        unpackTransforms(RFC_TRANSFORMS.prefixSuffixStorage, RFC_TRANSFORMS.prefixSuffixHeads, RFC_TRANSFORMS.triplets, PREFIX_SUFFIX_SRC, TRANSFORMS_SRC)
    }

    static func transformDictionaryWord(dst: Array<UInt8>, dstOffset: Int32, src: ByteBuffer, srcOffset: Int32, wordLen: Int32, transforms: Transform_Transforms, transformIndex: Int32): Int32 {
        var offset: Int32 = dstOffset
        let triplets = transforms.triplets
        let prefixSuffixStorage = transforms.prefixSuffixStorage
        let prefixSuffixHeads = transforms.prefixSuffixHeads
        let transformOffset = 3 * transformIndex
        let prefixIdx = triplets[Int64(transformOffset)]
        let transformType = triplets[Int64(transformOffset + 1)]
        let suffixIdx = triplets[Int64(transformOffset + 2)]
        var prefix = prefixSuffixHeads[Int64(prefixIdx)]
        let prefixEnd = prefixSuffixHeads[Int64(prefixIdx + 1)]
        var suffix = prefixSuffixHeads[Int64(suffixIdx)]
        let suffixEnd = prefixSuffixHeads[Int64(suffixIdx + 1)]
        var omitFirst = transformType - OMIT_FIRST_BASE
        var omitLast = transformType - OMIT_LAST_BASE
        if (omitFirst < 1 || omitFirst > OMIT_FIRST_LAST_LIMIT) {
            omitFirst = 0
        }
        if (omitLast < 1 || omitLast > OMIT_FIRST_LAST_LIMIT) {
            omitLast = 0
        }
        while (prefix != prefixEnd) {
            dst[Int64(match (0) { case _ => offset++; offset - 1 })] = prefixSuffixStorage[Int64(match (0) { case _ => prefix++; prefix - 1 })]
        }
        var len = wordLen
        if (omitFirst > len) {
            omitFirst = len
        }
        var dictOffset: Int32 = srcOffset + omitFirst
        len -= omitFirst
        len -= omitLast
        var i = len
        while (i > 0) {
            let tempArr = Array<UInt8>(1) {_ => 0} 
            src.seek(Begin(Int64(dictOffset)))
            src.read(tempArr)
            dst[Int64(offset)] = tempArr[0]
            offset++
            dictOffset++
            i--
        }
        if (transformType == UPPERCASE_FIRST || transformType == UPPERCASE_ALL) {
            var uppercaseOffset = offset - len
            if (transformType == UPPERCASE_FIRST) {
                len = 1
            }
            while (len > 0) {
                let c0 = Int64(Int64(dst[Int64(uppercaseOffset)]) & 0xFF)
                if (c0 < 0xC0) {
                    if (c0 >= 97 && c0 <= 122) {
                        dst[Int64(uppercaseOffset)] = UInt8(Int64(dst[Int64(uppercaseOffset)]) ^ 32)
                    }
                    uppercaseOffset += 1
                    len -= 1
                } else {
                    if (c0 < 0xE0) {
                        dst[Int64(uppercaseOffset + 1)] = UInt8(Int64(dst[Int64(uppercaseOffset + 1)]) ^ 32)
                        uppercaseOffset += 2
                        len -= 2
                    } else {
                        dst[Int64(uppercaseOffset + 2)] = UInt8(Int64(dst[Int64(uppercaseOffset + 2)]) ^ 5)
                        uppercaseOffset += 3
                        len -= 3
                    }
                }
            }
        } else {
            if (transformType == SHIFT_FIRST || transformType == SHIFT_ALL) {
                var shiftOffset = offset - len
                let param = Int64(transforms.params[Int64(transformIndex)])
                var scalar = (param & 0x7FFF) + (0x1000000 - (param & 0x8000))
                while (len > 0) {
                    var step: Int32 = 1
                    let c0 = Int64(Int64(dst[Int64(shiftOffset)]) & 0xFF)
                    if (c0 < 0x80) {
                        scalar += c0
                        dst[Int64(shiftOffset)] = UInt8(scalar & 0x7F)
                    } else {
                        if (c0 < 0xC0) { } else {
                            if (c0 < 0xE0) {
                                if (len >= 2) {
                                    let c1 = Int64(dst[Int64(shiftOffset + 1)])
                                    scalar += (c1 & 0x3F) | ((c0 & 0x1F) << 6)
                                    dst[Int64(shiftOffset)] = UInt8(0xC0 | ((scalar >> 6) & 0x1F))
                                    dst[Int64(shiftOffset + 1)] = UInt8((c1 & 0xC0) | (scalar & 0x3F))
                                    step = 2
                                } else {
                                    step = len
                                }
                            } else {
                                if (c0 < 0xF0) {
                                    if (len >= 3) {
                                        let c1 = Int64(dst[Int64(shiftOffset + 1)])
                                        let c2 = Int64(dst[Int64(shiftOffset + 2)])
                                        scalar += (c2 & 0x3F) | ((c1 & 0x3F) << 6) | ((c0 & 0x0F) << 12)
                                        dst[Int64(shiftOffset)] = UInt8(0xE0 | ((scalar >> 12) & 0x0F))
                                        dst[Int64(shiftOffset + 1)] = UInt8((c1 & 0xC0) | ((scalar >> 6) & 0x3F))
                                        dst[Int64(shiftOffset + 2)] = UInt8((c2 & 0xC0) | (scalar & 0x3F))
                                        step = 3
                                    } else {
                                        step = len
                                    }
                                } else {
                                    if (c0 < 0xF8) {
                                        if (len >= 4) {
                                            let c1 = Int64(dst[Int64(shiftOffset + 1)])
                                            let c2 = Int64(dst[Int64(shiftOffset + 2)])
                                            let c3 = Int64(dst[Int64(shiftOffset + 3)])
                                            scalar += (c3 & 0x3F) | ((c2 & 0x3F) << 6) | ((c1 & 0x3F) << 12) | ((c0 & 0x07) << 18)
                                            dst[Int64(shiftOffset)] = UInt8(0xF0 | ((scalar >> 18) & 0x07))
                                            dst[Int64(shiftOffset + 1)] = UInt8((c1 & 0xC0) | ((scalar >> 12) & 0x3F))
                                            dst[Int64(shiftOffset + 2)] = UInt8((c2 & 0xC0) | ((scalar >> 6) & 0x3F))
                                            dst[Int64(shiftOffset + 3)] = UInt8((c3 & 0xC0) | (scalar & 0x3F))
                                            step = 4
                                        } else {
                                            step = len
                                        }
                                    }
                                }
                            }
                        }
                    }
                    shiftOffset += step
                    len -= step
                    if (transformType == SHIFT_FIRST) {
                        len = 0
                    }
                }
            }
        }
        while (suffix != suffixEnd) {
            dst[Int64(match (0) { case _ => offset++; offset - 1 })] = prefixSuffixStorage[Int64(match (0) { case _ => suffix++; suffix - 1 })]
        }
        return offset - dstOffset
    }
}


class Transform_Transforms {
    let numTransforms: Int32

    let triplets: Array<Int32>

    let prefixSuffixStorage: Array<UInt8>

    let prefixSuffixHeads: Array<Int32>

    let params: Array<Int16>

    init(numTransforms: Int32, prefixSuffixLen: Int32, prefixSuffixCount: Int32) {
        this.numTransforms = numTransforms
        this.triplets = Array<Int32>(Int64(numTransforms * 3)) { _ => 0 }
        this.params = Array<Int16>(Int64(numTransforms)) { _ => 0 }
        this.prefixSuffixStorage = Array<UInt8>(Int64(prefixSuffixLen)) { _ => 0 }
        this.prefixSuffixHeads = Array<Int32>(Int64(prefixSuffixCount + 1)) { _ => 0 }
    }
}