//========================================================================
//
// Linearization.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2010, 2012 Hib Eris <hib@hiberis.nl>
// Copyright 2015 Jason Crain <jason@aquaticape.us>
// Copyright 2017, 2019, 2025 Albert Astals Cid <aacid@kde.org>
// Copyright 2019 Adam Reichold <adam.reichold@t-online.de>
// Copyright 2019 Even Rouault <even.rouault@spatialys.com>
// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
// Copyright (C) 2025 Jonathan Hähne <jonathan.haehne@hotmail.com>
// Copyright (C) 2025 Arnav V <arnav0872@gmail.com>
// Copyright (C) 2026 Adam Sampson <ats@offog.org>
//
//========================================================================

#include "Linearization.h"
#include "Parser.h"

//------------------------------------------------------------------------
// Linearization
//------------------------------------------------------------------------

Linearization::Linearization(BaseStream *str)
{
    Parser *parser;

    (void)str->rewind();
    parser = new Parser(nullptr, str->makeSubStream(str->getStart(), false, 0, Object::null()), false);
    Object obj1 = parser->getObj();
    Object obj2 = parser->getObj();
    Object obj3 = parser->getObj();
    linDict = parser->getObj();
    if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") && linDict.isDict()) {
        Object obj5 = linDict.dictLookup("Linearized");
        if (!obj5.isNum() || obj5.getNum() <= 0) {
            linDict.setToNull();
        }
    } else {
        linDict.setToNull();
    }
    delete parser;
}

Linearization::~Linearization() = default;

unsigned int Linearization::getLength() const
{
    if (!linDict.isDict()) {
        return 0;
    }

    int length;
    if (linDict.getDict()->lookupInt("L", {}, &length) && length > 0) {
        return length;
    }
    error(errSyntaxWarning, -1, "Length in linearization table is invalid");
    return 0;
}

unsigned int Linearization::getHintsOffset() const
{
    int hintsOffset;

    Object obj1, obj2;
    if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArrayOfLengthAtLeast(2)) && (obj2 = obj1.arrayGet(0), obj2.isInt()) && obj2.getInt() > 0) {
        hintsOffset = obj2.getInt();
    } else {
        error(errSyntaxWarning, -1, "Hints table offset in linearization table is invalid");
        hintsOffset = 0;
    }

    return hintsOffset;
}

unsigned int Linearization::getHintsLength() const
{
    int hintsLength;

    Object obj1, obj2;
    if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArrayOfLengthAtLeast(2)) && (obj2 = obj1.arrayGet(1), obj2.isInt()) && obj2.getInt() > 0) {
        hintsLength = obj2.getInt();
    } else {
        error(errSyntaxWarning, -1, "Hints table length in linearization table is invalid");
        hintsLength = 0;
    }

    return hintsLength;
}

unsigned int Linearization::getHintsOffset2() const
{
    int hintsOffset2 = 0; // default to 0

    Object obj1;
    if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArrayOfLengthAtLeast(4))) {
        Object obj2 = obj1.arrayGet(2);
        if (obj2.isInt() && obj2.getInt() > 0) {
            hintsOffset2 = obj2.getInt();
        } else {
            error(errSyntaxWarning, -1, "Second hints table offset in linearization table is invalid");
            hintsOffset2 = 0;
        }
    }

    return hintsOffset2;
}

unsigned int Linearization::getHintsLength2() const
{
    int hintsLength2 = 0; // default to 0

    Object obj1;
    if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArrayOfLengthAtLeast(4))) {
        Object obj2 = obj1.arrayGet(3);
        if (obj2.isInt() && obj2.getInt() > 0) {
            hintsLength2 = obj2.getInt();
        } else {
            error(errSyntaxWarning, -1, "Second hints table length in linearization table is invalid");
            hintsLength2 = 0;
        }
    }

    return hintsLength2;
}

int Linearization::getObjectNumberFirst() const
{
    int objectNumberFirst = 0;
    if (linDict.isDict() && linDict.getDict()->lookupInt("O", {}, &objectNumberFirst) && objectNumberFirst > 0) {
        return objectNumberFirst;
    }
    error(errSyntaxWarning, -1, "Object number of first page in linearization table is invalid");
    return 0;
}

unsigned int Linearization::getEndFirst() const
{
    int pageEndFirst = 0;
    if (linDict.isDict() && linDict.getDict()->lookupInt("E", {}, &pageEndFirst) && pageEndFirst > 0) {
        return pageEndFirst;
    }
    error(errSyntaxWarning, -1, "First page end offset in linearization table is invalid");
    return 0;
}

int Linearization::getNumPages() const
{
    int numPages = 0;
    if (linDict.isDict() && linDict.getDict()->lookupInt("N", {}, &numPages) && numPages > 0) {
        return numPages;
    }
    error(errSyntaxWarning, -1, "Page count in linearization table is invalid");
    return 0;
}

unsigned int Linearization::getMainXRefEntriesOffset() const
{
    int mainXRefEntriesOffset = 0;
    if (linDict.isDict() && linDict.getDict()->lookupInt("T", {}, &mainXRefEntriesOffset) && mainXRefEntriesOffset > 0) {
        return mainXRefEntriesOffset;
    }
    error(errSyntaxWarning, -1, "Main Xref offset in linearization table is invalid");
    return 0;
}

int Linearization::getPageFirst() const
{
    int pageFirst = 0; // Optional, defaults to 0.

    if (linDict.isDict()) {
        linDict.getDict()->lookupInt("P", {}, &pageFirst);
    }

    if ((pageFirst < 0) || (pageFirst >= getNumPages())) {
        error(errSyntaxWarning, -1, "First page in linearization table is invalid");
        return 0;
    }

    return pageFirst;
}
