// File: dannye[P]jed/src/encoder.cpp
// Function: encodeBlockComponent(BitWriter&, int*, int&, HuffmanTable const&, HuffmanTable const&)
bool encodeBlockComponent(
    BitWriter& bitWriter,
    int* const component,
    int& previousDC,
    const HuffmanTable& dcTable,
    const HuffmanTable& acTable
) {
    // encode DC value
    int coeff = component[0] - previousDC;
    previousDC = component[0];

    uint coeffLength = bitLength(std::abs(coeff));
    if (coeffLength > 11) {
        std::cout << "Error - DC coefficient length greater than 11\n";
        return false;
    }
    if (coeff < 0) {
        coeff += (1 << coeffLength) - 1;
    }

    uint code = 0;
    uint codeLength = 0;
    if (!getCode(dcTable, coeffLength, code, codeLength)) {
        std::cout << "Error - Invalid DC value\n";
        return false;
    }
    bitWriter.writeBits(code, codeLength);
    bitWriter.writeBits(coeff, coeffLength);

    // encode AC values
    for (uint i = 1; i < 64; ++i) {
        // find zero run length
        byte numZeroes = 0;
        while (i < 64 && component[zigZagMap[i]] == 0) {
            numZeroes += 1;
            i += 1;
        }

        if (i == 64) {
            if (!getCode(acTable, 0x00, code, codeLength)) {
                std::cout << "Error - Invalid AC value\n";
                return false;
            }
            bitWriter.writeBits(code, codeLength);
            return true;
        }

        while (numZeroes >= 16) {
            if (!getCode(acTable, 0xF0, code, codeLength)) {
                std::cout << "Error - Invalid AC value\n";
                return false;
            }
            bitWriter.writeBits(code, codeLength);
            numZeroes -= 16;
        }

        // find coeff length
        coeff = component[zigZagMap[i]];
        coeffLength = bitLength(std::abs(coeff));
        if (coeffLength > 10) {
            std::cout << "Error - AC coefficient length greater than 10\n";
            return false;
        }
        if (coeff < 0) {
            coeff += (1 << coeffLength) - 1;
        }

        // find symbol in table
        byte symbol = numZeroes << 4 | coeffLength;
        if (!getCode(acTable, symbol, code, codeLength)) {
            std::cout << "Error - Invalid AC value\n";
            return false;
        }
        bitWriter.writeBits(code, codeLength);
        bitWriter.writeBits(coeff, coeffLength);
    }

    return true;
}

