
/*
 * See the files COPYING and README for copyright information and how to reach 
 * the author.
 *
 *  $Id: jobs.c,v 1.3 2004/12/02 15:02:52 lordjaxom Exp $
 */

#include "i18n.h"
#include "jobs.h"
#include "setup.h"
#include "process-dvd.h"
#include "process-vdr.h"
#include <vdr/recording.h>
#include <vdr/interface.h>
#include <vdr/videodir.h>
#include <vdr/plugin.h>
#include <vdr/config.h>

#define NEXT_ARCHIVE_MARKER "/.nextArchiveDVD"

// --- cBurnRecording ---------------------------------------------------------

cBurnRecording::cBurnRecording(cRecording *Recording, time_t Timestamp)
{
    char *ptr, *tmp;
    mFileName = strdup(Recording->FileName());
    strncpy(mName, Recording->Name(), JOBNAMELEN);
#if VDRVERSNUM < 10325
    mSummary = (Recording->Summary()? strdup(Recording->Summary()) : NULL);
#else
    if (Recording->Info()->Title()) {
            asprintf(&mRecordingTitle, "%s",
                     Recording->Info()->Title());
        }

    if (Recording->Info()->ShortText()) {
        if (Recording->Info()->Description()) {
//            asprintf(&mSummary, "%s\n%s\n\n%s", Recording->Info()->Title(),
            asprintf(&mSummary, "%s\n\n%s",
                     Recording->Info()->ShortText(),
                     Recording->Info()->Description());
        } else {
//            asprintf(&mSummary, "%s\n%s", Recording->Info()->Title(),
            asprintf(&mSummary, "%s",
                     Recording->Info()->ShortText());
        }
    } else {
        if (Recording->Info()->Description()) {
//            asprintf(&mSummary, "%s\n\n%s", Recording->Info()->Title(),
            asprintf(&mSummary, "%s", 
                     Recording->Info()->Description());
        } else {
            asprintf(&mSummary, "%s", Recording->Info()->Title());
        }
    }
#endif
    mTempDir = NULL;
    mTimestamp = Timestamp;
    mNumber = -1;
    mSizeMB = 0;
    mDt = dtDVD;

    tmp = mName;
    do {
        ptr = tmp;
        while (strchr("@%", *ptr) != NULL)
            ++ptr;
        memmove(tmp, ptr, strlen(ptr) + 1);
        if ((tmp = strchr(tmp, '~')) != NULL) {
            ++tmp;
            if (BurnSetup.HidePathInfo) {
                memmove(mName, tmp, strlen(tmp) + 1);
                tmp = mName;
            }
        }
    } while (tmp != NULL);

    mTitle = strdup(Recording->Title('\t'));
    ptr = mTitle;
    while ((tmp = strchr(ptr + 1, '\t')) != NULL) {
        *tmp = ' ';
        ptr = tmp;
    }
    *ptr = '\0';
}

cBurnRecording::cBurnRecording(const cBurnRecording &Recording)
{
    mFileName = strdup(Recording.mFileName);
    strncpy(mName, Recording.mName, JOBNAMELEN);
    mSummary = (Recording.mSummary ? strdup(Recording.mSummary) : NULL);
    mTitle = strdup(Recording.mTitle);
    mTempDir = (Recording.mTempDir ? strdup(Recording.mTempDir) : NULL);
    mTimestamp = Recording.mTimestamp;
    mNumber = Recording.mNumber;
    mSizeMB = Recording.mSizeMB;
    mDt = Recording.mDt;
}

cBurnRecording::~cBurnRecording(void)
{
    free(mFileName);
    free(mSummary);
    free(mTitle);
    free(mTempDir);
}

void cBurnRecording::SetTitle(const char *Title)
{
    free(mTitle);
    mTitle = strdup(Title);
}

void cBurnRecording::SetTempDir(const char *TempDir)
{
    free(mTempDir);
    asprintf(&mTempDir, "%s/VDRSYNC.%d", TempDir, mNumber);
}

double cBurnRecording::SizeMB(eDiskType dt)
{
    if (mSizeMB == 0 || mDt != dt) {
        mSizeMB = ScanRecordingSize(FileName(), dt) / 1024.f;
        mDt = dt;
    }
    return mSizeMB;
}

