/*
    SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>

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



#include "k3bprocess.h"
#include "k3bexternalbinmanager.h"

#include <QByteArray>
#include <QDebug>
#include <QStringList>


namespace {
    QStringList splitOutput( const QByteArray& data,
                             QString& unfinishedLine,
                             bool suppressEmptyLines )
    {
        //
        // The stderr splitting is mainly used for parsing of messages
        // That's why we simplify the data before proceeding
        //
        int len = data.length();

        QByteArray buffer;
        for( int i = 0; i < len; i++ ) {
            if( data[i] == '\b' ) {
                while( i < len &&
                       data[i] == '\b' )  // we replace multiple backspaces with a single line feed
                    i++;
                buffer += '\n';
            }
            if ( i < len ) {
                if( data[i] == '\r' )
                    buffer += '\n';
                else if( data[i] == '\t' )  // replace tabs with a single space
                    buffer += ' ';
                else
                    buffer += data[i];
            }
        }

        QStringList lines = QString::fromLocal8Bit( buffer ).split( '\n', suppressEmptyLines ? Qt::SkipEmptyParts : Qt::KeepEmptyParts );

        // in case we suppress empty lines we need to handle the following separately
        // to make sure we join unfinished lines correctly
        if( suppressEmptyLines && buffer.startsWith( '\n' ) )
            lines.prepend( QString() );

        if( !unfinishedLine.isEmpty() ) {
            lines.first().prepend( unfinishedLine );
            unfinishedLine.truncate(0);

            qDebug() << "(K3b::Process)           joined line: '" << (lines.first()) << "'";
        }

        QStringList::iterator it;

        // check if line ends with a newline
        // if not save the last line because it is not finished
        if ( !buffer.isEmpty() ) {
            const auto c = buffer[buffer.length()-1];
            bool hasUnfinishedLine = ( c != '\n' && c != '\r' && QChar( c ) != QChar(46) );  // What is unicode 46?? It is printed as a point
            if( hasUnfinishedLine ) {
                qDebug() << "(K3b::Process) found unfinished line: '" << lines.last() << "'";
                qDebug() << "(K3b::Process)             last char: '" << buffer.right(1) << "'";
                unfinishedLine = lines.takeLast();
            }
        }

        return lines;
    }
}


class K3b::Process::Private
{
public:
    QString unfinishedStdoutLine;
    QString unfinishedStderrLine;

    bool suppressEmptyLines;

    bool bSplitStdout;
};


K3b::Process::Process( QObject* parent )
    : K3bKProcess( parent ),
      d( new Private() )
{
    setNextOpenMode( ReadWrite|Unbuffered );
    d->suppressEmptyLines = true;
    d->bSplitStdout = false;

    connect( this, SIGNAL(readyReadStandardError()),
             this, SLOT(slotReadyReadStandardError()) );
    connect( this, SIGNAL(readyReadStandardOutput()),
             this, SLOT(slotReadyReadStandardOutput()) );
}


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


K3b::Process& K3b::Process::operator<<( const K3b::ExternalBin* bin )
{
    return static_cast<Process&>( K3bKProcess::operator<<( bin->path() ) );
}


K3b::Process& K3b::Process::operator<<( const char* arg )
{
    return static_cast<Process&>( K3bKProcess::operator<<( QLatin1String( arg ) ) );
}


K3b::Process& K3b::Process::operator<<( const QByteArray& arg )
{
    return static_cast<Process&>( K3bKProcess::operator<<( QLatin1String( arg ) ) );
}


K3b::Process& K3b::Process::operator<<( const QLatin1String& arg )
{
    return static_cast<Process&>( K3bKProcess::operator<<( arg ) );
}


void K3b::Process::setSplitStdout( bool b )
{
    d->bSplitStdout = b;
}


void K3b::Process::slotReadyReadStandardOutput()
{
    if( d->bSplitStdout ) {
        QStringList lines = splitOutput( readAllStandardOutput(), d->unfinishedStdoutLine, d->suppressEmptyLines );

        for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
            QString& str = *it;

            // just to be sure since something in splitOutput does not do this right
            if( d->suppressEmptyLines && str.isEmpty() )
                continue;

            emit stdoutLine( str );
        }
    }
}


void K3b::Process::slotReadyReadStandardError()
{
    QStringList lines = splitOutput( readAllStandardError(), d->unfinishedStderrLine, d->suppressEmptyLines );

    for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
        QString& str = *it;

        // just to be sure since something in splitOutput does not do this right
        if( d->suppressEmptyLines && str.isEmpty() )
            continue;

        emit stderrLine( str );
    }
}


void K3b::Process::setSuppressEmptyLines( bool b )
{
    d->suppressEmptyLines = b;
}


QString K3b::Process::joinedArgs()
{
    return program().join( " " );
}


void K3b::Process::close()
{
    qDebug();
    closeWriteChannel();
    closeReadChannel( QProcess::StandardOutput );
}


bool K3b::Process::start( KProcess::OutputChannelMode mode )
{
    qDebug();
    setOutputChannelMode( mode );
    K3bKProcess::start();
    qDebug() << "started";
    return K3bQProcess::waitForStarted();
}

#include "moc_k3bprocess.cpp"