// Assembly:
subq $0x48, %rsp
movq %rdi, 0x38(%rsp)
movq %rsi, 0x30(%rsp)
movq %rdx, 0x28(%rsp)
movq %rcx, 0x20(%rsp)
movq %r8, 0x18(%rsp)
movq 0x30(%rsp), %rax
movl (%rax), %eax
movq 0x28(%rsp), %rcx
subl (%rcx), %eax
movl %eax, 0x14(%rsp)
movq 0x30(%rsp), %rax
movl (%rax), %ecx
movq 0x28(%rsp), %rax
movl %ecx, (%rax)
movl 0x14(%rsp), %edi
movl %edi, %eax
negl %eax
cmovnsl %eax, %edi
callq 0x4090
movl %eax, 0x10(%rsp)
cmpl $0xb, 0x10(%rsp)
jbe 0x4205
movq 0x5de1(%rip), %rdi      # 0x9fd0
leaq 0x2f85(%rip), %rsi      # 0x717b
callq 0x2130
movb $0x0, 0x47(%rsp)
jmp 0x44ad
cmpl $0x0, 0x14(%rsp)
jge 0x4222
movl 0x10(%rsp), %ecx
movl $0x1, %eax
shll %cl, %eax
subl $0x1, %eax
addl 0x14(%rsp), %eax
movl %eax, 0x14(%rsp)
movl $0x0, 0xc(%rsp)
movl $0x0, 0x8(%rsp)
movq 0x20(%rsp), %rdi
movl 0x10(%rsp), %eax
leaq 0xc(%rsp), %rdx
leaq 0x8(%rsp), %rcx
movzbl %al, %esi
callq 0x40c0
testb $0x1, %al
jne 0x426e
movq 0x5d78(%rip), %rdi      # 0x9fd0
leaq 0x2f4b(%rip), %rsi      # 0x71aa
callq 0x2130
movb $0x0, 0x47(%rsp)
jmp 0x44ad
movq 0x38(%rsp), %rdi
movl 0xc(%rsp), %esi
movl 0x8(%rsp), %edx
callq 0x5370
movq 0x38(%rsp), %rdi
movl 0x14(%rsp), %esi
movl 0x10(%rsp), %edx
callq 0x5370
movl $0x1, 0x4(%rsp)
cmpl $0x40, 0x4(%rsp)
jae 0x44a8
movb $0x0, 0x3(%rsp)
xorl %eax, %eax
cmpl $0x40, 0x4(%rsp)
movb %al, 0x1(%rsp)
jae 0x42d8
movq 0x30(%rsp), %rax
movl 0x4(%rsp), %ecx
movl %ecx, %edx
leaq 0x2fb7(%rip), %rcx      # 0x7280
movzbl (%rcx,%rdx), %ecx
cmpl $0x0, (%rax,%rcx,4)
sete %al
movb %al, 0x1(%rsp)
movb 0x1(%rsp), %al
testb $0x1, %al
jne 0x42e2
jmp 0x42fb
movzbl 0x3(%rsp), %eax
addl $0x1, %eax
movb %al, 0x3(%rsp)
movl 0x4(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x4(%rsp)
jmp 0x42aa
cmpl $0x40, 0x4(%rsp)
jne 0x4355
movq 0x18(%rsp), %rdi
xorl %esi, %esi
leaq 0xc(%rsp), %rdx
leaq 0x8(%rsp), %rcx
callq 0x40c0
testb $0x1, %al
jne 0x4339
movq 0x5cad(%rip), %rdi      # 0x9fd0
leaq 0x2e9a(%rip), %rsi      # 0x71c4
callq 0x2130
movb $0x0, 0x47(%rsp)
jmp 0x44ad
movq 0x38(%rsp), %rdi
movl 0xc(%rsp), %esi
movl 0x8(%rsp), %edx
callq 0x5370
movb $0x1, 0x47(%rsp)
jmp 0x44ad
jmp 0x4357
movzbl 0x3(%rsp), %eax
cmpl $0x10, %eax
jl 0x43bb
movq 0x18(%rsp), %rdi
movl $0xf0, %esi
leaq 0xc(%rsp), %rdx
leaq 0x8(%rsp), %rcx
callq 0x40c0
testb $0x1, %al
jne 0x439b
movq 0x5c4b(%rip), %rdi      # 0x9fd0
leaq 0x2e38(%rip), %rsi      # 0x71c4
callq 0x2130
movb $0x0, 0x47(%rsp)
jmp 0x44ad
movq 0x38(%rsp), %rdi
movl 0xc(%rsp), %esi
movl 0x8(%rsp), %edx
callq 0x5370
movzbl 0x3(%rsp), %eax
subl $0x10, %eax
movb %al, 0x3(%rsp)
jmp 0x4357
movq 0x30(%rsp), %rax
movl 0x4(%rsp), %ecx
movl %ecx, %edx
leaq 0x2eb3(%rip), %rcx      # 0x7280
movzbl (%rcx,%rdx), %ecx
movl (%rax,%rcx,4), %eax
movl %eax, 0x14(%rsp)
movl 0x14(%rsp), %edi
movl %edi, %eax
negl %eax
cmovnsl %eax, %edi
callq 0x4090
movl %eax, 0x10(%rsp)
cmpl $0xa, 0x10(%rsp)
jbe 0x4410
movq 0x5bd6(%rip), %rdi      # 0x9fd0
leaq 0x2ddd(%rip), %rsi      # 0x71de
callq 0x2130
movb $0x0, 0x47(%rsp)
jmp 0x44ad
cmpl $0x0, 0x14(%rsp)
jge 0x442d
movl 0x10(%rsp), %ecx
movl $0x1, %eax
shll %cl, %eax
subl $0x1, %eax
addl 0x14(%rsp), %eax
movl %eax, 0x14(%rsp)
movzbl 0x3(%rsp), %eax
shll $0x4, %eax
orl 0x10(%rsp), %eax
movb %al, 0x2(%rsp)
movq 0x18(%rsp), %rdi
leaq 0xc(%rsp), %rdx
leaq 0x8(%rsp), %rcx
movzbl 0x2(%rsp), %esi
callq 0x40c0
testb $0x1, %al
jne 0x4474
movq 0x5b6f(%rip), %rdi      # 0x9fd0
leaq 0x2d5c(%rip), %rsi      # 0x71c4
callq 0x2130
movb $0x0, 0x47(%rsp)
jmp 0x44ad
movq 0x38(%rsp), %rdi
movl 0xc(%rsp), %esi
movl 0x8(%rsp), %edx
callq 0x5370
movq 0x38(%rsp), %rdi
movl 0x14(%rsp), %esi
movl 0x10(%rsp), %edx
callq 0x5370
movl 0x4(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x4(%rsp)
jmp 0x429a
movb $0x1, 0x47(%rsp)
movb 0x47(%rsp), %al
andb $0x1, %al
addq $0x48, %rsp
retq
nopl (%rax,%rax)