const void cBurnRecording::RefreshTracks(void)
{
    mTracks.Clear();
    mSizeMB = 0;
}

const tTrackInfoArray & cBurnRecording::GetTracks(void)
{
    if (!mTracks.Size()) {
        mTracks = FindTitleTracks(mTempDir);
    }
    isyslog("BURN: GetTracks of: %s - %s", mTempDir, mTracks[0]->Filename());
    return mTracks;
}

const char *cBurnRecording::GetChapters(eChaptersMode mChaptersMode)
{
    char *mChapters;
    asprintf(&mChapters, "0");

    if (mChaptersMode == cmNoChapters) {
        return mChapters;
    } else if (mChaptersMode == cm5Mins) {
        asprintf(&mChapters,
                "0,5:00,10:00,15:00,20:00,25:00,30:00,35:00,40:00,45:00,"
                "50:00,55:00,1:00:00,1:05:00,1:10:00,1:15:00,1:20:00,"
                "1:25:00,1:30:00,1:35:00,1:40:00,1:45:00,1:50:00,1:55:00,"
                "2:00:00,2:05:00,2:10:00,2:15:00,2:20:00,2:25:00,2:30:00,"
                "2:35:00,2:40:00,2:45:00,2:50:00,2:55:00,3:00:00,3:05:00,"
                "3:10:00,3:15:00,3:20:00,3:25:00,3:30:00,3:35:00,3:40:00,"
                "3:45:00,3:50:00,3:55:00,4:00:00");
        return mChapters;
    } else if (mChaptersMode == cm10Mins) {
        asprintf(&mChapters, "0,10:00,20:00,30:00,40:00,50:00,"
                "1:00:00,1:10:00,1:20:00,1:30:00,1:40:00,1:50:00,"
                "2:00:00,2:10:00,2:20:00,2:30:00,2:40:00,2:50:00,"
                "3:00:00,3:10:00,3:20:00,3:30:00,3:40:00,3:50:00,"
                "4:00:00");
        return mChapters;
    } else if (mChaptersMode == cm15Mins) {
        asprintf(&mChapters, "0,15:00,30:00,45:00,1:00:00,1:15:00,"
                "1:30:00,1:45:00,2:00:00,2:15:00,2:30:00,2:45:00,"
                "3:00:00,3:15:00,3:30:00,3:45:00,4:00:00");
        return mChapters;
    } else if (mChaptersMode == cm30Mins) {
        asprintf(&mChapters, "0,30:00,1:00:00,1:30:00,2:00:00,2:30:00,"
                "3:00:00,3:30:00,4:00:00");
        return mChapters;
    } else if (mChaptersMode == cm60Mins) {
        asprintf(&mChapters, "0," "1:00:00," "2:00:00," "3:00:00," "4:00:00");
        return mChapters;
    } else if (mChaptersMode == cmEditingMarks) {
        cMarks marks;
        cMark *mark;
        int counter = 0;

        if (marks.Load(mFileName)) {
            marks.Sort();
            if (marks.Count() % 2 != 0) {
                isyslog("BURN: GetChapters: An odd number of marks (%i) have been loaded, which will usually lead to unmeant behaviour!",
                        marks.Count());
            }
            mark = marks.GetNext(0);    // get 2nd mark
            while (mark) {
                if (((++counter) % 2 == 0) && (marks.GetNext(mark->position))) {
                    asprintf(&mChapters, "%s,%s", mChapters,
                             (const char*)IndexToHMSF(mark->position, false));
                }
                mark = marks.GetNext(mark->position);
            }
            isyslog("BURN: GetChapters: Following marks loaded: %s",
                    mChapters);
        } else {
            // No marks could be loaded
            isyslog("BURN: GetChapters: Loading marks failed! Chapters every 10 minutes.");
            asprintf(&mChapters,
                    "0,10:00,20:00,30:00,40:00,50:00,1:00:00,1:10:00,"
                    "1:20:00,1:30:00,1:40:00,1:50:00,2:00:00,2:10:00,"
                    "2:20:00,2:30:00,2:40:00,2:50:00,3:00:00,3:10:00,"
                    "3:20:00,3:30:00,3:40:00,3:50:00,4:00:00");
        }
        return mChapters;
    } else {
        isyslog("BURN: GetChapters: Unexpected cmChaptersMode! Chapters every 10 minutes.");
        asprintf(&mChapters,
                "0,10:00,20:00,30:00,40:00,50:00,1:00:00,1:10:00,1:20:00,"
                "1:30:00,1:40:00,1:50:00,2:00:00,2:10:00,2:20:00,2:30:00,"
                "2:40:00,2:50:00,3:00:00,3:10:00,3:20:00,3:30:00,3:40:00,"
                "3:50:00,4:00:00");
        return mChapters;
    }
}

