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

#include "Config.h"
#include "Cdrecord.h"
#include "CDstate.h"
#include "String.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;
	trayOpen = -1;
	fifoPercent = MBwritten = MBofTrackWritten = MBtotal = 0;
	time(&t0);
	t1 = t2 = t0;
	overburn = 0;
	fixationSeconds = (4500 + 2250 + 4500) / 75 / 1;

	// 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 && firstError < 256) {
		if (results[firstError].str == NULL)
			results[firstError].str = new String();
		return results[firstError].str->getBytes();
	}
	else
	if (mayNotFit)
		return "may not fit";
	else
		return NULL;
}

const char *Cdrecord::getLastError() {
	if (lastError >= 0 && lastError < 256) {
		if (results[lastError].str == NULL)
			results[lastError].str = new String();
		return results[lastError].str->getBytes();
	}
	else
		return NULL;
}

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 trayOpen == 0;
}

bool Cdrecord::isTrayOpen() {
	return mediumNotPresent && trayOpen == 1;
}

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 new Exception("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)
				results[lastCommand].str = new String();
			*results[lastCommand].str = "";
		}
		else
			lastCommand = -1;
	}
	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 new Exception("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 new Exception("may not fit");
		}
		else
		if (CDstate != NULL && !CDstate->isClosed
			&& CDstate->blocks2 < 4500 + 6750) {
			terminate();
			throw new Exception("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 = !overburn;
	else
	if (sscanf(lineStr, "Mode Sense Data %x %x %x", &a, &b, &c) == 3) {
		if (trayOpen < 0 && c > 0)
			trayOpen = c & 0x01;
	}
	else
	if (strstr(lineStr, "Re-load disk and hit <CR>") != NULL)
		throw new Exception("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;

			String line = lineStr;
			int p0 = line.indexOf("(");
			if (p0 >= 0) {
				int p = line.indexOf(")");
				if (p > p0) {
					if (p - p0 > 45)
						p = p0 + 45;
					String str = line.substring(p0 + 1, p);
					if (results[lastCommand].str == NULL)
						results[lastCommand].str = new String();
					*results[lastCommand].str = str;
				}
			}
		}
//		if (strstr(lineStr, "medium not present") != NULL) {
		if (lastCommand == 0x00 && code == 0x3A) {
			mediumNotPresent = 1;
//			if (strstr(lineStr, "- tray closed") != NULL)
			if (qual == 0x01)
				trayOpen = 0;
			else
//			if (strstr(lineStr, "- tray open") != NULL)
			if (qual == 0x02)
				trayOpen = 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;

			numOfSectors = 4500 + 2250;
			if (CDstate->blocks1 == CDstate->blocks0)
				numOfSectors += 4500;
			fixationSeconds = numOfSectors / 75 / CDstate->speed;
			if (CDstate->isRW)
				fixationSeconds = 220 * fixationSeconds / 100;
			else
				fixationSeconds = 140 * fixationSeconds / 100;
		}
	}
	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;
	}
	else
	if (strstr(lineStr,
		"Trying to use high speed medium on low speed writer.") != NULL)
		throw new Exception(
			"Trying to use high speed medium on low speed writer.");

	return lineStr;
}
