/*
 * @(#)Cdrecord.cpp
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 2000-2002 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDWserver.cpp for details.
 *
 * Jörg Haeger, 13.03.2000
 */

#include "Config.h"
#include "Support.h"
#include "Cdrecord.h"
#include "CDstate.h"

Cdrecord::Cdrecord(int writerNo, struct CDstate *CDstate):
	CDstate(CDstate),
	Process(config.getCdrecordPath())
{
	for (int i = 0; i < 256; i++)
	{
		results[i].key = -1;
		results[i].code = 0;
		results[i].qual = 0;
		results[i].str = NULL;
		results[i].successCounter = 0;
	}
	firstError = lastCommand = lastError = -1;

	cannotReadTOC = cannotReadSessionOffset = 0;
	fixating = 0;
	mayNotFit = 0;
	mediumNotPresent = 0;
	fifoPercent = MBwritten = MBofTrackWritten = MBtotal = 0;
	time(&t0);
	t1 = t2 = t0;

	// add dev-argument of cdrecord
	char str[4*1024];
	sprintf(str, "dev=%s", config.getWriter(writerNo));
	addArg(str);
}

Cdrecord::~Cdrecord()
{
	for (int i = 0; i < 256; i++)
		if (results[i].str != NULL)
			delete[] results[i].str;
}

const char *Cdrecord::getFirstError()
{
	if (firstError >= 0)
	{
		static char str[45 + 1];
		strncpy(str, results[firstError].str, sizeof str - 1);
		str[sizeof str - 1] = 0;
		return str;
	}
	else
	if (mayNotFit)
		return "may not fit";
	else
	if (CDstate != NULL && CDstate->manufacturerIndex == -1)
		return "perhaps a DVD+RW";
	else
		return NULL;
}

const char *Cdrecord::getLastError()
{
	if (lastError < 0)
		return NULL;
	else
	{
		static char str[45 + 1];
		strncpy(str, results[lastError].str, sizeof str - 1);
		str[sizeof str - 1] = 0;
		return str;
	}
}

void Cdrecord::getProgress(int &current, int &total)
{
/*	time(&t1);
	current = t1 - t0;
	total = t2 - t0;
*/
	current = MBwritten + MBofTrackWritten;
	total = MBtotal;
}

int Cdrecord::isBlankDisk()
{
	// a blank disk is detected by read toc
	return results[0x43].code == 0x8F && results[0x43].qual == 0x00;
}

int Cdrecord::isDriveReady()
{
	return results[0x00].successCounter > 0;
//		results[0x00].key == 0x00
//		&& results[0x00].code == 0x00
//		&& results[0x00].qual == 0x00;
}

int Cdrecord::isInProcessOfBecomingReady()
{
	// logical unit is in process of becoming ready
	return results[0x00].code == 0x04 && results[0x00].qual == 0x01;
}

int Cdrecord::isMediumNotPresent()
{
//	return mediumNotPresent;
	return results[0x00].code == 0x3A;
}

int Cdrecord::isTrayClosed()
{
	return (results[0x00].code == 0x3A && results[0x00].qual == 0x01)
		|| isDriveReady();
}

