/*
 * Copyright 2016 Intel Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ----
 *
 * Part of IJG's libjpeg library (primarily jdmarker.c) was used as a reference
 * while implementing this parser.  This implementation loosely reproduces some
 * of the parsing logic found in the libjpeg jdmarker.c file.  Although, this
 * logic has been refactored using C++-style syntax and data structures and
 * adjusted appropriately to fit into the overall libyami framework.  Therefore,
 * this implementation is considered to be partially derived from IJG's libjpeg.
 *
 * The following license preamble, below, is reproduced from libjpeg's
 * jdmarker.c file.  The README.ijg is also provided with this file:
 *
 * Copyright (C) 1991-1998, Thomas G. Lane.
 * Modified 2009-2013 by Guido Vollbeding.
 * The jdmarker.c file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README.ijg file.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

// primary header
#include "jpegParser.h"

// library headers
#include "common/log.h"

// system headers
#include <algorithm>
#include <cstring>

namespace YamiParser {
namespace JPEG {

#define INPUT_BYTE(var, action) \
    do { \
        if (m_input.end()) { \
            action; \
        } \
        var = m_input.read(8); \
    } while(0)

#define INPUT_2BYTES(var, action) \
    do { \
        uint16_t b1, b2; \
        INPUT_BYTE(b1, action); \
        INPUT_BYTE(b2, action); \
        var = ((b1 << 8) | b2) & 0xffff; \
    } while(0)

const Defaults Defaults::s_instance;

Defaults::Defaults()
    : m_acHuffTables()
    , m_dcHuffTables()
    , m_quantTables()
{
    // The following tables are from
    // https://www.w3.org/Graphics/JPEG/itu-t81.pdf

    { // K.3.3.1 Luminance DC coefficients
        std::array<uint8_t, 16> codes = {
            0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        };
        std::array<uint8_t, 256> values = {
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0a, 0x0b
        };

        m_dcHuffTables[0].reset(new HuffTable());
        m_dcHuffTables[0]->codes.swap(codes);
        m_dcHuffTables[0]->values.swap(values);
    }

    { // K.3.3.1 Chrominance DC coefficients
        std::array<uint8_t, 16> codes = {
            0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
            0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
        };
        std::array<uint8_t, 256> values = {
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0a, 0x0b
        };

        m_dcHuffTables[1].reset(new HuffTable());
        m_dcHuffTables[1]->codes.swap(codes);
        m_dcHuffTables[1]->values.swap(values);
    }

    { // K.3.3.2 Luminance AC coefficients
        std::array<uint8_t, 16> codes = {
            0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
            0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d
        };
        std::array<uint8_t, 256> values = {
            0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
            0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
            0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
            0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
            0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
            0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
            0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
            0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
            0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
            0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
            0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
            0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
            0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
            0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
            0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
            0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
            0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
            0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
            0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
            0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
            0xf9, 0xfa
        };

        m_acHuffTables[0].reset(new HuffTable());
        m_acHuffTables[0]->codes.swap(codes);
        m_acHuffTables[0]->values.swap(values);
    }

    { // K.3.3.2 Chrominance AC coefficients
        std::array<uint8_t, 16> codes = {
            0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
            0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77
        };
        std::array<uint8_t, 256> values = {
            0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
            0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
            0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
            0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
            0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
            0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
            0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
            0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
            0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
            0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
            0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
            0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
            0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
            0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
            0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
            0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
            0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
            0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
            0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
            0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
            0xf9, 0xfa
        };

        m_acHuffTables[1].reset(new HuffTable());
        m_acHuffTables[1]->codes.swap(codes);
        m_acHuffTables[1]->values.swap(values);
    }

    static const std::array<uint8_t, 64> zigzag8x8 = {
        0,   1,  8, 16,  9,  2,  3, 10,
        17, 24, 32, 25, 18, 11,  4,  5,
        12, 19, 26, 33, 40, 48, 41, 34,
        27, 20, 13,  6,  7, 14, 21, 28,
        35, 42, 49, 56, 57, 50, 43, 36,
        29, 22, 15, 23, 30, 37, 44, 51,
        58, 59, 52, 45, 38, 31, 39, 46,
        53, 60, 61, 54, 47, 55, 62, 63
    };

    { // Table K.1 Luminance quantization table values
        std::array<uint16_t, DCTSIZE2> values = {
            16,  11,  10,  16,  24,  40,  51,  61,
            12,  12,  14,  19,  26,  58,  60,  55,
            14,  13,  16,  24,  40,  57,  69,  56,
            14,  17,  22,  29,  51,  87,  80,  62,
            18,  22,  37,  56,  68, 109, 103,  77,
            24,  35,  55,  64,  81, 104, 113,  92,
            49,  64,  78,  87, 103, 121, 120, 101,
            72,  92,  95,  98, 112, 100, 103,  99
        };

        m_quantTables[0].reset(new QuantTable());
        for (size_t i(0); i < DCTSIZE2; ++i)
            m_quantTables[0]->values[i] = values[zigzag8x8[i]];
        m_quantTables[0]->precision = 0; // 1-byte precision
    }

    { // Table K.2 Chrominance quantization table values
        std::array<uint16_t, DCTSIZE2> values = {
            17,  18,  24,  47,  99,  99,  99,  99,
            18,  21,  26,  66,  99,  99,  99,  99,
            24,  26,  56,  99,  99,  99,  99,  99,
            47,  66,  99,  99,  99,  99,  99,  99,
            99,  99,  99,  99,  99,  99,  99,  99,
            99,  99,  99,  99,  99,  99,  99,  99,
            99,  99,  99,  99,  99,  99,  99,  99,
            99,  99,  99,  99,  99,  99,  99,  99
        };

        m_quantTables[1].reset(new QuantTable());
        for (size_t i(0); i < DCTSIZE2; ++i)
            m_quantTables[1]->values[i] = values[zigzag8x8[i]];
        m_quantTables[1]->precision = 0; // 1-byte precision
    }
}

Parser::Parser(const uint8_t* data, const uint32_t size)
    : m_input(data, size)
    , m_data(data)
    , m_size(size)
    , m_current()
    , m_frameHeader()
    , m_scanHeader()
    , m_quantTables()
    , m_dcHuffTables()
    , m_acHuffTables()
    , m_arithDCL()
    , m_arithDCU()
    , m_arithACK()
    , m_callbacks()
    , m_sawSOI(false)
    , m_sawEOI(false)
    , m_sawSOS(false)
    , m_restartInterval(0)
{
}

bool Parser::skipBytes(const uint32_t nBytes)
{
    if ((static_cast<uint64_t>(nBytes) << 3)
            > m_input.getRemainingBitsCount()) {
        ERROR("Not enough bytes in stream");
        return false;
    }

    // BitReader only supports skipping a max of CACHEBYTES at a time
    // for each call to skip.
    const uint32_t nSkips = nBytes / BitReader::CACHEBYTES;
    const uint32_t rem = nBytes % BitReader::CACHEBYTES;
    for (uint32_t i(0); i < nSkips; ++i)
        m_input.skip(BitReader::CACHEBYTES << 3);
    m_input.skip(rem << 3);

    return true;
}

void Parser::registerCallback(const Marker& marker, const Callback& callback)
{
    m_callbacks[marker].push_back(callback);
}

void Parser::registerStartOfFrameCallback(const Callback& callback)
{
    static const std::array<const Marker, 13> sofMarkers = {
        M_SOF0, M_SOF1 , M_SOF2 , M_SOF3 , M_SOF5 , M_SOF6 , M_SOF7,
        M_SOF9, M_SOF10, M_SOF11, M_SOF13, M_SOF14, M_SOF15 };

    for (size_t i(0); i < 13; ++i)
        registerCallback(sofMarkers[i], callback);
}

Parser::CallbackResult Parser::notifyCallbacks() const
{
    const Callbacks::const_iterator match(m_callbacks.find(m_current.marker));
    if (match != m_callbacks.end()) {
        const CallbackList& callbacks = match->second;
        const size_t nCallbacks = callbacks.size();
        for (size_t i(0); i < nCallbacks; ++i)
            if (callbacks[i]() == ParseSuspend)
                return ParseSuspend;
    }
    return ParseContinue;
}

bool Parser::firstMarker()
{
    uint32_t c, c2;

    INPUT_BYTE(c, return false);
    INPUT_BYTE(c2, return false);
    if (c != 0xFF || c2 != M_SOI) {
        ERROR("No SOI found. Not a JPEG");
        return false;
    }

    m_current.marker = static_cast<Marker>(c2);
    m_current.position = currentBytePosition() - 1;
    m_current.length = 0; // set by marker parse routines when appropriate

    return true;
}

bool Parser::nextMarker()
{
    if (m_input.getRemainingBitsCount() == 0)
        return false;

    const uint8_t* const end = m_data + m_size - 1;
    const uint8_t* const start = m_data + currentBytePosition();
    const uint8_t* current = start;
    const uint8_t* match;

    do {
        match = std::find(current, end, 0xFF);
        if (match == end) {
            skipBytes(std::distance(start, match) + 1);
            return false;
        }
        if (*(match + 1) < 0xFF && *(match + 1) >= M_SOF0)
            break;
        current = match + 1;
    } while (true);

    skipBytes(std::distance(start, match) + 1);

    m_current.marker = static_cast<Marker>(m_input.read(8));
    m_current.position = currentBytePosition() - 1;
    m_current.length = 0; // set by marker parse routines when appropriate

    return true;
}

bool Parser::parse()
{
    while (true) {
        if (!m_sawSOI) {
            if (!firstMarker()) {
                return false;
            }
        } else if (!nextMarker()) {
            if (m_sawSOS && !m_sawEOI) {
                // NOTE: By standard, a missing EOI technically means the image
                // is corrupt.  However, there are many images in the wild where
                // a missing EOI is the only corruption to the image (i.e. the
                // entropy-coded data and headers are still intact).  This can
                // happen, for example, when the image was encoded by a buggy
                // application that forgets to append the EOI to the final
                // image.  If a missing EOI is the only issue, then decoders can
                // still successfully decode it.  However, a missing EOI does
                // not necessarily mean the rest of the image is intact.  That
                // is, there is no guarantee that the final decoded output will
                // produce the desired result.  Either way, we insert a fake
                // EOI marker here to give decoders a chance to decode the
                // image.
                WARNING("No EOI marker found on input... inserting one now");
                m_current.marker = M_EOI;
                m_current.position = currentBytePosition() - 1;
                m_current.length = 0;
            } else {
                return m_sawEOI;
            }
        }

        DEBUG("%s (byte:0x%02x position:%u)", __func__,
            m_current.marker, m_current.position);

        bool parseSegment = false;

        switch (m_current.marker) {
        case M_SOI:
            parseSegment = parseSOI();
            break;
        case M_SOF0: // Baseline
            parseSegment = parseSOF(true, false, false);
            break;
        case M_SOF1: // Extended sequential, Huffman
            parseSegment = parseSOF(false, false, false);
            break;
        case M_SOF2: // Progressive, Huffman
            parseSegment = parseSOF(false, true, false);
            break;
        case M_SOF9: // Extended sequential, arithmetic
            parseSegment = parseSOF(false, false, true);
            break;
        case M_SOF10: // Progressive, arithmetic
            parseSegment = parseSOF(false, true, true);
            break;
        case M_DAC:
            parseSegment = parseDAC();
            break;
        case M_DHT:
            parseSegment = parseDHT();
            break;
        case M_DQT:
            parseSegment = parseDQT();
            break;
        case M_DRI:
            parseSegment = parseDRI();
            break;
        case M_SOS:
            parseSegment = parseSOS();
            break;
        case M_EOI:
            parseSegment = parseEOI();
            break;

        case M_APP0:
        case M_APP1:
        case M_APP2:
        case M_APP3:
        case M_APP4:
        case M_APP5:
        case M_APP6:
        case M_APP7:
        case M_APP8:
        case M_APP9:
        case M_APP10:
        case M_APP11:
        case M_APP12:
        case M_APP13:
        case M_APP14:
        case M_APP15:
            parseSegment = parseAPP();
            break;

        case M_COM:
        case M_RST0:
        case M_RST1:
        case M_RST2:
        case M_RST3:
        case M_RST4:
        case M_RST5:
        case M_RST6:
        case M_RST7:
            parseSegment = true;
            break;

        /* Currently unsupported SOFn types */
        case M_SOF3:  // Lossless, Huffman
        case M_SOF5:  // Differential sequential, Huffman
        case M_SOF6:  // Differential progressive, Huffman
        case M_SOF7:  // Differential lossless, Huffman
        case M_JPG:   // Reserved for JPEG extensions
        case M_SOF11: // Lossless, arithmetic
        case M_SOF13: // Differential sequential, arithmetic
        case M_SOF14: // Differential progressive, arithmetic
        case M_SOF15: // Differential lossless, arithmetic
            //unsupported
            ERROR("Unsupported marker encountered: 0x%02x", m_current.marker);
            return false;

        default:
            // unknown or unhandled marker
            ERROR("Unknown or unhandled marker: 0x%02x", m_current.marker);
            return false;
        }

        if (!parseSegment)
            return false;

        if (notifyCallbacks() == ParseSuspend)
            return true;
    }
}

