/* This file is part of the KDE project
   SPDX-FileCopyrightText: 2012 Mojtaba Shahi Senobari <mojtaba.shahi3000@gmail.com>

   SPDX-License-Identifier: LGPL-2.0-or-later
*/

#ifndef MOBIHEADERGENERATOR_H
#define MOBIHEADERGENERATOR_H

#include <QChar>
#include <QHash>
#include <QObject>

// I follow the file structure that mobi packet creator  create.
struct palmDBHeader {
    palmDBHeader();

    /// database name. This name is 0 terminated in the field and will be used as the file name on a computer.
    /// For eBooks this usually contains the title and may have the author depending on the length available.
    QByteArray title;

    /** bit field.
        0x0002 Read-Only
        0x0004 Dirty AppInfoArea
        0x0008 Backup this database (i.e. no conduit exists)
        0x0010 (16 decimal) Okay to install newer over existing copy, if present on PalmPilot
        0x0020 (32 decimal) Force the PalmPilot to reset after this database is installed
        0x0040 (64 decimal) Don't allow copy of file to be beamed to other Pilot.
        */
    qint16 attributes;

    qint16 version; /// file version
    qint32 creationDate; /// No. of seconds since start of January 1, 1904.
    qint32 modificationDate; /// No. of seconds since start of January 1, 1904.
    qint32 lastBackupDate; /// 	No. of seconds since start of January 1, 1904.
    qint32 modificationNumber;
    qint32 appInfoId; /// offset to start of Application Info (if present) or null
    qint32 sortInfoId; /// offset to start of Sort Info (if present) or null
    QByteArray type;
    QByteArray creator;
    qint32 uniqueIdSeed; /// used internally to identify record
    qint32 nextRecordIdList; /// Only used when in-memory on Palm OS. Always set to zero in stored files.
    qint16 numberOfRecords; /// number of records in the file - N

    // This will repeate of record numbers.
    qint32 recordOffset; /// the offset of record n from the start of the PDB of this record
    int recordUniqueId; /// The unique ID for this record. Often just a sequential count from 0
    // Format: QHash<recordOffset, recorUniqueId>
    QHash<int, int> recordsInfo;
    //

    // There should two bytes zero

    int headerLength; // This is not in header just to have header length for myself
    // qint32 grapToData; /// traditionally 2 zero bytes to Info or raw data
};

/// PalmDocHeadr length = 16
struct palmDocHeader {
    palmDocHeader();

    qint16 compression; /// 1 == no compression, 2 = PalmDOC compression, 17480 = HUFF/CDIC compression
    qint16 unused; /// Always zero
    qint32 textLength;
    qint16 pdbrecordCount;
    qint16 maxRecordSize; /// Maximum size of each record containing text, always 4096

    /// 0 == no encryption, 1 = Old Mobipocket
    /// Encryption, 2 = Mobipocket Encryption
    qint16 encryptionType;

    qint16 unknown; /// Usually zero
    qint32 headerLength; // This is not in header just to have header length for myself
};

/// mobi header length 232
struct mobiHeader {
    mobiHeader();

    QByteArray identifier; /// 	the characters M O B I
    int mobiHeaderLength; // = 232;
    qint32 mobiType; // = 2;
    /**  The kind of Mobipocket file this is
        2 Mobipocket Book
        3 PalmDoc Book
        4 Audio
        232 mobipocket? generated by kindlegen1.2
        248 KF8: generated by kindlegen2
        257 News
        258 News_Feed
        259 News_Magazine
        513 PICS
        514 WORD
        515 XLS
        516 PPT
        517 TEXT
        518 HTML
    */
    qint32 textEncoding; /// 1252 = CP1252 (WinLatin1); 65001 = UTF-8
    qint32 uniqueId; /// Some kind of unique ID number

    // FIXME: Really i do not know what should i set for this parameter.
    qint32 fileVersion; /// Version of the Mobipocket format used in this file.

    qint32 ortographicIndex; /// Section number of orthographic meta index. 0xFFFFFFFF if index is not available.
    qint32 inflectionIndex; /// Section number of inflection meta index. 0xFFFFFFFF if index is not available.
    qint32 indexNames; /// 0xFFFFFFFF if index is not available.
    qint32 indexkeys; /// 0xFFFFFFFF if index is not available.
    qint32 extraIndex0; /// Section number of extra 0 meta index. 0xFFFFFFFF if index is not available.
    qint32 extraIndex1; /// Section number of extra 0 meta index. 0xFFFFFFFF if index is not available.
    qint32 extraIndex2; /// Section number of extra 0 meta index. 0xFFFFFFFF if index is not available.
    qint32 extraIndex3; /// Section number of extra 0 meta index. 0xFFFFFFFF if index is not available.
    qint32 extraIndex4; /// Section number of extra 0 meta index. 0xFFFFFFFF if index is not available.
    qint32 extraIndex5; /// Section number of extra 0 meta index. 0xFFFFFFFF if index is not available.