// --- cBurnJob ---------------------------------------------------------------

cBurnJob::cBurnJob(void)
{
    mTempDir = NULL;
    mTitle[0] = '\0';
    mDiskType = (eDiskType)BurnSetup.DiskType;
    mStoreMode = (eStoreMode)BurnSetup.StoreMode;
    mChaptersMode = (eChaptersMode)BurnSetup.ChaptersMode;
    mBurnMode = bmShrink;
    mProcess = NULL;
    mIsProcessed = false;
    mMarksSet = false;
    mArchiveMark = -1;
}

cBurnJob::~cBurnJob()
{
    DELETENULL(mProcess);
    free(mTempDir);
}

double cBurnJob::SizeMB()
{
    cBurnRecording *rec;
    double sizeMB = 0.0;
    for (rec = First(); rec; rec = Next(rec)) {
        sizeMB += rec->SizeMB(DiskType());
    }
    return sizeMB;
}

void cBurnJob::Start(void)
{
    mIsProcessed = true;

    if (mProcess) {
        if (mProcess->IsPremastered()) {
            isyslog("BURN: repeating job");
            mProcess->Repeat();
            return;
        }
        DELETENULL(mProcess);
    }

    BurnSetup.DiskType = (int)mDiskType;
    BurnSetup.StoreMode = (int)mStoreMode;
    BurnSetup.ChaptersMode = (int)mChaptersMode;
    cPlugin *p = cPluginManager::GetPlugin("burnold");
    if (p) {
        cMenuSetupPage *menu = p->SetupMenu();
        if (menu) {
            menu->SetPlugin(p);
            menu->ProcessKey(kOk);
            delete menu;
        }
    }

    switch (mDiskType) {
        case dtDVD:
        case dtDVDnm:
            mProcess = new cBurnProcessDVD(this);
            break;

        case dtVDR:
            mProcess = new cBurnProcessVDR(this);
            break;

        default:
            break;
    };
}

void cBurnJob::Stop(void)
{
    if (mProcess) {
        mProcess->Stop();
    }
}

void cBurnJob::Add(cBurnRecording * Object)
{
    cBurnRecording *before = First();
    Object->SetNumber(Count());
    if (mTitle[0] == '\0') {
        strncpy(mTitle, Object->Name(), JOBNAMELEN);
    }
    while (before && before->Timestamp() < Object->Timestamp()) {
        before = Next(before);
    }
    if (before) {
        cListBase::Ins(Object, before);
    } else {
        cListBase::Add(Object);
    }
}

void cBurnJob::SetTempDir(const char *TempDir)
{
    for (cBurnRecording * rec = First(); rec; rec = Next(rec)) {
        rec->SetTempDir(TempDir);
    }
    mTempDir = strdup(TempDir);
}

cBurnRecording *cBurnJob::GetByFileName(const char *FileName)
{
    for (cBurnRecording * rec = First(); rec; rec = Next(rec)) {
        if (strcmp(rec->FileName(), FileName) == 0) {
            return rec;
        }
    }
    return NULL;
}

/* Don not call getArchiveMark before previous archive jobs are finished
 * or you will get duplicate archive numbers
 */
int cBurnJob::GetArchiveMark()
{
    if (mArchiveMark < 0) {
        mArchiveMark = GetNextArchiveMark();
    }
    return mArchiveMark;
}