bool Parser::parseSOI()
{
    if (m_sawSOI) {
        ERROR("Duplicate SOI encountered");
        return false;
    }

    m_sawSOI = true;

    return true;
}

bool Parser::parseAPP()
{
    INPUT_2BYTES(m_current.length, return false);

    return skipBytes(m_current.length - 2);
}

bool Parser::parseSOF(bool isBaseline, bool isProgressive, bool isArithmetic)
{
    if (m_frameHeader) {
        ERROR("Duplicate SOF encountered");
        return false;
    }

    INPUT_2BYTES(m_current.length, return false);

    m_frameHeader.reset(new FrameHeader);

    m_frameHeader->isBaseline = isBaseline;
    m_frameHeader->isProgressive = isProgressive;
    m_frameHeader->isArithmetic = isArithmetic;

    INPUT_BYTE(m_frameHeader->dataPrecision, return false);
    INPUT_2BYTES(m_frameHeader->imageHeight, return false);
    INPUT_2BYTES(m_frameHeader->imageWidth, return false);

    uint32_t numComponents;

    INPUT_BYTE(numComponents, return false);

    DEBUG("baseline      : %d", m_frameHeader->isBaseline);
    DEBUG("progressive   : %d", m_frameHeader->isProgressive);
    DEBUG("arithmetic    : %d", m_frameHeader->isArithmetic);
    DEBUG("precision     : %d", m_frameHeader->dataPrecision);
    DEBUG("image width   : %u", m_frameHeader->imageWidth);
    DEBUG("image height  : %u", m_frameHeader->imageHeight);
    DEBUG("num components: %d", numComponents);

    if (m_frameHeader->imageWidth <= 0 || m_frameHeader->imageHeight <= 0
            || numComponents <= 0) {
        ERROR("Empty image");
        return false;
    }

    if ((m_current.length - 8) != (numComponents * 3)
            || numComponents > MAX_COMPS_IN_SCAN) {
        ERROR("Bad length");
        return false;
    }

    m_frameHeader->components.resize(numComponents);
    m_frameHeader->maxHSampleFactor = 0;
    m_frameHeader->maxVSampleFactor = 0;

    for (size_t index(0); index < numComponents; ++index) {
        int factor;
        Component::Shared& component = m_frameHeader->components[index];
        component.reset(new Component);
        component->index = index;

        INPUT_BYTE(component->id, return false);
        INPUT_BYTE(factor, return false);

        component->hSampleFactor = (factor >> 4) & 15;
        component->vSampleFactor = factor & 15;

        if (component->hSampleFactor > m_frameHeader->maxHSampleFactor)
            m_frameHeader->maxHSampleFactor = component->hSampleFactor;

        if (component->vSampleFactor > m_frameHeader->maxVSampleFactor)
            m_frameHeader->maxVSampleFactor = component->vSampleFactor;

        INPUT_BYTE(component->quantTableNumber, return false);
    }

    return true;
}

