#include "fastfile_cod11_ps3.h"
#include "zonefile_cod11_ps3.h"
#include "encryption.h"

#include <QFile>
#include <QDebug>

FastFile_COD11_PS3::FastFile_COD11_PS3()
    : FastFile() {
    SetCompany(COMPANY_INFINITY_WARD);
    SetType(FILETYPE_FAST_FILE);
    SetSignage(SIGNAGE_UNSIGNED);
    SetMagic(0);
    SetVersion(0);
    SetGame("COD11");
    SetPlatform("PS3");
}

FastFile_COD11_PS3::FastFile_COD11_PS3(const QByteArray& aData)
    : FastFile_COD11_PS3() {

    if (!aData.isEmpty()) {
        Load(aData);
    }
}

FastFile_COD11_PS3::FastFile_COD11_PS3(const QString aFilePath)
    : FastFile_COD11_PS3() {

    if (!aFilePath.isEmpty()) {
        Load(aFilePath);
    }
}

FastFile_COD11_PS3::~FastFile_COD11_PS3() {

}

QByteArray FastFile_COD11_PS3::GetBinaryData() const {
    return QByteArray();
}

bool FastFile_COD11_PS3::Load(const QString aFilePath) {
    if (aFilePath.isEmpty()) {
        return false;
    }

    // Check fastfile can be read
    QFile *file = new QFile(aFilePath);
    if (!file->open(QIODevice::ReadOnly)) {
        qDebug() << QString("Error: Failed to open FastFile: %1!").arg(aFilePath);
        return false;
    }

    // Decompress fastfile and close
    const QString fastFileStem = aFilePath.section("/", -1, -1).section(".", 0, 0);
    SetStem(fastFileStem);
    if (!Load(file->readAll())) {
        qDebug() << "Error: Failed to load fastfile: " << fastFileStem + ".ff";
        return false;
    }

    file->close();

    // Open zone file after decompressing ff and writing
    return true;
}

bool FastFile_COD11_PS3::Load(const QByteArray aData) {
    QByteArray decompressedData;

    // Create a XDataStream on the input data.
    XDataStream fastFileStream(aData);
    fastFileStream.setByteOrder(XDataStream::LittleEndian);

    // Parse header values.
    SetCompany(pParseFFCompany(&fastFileStream));
    SetType(pParseFFFileType(&fastFileStream));
    SetSignage(pParseFFSignage(&fastFileStream));
    SetMagic(pParseFFMagic(&fastFileStream));
    quint32 version = pParseFFVersion(&fastFileStream);
    SetVersion(version);
    SetPlatform(pCalculateFFPlatform(version));
    SetGame("COD9");

    // For COD7/COD9, use BigEndian.
    fastFileStream.setByteOrder(XDataStream::BigEndian);
    if (GetPlatform() == "PC") {
        fastFileStream.setByteOrder(XDataStream::LittleEndian);
    }

    // Select key based on game.
    QByteArray key;
    if (GetPlatform() == "360") {
        key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
    } else if (GetPlatform() == "PC") {
        key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
    }

    // Read the 8-byte magic.
    QByteArray fileMagic(8, Qt::Uninitialized);
    fastFileStream.readRawData(fileMagic.data(), 8);
    if (fileMagic != "PHEEBs71") {
        qWarning() << "Invalid fast file magic!";
        return false;
    }
    fastFileStream.skipRawData(4);

    // Read IV table name (32 bytes).
    QByteArray fileName(32, Qt::Uninitialized);
    fastFileStream.readRawData(fileName.data(), 32);

    // Skip the RSA signature (256 bytes).
    QByteArray rsaSignature(256, Qt::Uninitialized);
    fastFileStream.readRawData(rsaSignature.data(), 256);

    if (GetPlatform() == "360") {
        //decompressedData = Compressor::cod9_decryptFastFile(aData);
    } else if (GetPlatform() == "PC") {
        decompressedData = Encryption::decryptFastFile_BO2(aData);
    }

    // For COD9, write out the complete decompressed zone for testing.
    QFile testFile("exports/" + GetBaseStem() + ".zone");
    if(testFile.open(QIODevice::WriteOnly)) {
        testFile.write(decompressedData);
        testFile.close();
    }

    // Load the zone file with the decompressed data (using an Xbox platform flag).
    ZoneFile_COD11_PS3* zoneFile = new ZoneFile_COD11_PS3();
    zoneFile->SetStem(GetBaseStem() + ".zone");
    if (!zoneFile->Load(decompressedData)) {
        qWarning() << "Failed to load ZoneFile!";
        return false;
    }
    SetZoneFile(zoneFile);

    return true;
}