int cBurnJob::GetNextArchiveMark()
{
    char *nextMarkerFileName = (char *)alloca(strlen(VideoDirectory) +
            strlen(NEXT_ARCHIVE_MARKER) + 1);
    if (!nextMarkerFileName) {
        return -1;
    }
    strcpy(nextMarkerFileName, VideoDirectory);
    char *end = nextMarkerFileName + strlen(VideoDirectory);
    strcpy(end, NEXT_ARCHIVE_MARKER);
    FILE *nextMarkerFile;

    int nextMark = 1;
    if ((nextMarkerFile = fopen(nextMarkerFileName, "r")) != (FILE *) NULL) {
        char *nextDVDText = (char *)alloca(16);
        if (!nextDVDText) {
            fclose(nextMarkerFile);
            return -1;
        }
        if (fgets(nextDVDText, 16, nextMarkerFile)) {
            if ((end = strchr(nextDVDText, '\n'))) {
                *end = 0;
            }
        }
        fclose(nextMarkerFile);
        if (strlen(nextDVDText)) {
            int tmp = strtol(nextDVDText, &end, 10);
            if (*end == 0) {
                nextMark = tmp;
            }
        }
    }
    return nextMark;
}

bool cBurnJob::CreateNextArchiveMark()
{
    if (!BurnSetup.CreateArchiveMarks) {
        return true;
    }

    int nextMark = GetNextArchiveMark();
    if (nextMark < 0) {
        return false;
    }

    nextMark++;

    char *nextMarkerFileName = (char *)alloca(strlen(VideoDirectory) +
            strlen(NEXT_ARCHIVE_MARKER) + 1);
    if (!nextMarkerFileName) {
        return false;
    }
    strcpy(nextMarkerFileName, VideoDirectory);
    char *end = nextMarkerFileName + strlen(VideoDirectory);
    strcpy(end, NEXT_ARCHIVE_MARKER);
    FILE *nextMarkerFile;

    if ((nextMarkerFile = fopen(nextMarkerFileName, "w")) != (FILE *) NULL) {
        int retVal = fprintf(nextMarkerFile, "%04d\n", nextMark);
        fclose(nextMarkerFile);
        if (retVal < 0) {
            return false;
        }
    }
    return true;
}

bool cBurnJob::CreateArchiveMarks()
{
    if (!BurnSetup.CreateArchiveMarks || mMarksSet) {
        return true;
    }

    int nextMark = GetArchiveMark();
    if (nextMark < 0) {
        return false;
    }

    cBurnRecording *rec;
    for (rec = First(); rec; rec = Next(rec)) {
        char *markerFileName = NULL;
        if (asprintf(&markerFileName, "%s/dvd.vdr", rec->FileName()) < 0) {
            return false;
        }
        if (access(markerFileName, F_OK) == 0) {
            Interface->Confirm(tr("Recording already archived"), 200);
            free(markerFileName);
            return false;
        }
        free(markerFileName);
    }

    for (rec = First(); rec; rec = Next(rec)) {
        char *markerFileName = NULL;
        if (asprintf(&markerFileName, "%s/dvd.vdr", rec->FileName()) < 0) {
            return false;
        }
        FILE *markerFile = NULL;
        if ((markerFile = fopen(markerFileName, "w")) == (FILE *) NULL) {
            return false;
        }
        int retVal = fprintf(markerFile, "%04d\n", nextMark);
        mMarksSet = true;
        fclose(markerFile);
        free(markerFileName);
        if (retVal < 0) {
            return false;
        }
    }
    return true;
}

bool cBurnJob::ClearArchiveMarks()
{
    if (!mMarksSet) {
        return true;
    }

    cBurnRecording *rec;
    for (rec = First(); rec; rec = Next(rec)) {
        char *markerFileName = NULL;
        if (asprintf(&markerFileName, "%s/dvd.vdr", rec->FileName()) < 0) {
            return false;
        }
        unlink(markerFileName);
        free(markerFileName);
    }
    mMarksSet = false;
    return true;
}

bool cBurnJob::Burning(int &Progress) const
{
    return mProcess ? mProcess->Burning(Progress) : false;
}

bool cBurnJob::Verifying(int &Progress) const
{
    return mProcess ? mProcess->Verifying(Progress) : false;
}

cBurnRecording *cBurnJob::Next(cBurnRecording * Recording) const
{
    return cList <cBurnRecording>::Next(Recording);
}