static bool componentIdMatches(const int id, const Component::Shared& component)
{
    return id == component->id;
}

bool Parser::parseSOS()
{
    using std::placeholders::_1;

    if (!m_frameHeader) {
        ERROR("SOS Encountered before SOF");
        return false;
    }

    INPUT_2BYTES(m_current.length, return false);

    size_t numComponents;

    INPUT_BYTE(numComponents, return false);

    if (m_current.length != (numComponents * 2 + 6)
            || numComponents < 1
            || numComponents > MAX_COMPS_IN_SCAN) {
        ERROR("Invalid SOS Length");
        return false;
    }

    m_scanHeader.reset(new ScanHeader);

    m_scanHeader->numComponents = numComponents;

    for (size_t i(0); i < numComponents; ++i)
        m_scanHeader->components[i].reset();

    for (size_t i(0); i < numComponents; ++i) {
        int id;
        int c;

        INPUT_BYTE(id, return false);

        Components::iterator component = std::find_if(
            m_frameHeader->components.begin(),
            m_frameHeader->components.end(),
            std::bind(&componentIdMatches, id, _1));

        if (component == m_frameHeader->components.end()
                || m_scanHeader->components[(*component)->index]) {
            ERROR("Bad Component Id (%d)", id);
            return false;
        }

        INPUT_BYTE(c, return false);

        m_scanHeader->components[i] = *component;
        (*component)->dcTableNumber = (c >> 4) & 15;
        (*component)->acTableNumber = c & 15;

        /* This CSi (cc) should differ from the previous CSi */
        for (size_t pi(0); pi < i; ++pi) {
            if (m_scanHeader->components[pi] == (*component)) {
                ERROR("Bad Component Id (%d)", id);
                return false;
            }
        }
    }

    // The following are used only in progressive mode
    // FIXME: validate these values according to JPEG specification
    INPUT_BYTE(m_scanHeader->ss, return false);
    INPUT_BYTE(m_scanHeader->se, return false);
    INPUT_BYTE(m_scanHeader->ah, return false);

    m_scanHeader->al = m_scanHeader->ah & 15;
    m_scanHeader->ah = (m_scanHeader->ah >> 4) & 15;

    m_sawSOS = true;

    return true;
}