    qint32 firstNonBookIndex; /// First record number (starting with 0) that's not the book's text

    qint32 fullNameOffset; /// Offset in record 0 (not from start of file) of the full name of the book
    qint32 fullNameLength; /// Length in bytes of the full name of the book

    /// Book locale code. Low byte is main language 09= English,
    /// next byte is dialect, 08 = British, 04 = US.
    /// Thus US English is 1033, UK English is 2057.
    qint32 local;
    qint32 inputLanguage; /// Input language for a dictionary
    qint32 outputLanguage; /// Output language for a dictionary
    qint32 minversion; /// Minimum mobipocket version support needed to read this file.

    /// First record number (starting with 0) that contains an image.
    /// Image records should be sequential.
    qint32 firstImageIndex;
    qint32 huffmanRecordOffset; /// The record number of the first huffman compression record.
    qint32 huffmanRecordCount; /// The number of huffman compression records.
    qint32 huffmanTableOffset; // = 0;
    qint32 huffmanTableLength; // = 0;

    qint32 EXTH_Flags; /// bitfield. if bit 6 (0x40) is set, then there's an EXTH record

    // Here there is 32 byte
    qint64 unknown1; // = 0;
    qint64 unknown1_1; // = 0;
    qint64 unknown1_2; // = 0;
    qint64 unknown1_3; // = 0;

    qint32 drmOffset; /// Offset to DRM key info in DRMed files. 0xFFFFFFFF if no DRM
    qint32 drmCount; /// Number of entries in DRM info. 0xFFFFFFFF if no DRM
    qint32 drmSize; /// Number of bytes in DRM info.
    qint32 drmFlags; /// Some flags concerning the DRM info.

    // Here there is 12 byte unknown
    qint64 unknown2; // = 0;
    qint32 unknown2_1; // = 0;

    qint16 firstContentRecordNumber; /// Number of first text record. Normally 1.
    qint16 lastContentRecordNumber; /// Last content record number

    qint32 unknown3; /// Use 0x00000001.

    qint32 FCIS_recordNumber;
    qint32 unknown4; /// Use 0x00000001.

    qint32 FLIS_recordNumber;
    qint32 unknown5; /// Use 0x00000001.

    qint64 unknown6; /// Use 0x0000000000000000.
    qint32 unknown7; /// Use 0xFFFFFFFF.
    qint32 unknown8; /// Use 0x00000000.
    qint32 unknown9; /// Use 0xFFFFFFFF.
    qint32 unknown10; /// Use 0xFFFFFFFF.
    qint32 extraRecordDataFlags; // = 0;

    /// (If not 0xFFFFFFFF)The record number of the
    /// first INDX record created from an ncx file.
    qint32 INDX_recordOffset;
};
struct exthHeader {
    exthHeader();

    QByteArray identifier; /// the characters E X T H.

    /// the length of the EXTH header, including
    /// the previous 4 bytes - but not including the final padding.
    int headerLength;

    /// The number of records in the EXTH header. the rest of the EXTH header
    /// consists of repeated EXTH records to the end of the EXTH length.
    qint32 exthRecordCount;

    // EXTH record start
    qint32 recordType; /// 	Exth Record type. Just a number identifying what's stored in the record

    /// length of EXTH record = L , including the 8 bytes in
    /// the type and length fields

    int pad;
    QHash<qint32, QByteArray> exthRecord;
};

class MobiHeaderGenerator
{
public:
    MobiHeaderGenerator();
    ~MobiHeaderGenerator();

    void generateMobiHeaders(QHash<QString, QString> metaData,
                             int compressedTextSize,
                             int uncompressedTextSize,
                             QList<int> imagesSize,
                             QList<qint32> textRecordsOffset);

public:
    palmDBHeader *m_dbHeader;
    palmDocHeader *m_docHeader;
    mobiHeader *m_mobiHeader;
    exthHeader *m_exthHeader;

    QByteArray m_title;

private:
    void generatePalmDataBase();
    void generatePalmDocHeader();
    void generateMobiHeader();
    void generateEXTH();
    int calculateRecordsCount();

private:
    QByteArray m_author;
    int m_rawTextSize;
    int m_uncompressedTextSize;
    QList<int> m_imgListSize;
    QList<qint32> m_textRecordsOffset;
};

#endif // MOBIHEADERGENERATOR_H
