package org.j7zip.SevenZip.Compression.Branch;

import org.j7zip.SevenZip.HRESULT;
import org.j7zip.SevenZip.ICompressFilter;

public class BCJ_x86_Decoder implements ICompressFilter {
    
    // struct CBranch86 - begin
    int  [] _prevMask = new int[1];  // UInt32
    int  [] _prevPos = new int[1]; // UInt32
    void x86Init() {
        _prevMask[0] = 0;
        _prevPos[0] = -5;
    }
    // struct CBranch86 - end
        
    static final boolean [] kMaskToAllowedStatus = {true, true, true, false, true, false, false, false };
    
    static final int [] kMaskToBitNumber = {0, 1, 2, 2, 3, 3, 3, 3};
    
    static final boolean Test86MSByte(int b) { return ((b) == 0 || (b) == 0xFF); }
    
    static final int x86_Convert(byte [] buffer, int endPos, int nowPos,
            int [] prevMask, int [] prevPos, boolean encoding) {
        int bufferPos = 0;
        int limit;
        
        if (endPos < 5)
            return 0;
        
        if (nowPos - prevPos[0] > 5)
            prevPos[0] = nowPos - 5;
        
        limit = endPos - 5;
        while(bufferPos <= limit) {
            int b = (buffer[bufferPos] & 0xFF);
            int offset;
            if (b != 0xE8 && b != 0xE9) {
                bufferPos++;
                continue;
            }
            offset = (nowPos + bufferPos - prevPos[0]);
            prevPos[0] = (nowPos + bufferPos);
            if (offset > 5)
                prevMask[0] = 0;
            else {
                for (int i = 0; i < offset; i++) {
                    prevMask[0] &= 0x77;
                    prevMask[0] <<= 1;
                }
            }
            b = (buffer[bufferPos + 4] & 0xFF);
            if (Test86MSByte(b) && kMaskToAllowedStatus[(prevMask[0] >> 1) & 0x7] &&
                    (prevMask[0] >>> 1) < 0x10) {
                int src =
                        ((b) << 24) |
                        ((buffer[bufferPos + 3] & 0xFF) << 16) |
                        ((buffer[bufferPos + 2] & 0xFF) << 8) |
                        (buffer[bufferPos + 1] & 0xFF);
                
                int dest;
                for (;;) {
                    int index;
                    if (encoding)
                        dest = (nowPos + bufferPos + 5) + src;
                    else
                        dest = src - (nowPos + bufferPos + 5);
                    if (prevMask[0] == 0)
                        break;
                    index = kMaskToBitNumber[prevMask[0] >>> 1];
                    b = (dest >> (24 - index * 8)) & 0xFF;
                    if (!Test86MSByte(b))
                        break;
                    src = dest ^ ((1 << (32 - index * 8)) - 1);
                }
                buffer[bufferPos + 4] = (byte)(~(((dest >> 24) & 1) - 1));
                buffer[bufferPos + 3] = (byte)(dest >> 16);
                buffer[bufferPos + 2] = (byte)(dest >> 8);
                buffer[bufferPos + 1] = (byte)dest;
                bufferPos += 5;
                prevMask[0] = 0;
            } else {
                bufferPos++;
                prevMask[0] |= 1;
                if (Test86MSByte(b))
                    prevMask[0] |= 0x10;
            }
        }
        return bufferPos;
    }
    
    public int SubFilter(byte [] data, int size) {
        return x86_Convert(data, size, _bufferPos, _prevMask, _prevPos, false);
    }
    
    public void SubInit() {
        x86Init();
    }
  
    int   _bufferPos; // UInt32
    
    // ICompressFilter interface
    public int Init() {
        _bufferPos = 0;
        SubInit();
        return HRESULT.S_OK;
    }
    
    public int Filter(byte [] data, int size) {
        int processedSize = SubFilter(data, size);
        _bufferPos += processedSize;
        return processedSize;
    }
}