bool Parser::parseEOI()
{
    if (m_sawEOI) {
        ERROR("Duplicate EOI encountered");
        return false;
    }

    m_sawEOI = true;

    return true;
}

bool Parser::parseDAC()
{
    INPUT_2BYTES(m_current.length, return false);

    long length = m_current.length - 2;

    while (length > 0) {
        size_t index;
        int value;

        INPUT_BYTE(index, return false);
        INPUT_BYTE(value, return false);

        length -= 2;

        if (index < 0 || index >= (2 * NUM_ARITH_TBLS)) {
            ERROR("Invalid DAC Index");
            return false;
        }

        if (index >= NUM_ARITH_TBLS) {
            m_arithACK[index - NUM_ARITH_TBLS] = value;
        } else {
            m_arithDCL[index] = value & 15;
            m_arithDCU[index] = (value >> 4) & 15;
            if (m_arithDCL[index] > m_arithDCU[index]) {
                ERROR("Invalid DAC Value");
                return false;
            }
        }
    }

    if (length != 0) {
        ERROR("Invalid DAC Length");
        return false;
    }

    return true;
}

bool Parser::parseDQT()
{
    INPUT_2BYTES(m_current.length, return false);

    long length = m_current.length - 2;

    while (length > 0) {
        int c;

        INPUT_BYTE(c, return false);

        const size_t index = c & 15;
        const int precision = (c >> 4) & 15;

        if (index >= NUM_QUANT_TBLS) {
            ERROR("Invalid quant table index encountered");
            return false;
        }

        QuantTable::Shared& quantTable = m_quantTables[index];
        if (!quantTable)
            quantTable.reset(new QuantTable);

        quantTable->precision = precision;

        for (size_t i(0); i < DCTSIZE2; ++i) {
            if (precision)
                INPUT_2BYTES(quantTable->values[i], return false);
            else
                INPUT_BYTE(quantTable->values[i], return false);
        }

        const uint32_t nbytes = precision ? 2 : 1;
        length -= (DCTSIZE2 * nbytes) + 1;
    }

    if (length != 0) {
        ERROR("Bad DQT length");
        return false;
    }

    return true;
}

