/*
    SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "k3bmkisofshandler.h"

#include "k3bexternalbinmanager.h"
#include "k3bcore.h"
#include "k3bjob.h"
#include "k3b_i18n.h"

#include <QDebug>
#include <QRegularExpression>

#include <cmath>



class K3b::MkisofsHandler::Private
{
public:
    const K3b::ExternalBin* mkisofsBin;
    double firstProgressValue;
    bool readError;
};


K3b::MkisofsHandler::MkisofsHandler()
{
    d = new Private;
    d->mkisofsBin = nullptr;
}


K3b::MkisofsHandler::~MkisofsHandler()
{
    delete d;
}


bool K3b::MkisofsHandler::mkisofsReadError() const
{
    return d->readError;
}


const K3b::ExternalBin* K3b::MkisofsHandler::initMkisofs()
{
    d->mkisofsBin = k3bcore->externalBinManager()->binObject( "mkisofs" );

    if( d->mkisofsBin ) {
        if( !d->mkisofsBin->copyright().isEmpty() )
            handleMkisofsInfoMessage( i18n("Using %1 %2 – Copyright © %3",
                                           QString("mkisofs"),
                                           d->mkisofsBin->version(),
                                           d->mkisofsBin->copyright()),
                                      K3b::Job::MessageInfo );

        d->firstProgressValue = -1;
        d->readError = false;
    }
    else {
        qDebug() << "(K3b::MkisofsHandler) could not find mkisofs executable";
        handleMkisofsInfoMessage( i18n("Mkisofs executable not found."), K3b::Job::MessageError );
    }

    return d->mkisofsBin;
}


void K3b::MkisofsHandler::parseMkisofsOutput( const QString& line )
{
    if( !line.isEmpty() ) {
        if( line.startsWith( d->mkisofsBin->path() ) ) {
            // error or warning
            QString errorLine = line.mid( d->mkisofsBin->path().length() + 2 );
            if( errorLine.startsWith( "Input/output error. Cannot read from" ) ) {
                handleMkisofsInfoMessage( i18n("Read error from file '%1'", errorLine.mid( 38, errorLine.length()-40 ) ),
                                          K3b::Job::MessageError );
                d->readError = true;
            }
            else if( errorLine.startsWith( "Value too large for defined data type" ) ) {
                handleMkisofsInfoMessage( i18n("Used version of mkisofs does not have large file support."), K3b::Job::MessageError );
                handleMkisofsInfoMessage( i18n("Files bigger than 2 GB cannot be handled."), K3b::Job::MessageError );
                d->readError = true;
            }
            else if( errorLine.startsWith( "No such file or directory. cannot open" ) ) {
                handleMkisofsInfoMessage( i18n("No such file or directory '%1'.", errorLine.mid( 40, errorLine.length()-41 ) ),
                                          K3b::Job::MessageError );
                d->readError = true;
            }
        }
        else if( line.contains( "done, estimate" ) ) {
            int p = parseMkisofsProgress( line );
            if( p != -1 )
                handleMkisofsProgress( p );
        }
        else if( line.contains( "extents written" ) ) {
            handleMkisofsProgress( 100 );
        }
        else if( line.startsWith( "Incorrectly encoded string" ) ) {
            static const QRegularExpression rx("[\\(\\)]");
            handleMkisofsInfoMessage( i18n("Encountered an incorrectly encoded filename '%1'",
                                           line.section( rx, 1, 1 )), K3b::Job::MessageError );
            handleMkisofsInfoMessage( i18n("This may be caused by a system update which changed the local character set."), K3b::Job::MessageError );
            handleMkisofsInfoMessage( i18n("You may use convmv (https://j3e.de/linux/convmv/) to fix the filename encoding."), K3b::Job::MessageError );
            d->readError = true;
        }
        else if( line.endsWith( "has not an allowable size." ) ) {
            handleMkisofsInfoMessage( i18n("The boot image has an invalid size."), K3b::Job::MessageError );
            d->readError = true;
        }
        else if( line.endsWith( "has multiple partitions." ) ) {
            handleMkisofsInfoMessage( i18n("The boot image contains multiple partitions."), K3b::Job::MessageError );
            handleMkisofsInfoMessage( i18n("A hard-disk boot image has to contain a single partition."), K3b::Job::MessageError );
            d->readError = true;
        }
        else {
            qDebug() << "(mkisofs) " << line;
        }
    }
}


int K3b::MkisofsHandler::parseMkisofsProgress(const QString& line)
{
    // in multisession mode mkisofs' progress does not start at 0 but at (X+Y)/X
    // where X is the data already on the cd and Y the data to create
    // This is not very dramatic but kind or ugly.
    // We just save the first emitted progress value and to some math ;)

    QString perStr = line;
    perStr.truncate(perStr.indexOf('%'));
    // FIXME: how to support Inuit or Samaritan Aramaic format or cover all
    // formats? right now it only support, for example: 0.52 and 0,52
    static const QRegularExpression rx("(\\d+.|,+\\d)");
    QStringList list;
    for (auto it = rx.globalMatch(perStr); it.hasNext();) {
        const auto match = it.next();
        list << match.captured(1);
    }
    if (list.size() < 2)
        return -1;
    // FIXME: the same story
    bool ok;
    double p = (list[0].replace(',', '.') + list[1]).toDouble(&ok);
    if (!ok) {
        qDebug() << "(K3b::MkisofsHandler) Parsing did not work for " << perStr;
        return -1;
    } else {
        if (d->firstProgressValue < 0)
            d->firstProgressValue = p;

        return(int(::ceil((p - d->firstProgressValue) * 100.0 /
                           (100.0 - d->firstProgressValue))));
    }
}
