/*
 * Cantata
 *
 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
 *
 * ----
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 */

#include "extractjob.h"
#include "device.h"
#include "support/utils.h"
#include "tags/tags.h"
#include "cdparanoia.h"
#include "gui/covers.h"
#include "mpd-interface/mpdconnection.h"
#include "gui/settings.h"
#include <QStringList>
#include <QProcess>
#include <QFile>

const int ExtractJob::constWavHeaderSize=44; // ffmpeg uses 46 byte header?

static void insertSize(unsigned char *data, qint32 size)
{
    data[0]=(size&0x000000ff);
    data[1]=(size&0x0000ff00)>>8;
    data[2]=(size&0x00ff0000)>>16;
    data[3]=(size&0xff000000)>>24;
}

void ExtractJob::writeWavHeader(QIODevice &dev, qint32 size)
{
    unsigned char riffHeader[] = {
        0x52, 0x49, 0x46, 0x46, // 0  "RIFF"
        0x00, 0x00, 0x00, 0x00, // 4  wavSize
        0x57, 0x41, 0x56, 0x45, // 8  "WAVE"
        0x66, 0x6d, 0x74, 0x20, // 12 "fmt "
        0x10, 0x00, 0x00, 0x00, // 16 Size of WAVE section chunk  (ffmpeg has 12 here???)
        0x01, 0x00, 0x02, 0x00, // 20 WAVE type format / Number of channels
        0x44, 0xac, 0x00, 0x00, // 24 Samples per second
        0x10, 0xb1, 0x02, 0x00, // 28 Bytes per second
        0x04, 0x00, 0x10, 0x00, // 32 Block alignment / Bits per sample
        0x64, 0x61, 0x74, 0x61, // 36 "data"  (ffmpeg preceeds this with two 0 bytes)
        0x00, 0x00, 0x00, 0x00  // 40 byteCount
    };

    if (0!=size) {
        insertSize(&riffHeader[4], (size+constWavHeaderSize)-8);
        insertSize(&riffHeader[40], size);
    }

    dev.write((char*)riffHeader, constWavHeaderSize);
}

ExtractJob::ExtractJob(const Encoders::Encoder &enc, int val, const QString &src, const QString &dest, const Song &s, const QString &cover)
    : encoder(enc)
    , value(val)
    , srcFile(src)
    , destFile(dest)
    , song(s)
    , coverFile(cover)
    , copiedCover(false)
{
}

ExtractJob::~ExtractJob()
{
}

void ExtractJob::run()
{
    if (stopRequested) {
        emit result(Device::Cancelled);
    } else {
        QStringList encParams=encoder.params(value, encoder.transcoder ? "pipe:" : "-", destFile);
        CdParanoia cdparanoia(srcFile, Settings::self()->paranoiaFull(), Settings::self()->paranoiaNeverSkip(), false, Settings::self()->paranoiaOffset());

        if (!cdparanoia) {
            emit result(Device::FailedToLockDevice);
            return;
        }
        QProcess process;
        QString cmd=encParams.takeFirst();
        process.start(cmd, encParams, QIODevice::WriteOnly);
        process.waitForStarted();

        if (stopRequested) {
            emit result(Device::Cancelled);
            process.close();
            return;
        }
        int firstSector = cdparanoia.firstSectorOfTrack(song.id);
        int lastSector = cdparanoia.lastSectorOfTrack(song.id);
        int total=lastSector-firstSector;
        int count=0;

        cdparanoia.seek(firstSector, SEEK_SET);

        writeWavHeader(process);
        while ((firstSector+count) <= lastSector) {
            qint16 *buf = cdparanoia.read();
            if (!buf) {
                emit result(Device::Failed);
                process.close();
                return;
            }
            if (stopRequested) {
                emit result(Device::Cancelled);
                process.close();
                return;
            }

            char *buffer=(char *)buf;

            qint64 writePos=0;
            do {
                qint64 bytesWritten = process.write(&buffer[writePos], CD_FRAMESIZE_RAW - writePos);
                if (stopRequested) {
                    emit result(Device::Cancelled);
                    process.close();
                    QFile::remove(destFile);
                    return;
                }
                if (-1==bytesWritten) {
                    emit result(Device::WriteFailed);
                    process.close();
                    QFile::remove(destFile);
                    return;
                }
                writePos+=bytesWritten;
            } while (writePos<CD_FRAMESIZE_RAW);

            count++;
            setPercent((count*100/total)+0.5);
        }
        process.closeWriteChannel();
        process.waitForFinished();
        Utils::setFilePerms(destFile);
        Tags::update(destFile, Song(), song, 3);

        if (!stopRequested && !coverFile.isEmpty()) {
            copiedCover=Covers::copyImage(Utils::getDir(coverFile), Utils::getDir(destFile), Utils::getFile(coverFile), Covers::albumFileName(song)+Utils::getExtension(coverFile), 0);
        }

        emit result(Device::Ok);
    }
}

#include "moc_extractjob.cpp"