bool Parser::parseDHT()
{
    INPUT_2BYTES(m_current.length, return false);

    long length = m_current.length - 2;

    while (length > 16) {
        size_t index;

        INPUT_BYTE(index, return false);

        long count = 0;
        std::vector<uint8_t> codes(16, 0);
        for (size_t i(0); i < 16; ++i) {
            INPUT_BYTE(codes[i], return false);
            count += codes[i];
        }

        length -= 1 + 16;

        if (count > 256 || count > length) {
            ERROR("Bad Huff Table");
            return false;
        }

        std::vector<uint8_t> huffval(256, 0);
        for (long i(0); i < count; ++i)
            INPUT_BYTE(huffval[i], return false);

        length -= count;

        HuffTables* huffTables;
        if (index & 0x10) { /* AC table definition */
            index -= 0x10;
            huffTables = &m_acHuffTables;
        } else { /* DC table definition */
            huffTables = &m_dcHuffTables;
        }

        if (index < 0 || index >= NUM_HUFF_TBLS) {
            ERROR("Bad Huff Table Index");
            return false;
        }

        HuffTable::Shared& huffTable = (*huffTables)[index];
        if (!huffTable)
            huffTable.reset(new HuffTable);

        std::memcpy(&huffTable->codes[0], &codes[0],
                    sizeof(huffTable->codes));
        std::memcpy(&huffTable->values[0], &huffval[0],
                    sizeof(huffTable->values));
    }

    if (length != 0) {
        ERROR("Bad DHT Length");
        return false;
    }

    return true;
}

bool Parser::parseDRI()
{
    INPUT_2BYTES(m_current.length, return false);

    if (m_current.length != 4) {
        ERROR("Bad DRI Length");
        return false;
    }

    INPUT_2BYTES(m_restartInterval, return false);

    return true;
}

} // namespace JPEG
} // namespace YamiParser