const char *Cdrecord::readLine(int timeout)
{
	const char *lineStr = Process::readLine(timeout);
	if (lineStr == NULL)
		return NULL;

	lineType = none;
	int a, b, c, d = 100;
/*	if (sscanf(lineStr, "  ATIP start of lead out: %d", &a) == 1)
	{
		if (CDstate != NULL)
			CDstate->ATIPLeadOut = a;
	}
	else
*/	if (sscanf(lineStr, "Blocks total: %d "
		"Blocks current: %d "
		"Blocks remaining: %d", &a, &b, &c) == 3)
	{
		if (CDstate != NULL)
		{
			CDstate->blocks0 = a;
			CDstate->blocks1 = b;
			CDstate->blocks2 = c;
		}
		lineType = capacity;
	}
	else
	if (strstr(lineStr, "Cannot read first writable address") != NULL)
		throw "can't read first writable address";
	else
	if (strstr(lineStr, "Cannot read session offset") != NULL)
		cannotReadSessionOffset = 1;
	else
	if (strstr(lineStr, "Cannot read TOC header") != NULL
		|| strstr(lineStr, "Cannot read TOC/PMA") != NULL)
		cannotReadTOC = 1;
	else
	if (sscanf(lineStr, "CDB: %x", &lastCommand) == 1)
	{
		// see cdrecord-1.8/libscg/scsierrs.c
		if (0 <= lastCommand && lastCommand < 256)
		{
			// start of the next command
			results[lastCommand].key = 0;
			results[lastCommand].code = 0;
			results[lastCommand].qual = 0;
			if (results[lastCommand].str != NULL) {
				delete[] results[lastCommand].str;
				results[lastCommand].str = NULL;
			}
		}
	}
	else
	if (strstr(lineStr, "cmd finished") == lineStr) {
		if (results[lastCommand].key == 0
			&& results[lastCommand].code == 0
			&& results[lastCommand].qual == 0)
			results[lastCommand].successCounter++;
	}
	else
	if (sscanf(lineStr, "Current Secsize: %d", &a) == 1)
	{
		if (CDstate != NULL)
			CDstate->blockSize = a;
	}
	else
/*	if (strstr(lineStr, "Drive needs to reload") != NULL)
	{
		// the media to return to proper status.
		terminate();
		throw "reload media";
	}
	else
*/	if (strstr(lineStr, "Fixating...") == lineStr)
		fixating = 1;
	else
	if (strstr(lineStr, "Is erasable") != NULL)
	{
		if (CDstate != NULL)
			CDstate->isRW = 1;
	}
	else
	if (strstr(lineStr, "Is not erasable") != NULL)
	{
		if (CDstate != NULL)
			CDstate->isRW = 0;
	}
	else
	if (strstr(lineStr, "Last chance to quit") != NULL)
	{
		if (mayNotFit)
		{
			terminate();
			throw "may not fit";
		}
		else
		if (CDstate != NULL && !CDstate->isClosed
			&& CDstate->blocks2 < 4500 + 6750)
		{
			terminate();
			throw "restartWithoutMulti";
		}
	}
	else
	if (sscanf(lineStr, "Manuf. index: %d", &a) == 1)
	{
		if (CDstate != NULL)
			CDstate->manufacturerIndex = a;
	}
	else
	if (strstr(lineStr, "may not fit on current disk") != NULL)
		mayNotFit = 1;
	else
	if (strstr(lineStr, "Re-load disk and hit <CR>") != NULL)
		throw "Hardware error. Try again!";
	else
	if (sscanf(lineStr, "Sense Code: %x Qual %x", &code, &qual) == 2)
	{
		if (0 <= lastCommand && lastCommand < 256)
		{
//			printf("cmd = %02x, key = %02x, "
//				"code = %02x, qual = %02x\n",
//				lastCommand, key, code, qual);
			if (firstError == -1)
				firstError = lastCommand;
			lastError = lastCommand;
			results[lastCommand].key = key;
			results[lastCommand].code = code;
			results[lastCommand].qual = qual;

			char *a = strchr(lineStr, '(');
			char *b = strchr(lineStr, ')');
			if (a != NULL && b != NULL && a < b)
			{
				char str[45 + 1];
				int n = min(b - a - 1, sizeof str - 1);
				strncpy(str, a + 1, n);
				str[n] = 0;
				results[lastCommand].str = strDup(str);
			}
		}
		if (strstr(lineStr, "medium not present") != NULL)
			mediumNotPresent = 1;
	}
	else
	if (sscanf(lineStr, "Sense Key: %x", &key) == 1)
		;
	else
	if (sscanf(lineStr, "Starting to write CD/DVD at speed %d", &a) == 1)
	{
		if (CDstate != NULL)
		{
			CDstate->speed = a;
			if (CDstate->speed == 0)
				CDstate->speed = 1;
			if (strstr(lineStr, "multi session") != NULL)
				CDstate->isClosed = 0;
			else
			if (strstr(lineStr, "single session") != NULL)
				CDstate->isClosed = 1;

			time(&t0);
			t1 = t0;
			int numOfSectors = 4500
				+ (CDstate->blocks1 - CDstate->blocks2)
				+ 2250;
			if (CDstate->blocks1 == CDstate->blocks0)
				numOfSectors += 4500;
			t2 = t0 + 9 + numOfSectors / 75 / CDstate->speed;
		}
	}
	else
	if (sscanf(lineStr, "Track %d: audio %d",
		&a, &b) == 2)
//		numOfSectors += (1024 * 1024 * b) / 2352;
		MBtotal += b;
	else
	if (sscanf(lineStr, "Track %d: data %d",
		&a, &b) == 2)
//		numOfSectors += (1024 * 1024 * b) / 2048;
		MBtotal += b;
	else
	if (sscanf(lineStr, "Track %d: %d of %d MB written (fifo %d",
		&a, &b, &c, &d) >= 3)
	{
		lineType = progress;
		if (b == c)
		{
			MBwritten += c;
			MBofTrackWritten = 0;
		}
		else
			MBofTrackWritten = b;
		fifoPercent = d;
	}

	return lineStr;
}
