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

/*
 * A simulation of cdrecord for testing CDWserver
 */

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

const char product[] = "cdrecord-dummy";
const char version[] = "20041008 (i686-pc-linux-gnu)";
const char *Identifikation0[] = {
	"", "CD-ROM Demo", "", "", "CD-RW Demo",
	"DVD+RW Demo", "", "DVD-RW Demo"
};
const char *IdentifikationATAPI[] = {
	"ATAPI CD-RW Demo", "", "", "", "", "", "", ""
};
const char **Identifikation = Identifikation0;
char error[40] = "no";
char lastMode[20];
int lastVal = 0;

void log(const char *dev, const char *mode) {
	char str[1024];
	sprintf(str, "/tmp/cdrecord.last.%s", dev);
	FILE *out = fopen(str, "a");
	if (out == NULL)
		return;

	int val = 0;
	if (strcmp(mode, lastMode) == 0)
		val = lastVal + 1;
	fprintf(out, "%s %d\n", mode, val);
	fclose(out);
}

void printError(const char *arg0,
	int cmd, int key, int code, int qual, const char *str) {
	printf("%s: Eingabe-/Ausgabefehler.\n", arg0);
	printf("CDB: %02x 00 00 00 00 00\n", cmd);
	printf("Sense Key: 0x%x Not Ready, Segment 0\n", key);
	printf("Sense Code: 0x%02x Qual 0x%02x (%s) Fru 0x0\n",
		code, qual, str);
	printf("cmd finished after 0.001s timeout 40s\n");
}

void readLastMode(const char *dev) {
	char str[1024];
	sprintf(str, "/tmp/cdrecord.config.%s", dev);
	FILE *in = fopen(str, "r");
	if (in != NULL) {
		while (1) {
			char lineStr[80];
			fgets(lineStr, sizeof lineStr, in);
			if (feof(in))
				break;
			sscanf(lineStr, "error=%s", error);
		}
		fclose(in);
	}

	sprintf(str, "/tmp/cdrecord.last.%s", dev);
	in = fopen(str, "r");
	if (in == NULL)
		strcpy(lastMode, "start");
	else {
		while (1) {
			char lineStr[80];
			fgets(lineStr, sizeof lineStr, in);
			if (feof(in))
				break;
			sscanf(lineStr, "%s %d", lastMode, &lastVal);
		}
		fclose(in);
	}
}

int main(int argc, char *args[]) {
	int atapi = 0, gracetime = 9, speed = 1, tsize = 0;
	int dummy = 0, multi = 0, useStdin = 0, waiti = 0;
	int bus, id, lun;
	char dev[80] = "";
	enum { eject, files, help, inq, load, msinfo,
		options, prcap, scanbus, toc, version }
		mode = help;
	for (int i = 1; i < argc; i++)
		if (strcmp(args[i], "-dummy") == 0)
			dummy = 1;
		else
		if (sscanf(args[i], "dev=%d,%d,%d", &bus, &id, &lun) == 3)
			sprintf(dev, "%d,%d,%d", bus, id, lun);
		else
		if (strcmp(args[i], "dev=ATAPI") == 0) {
			atapi = 1;
			Identifikation = IdentifikationATAPI;
		}
		else
		if (sscanf(args[i], "dev=ATAPI:%d,%d,%d", &bus, &id, &lun) == 3) {
			atapi = 1;
			Identifikation = IdentifikationATAPI;
			sprintf(dev, "ATAPI:%d,%d,%d", bus, id, lun);
		}
		else
		if (strcmp(args[i], "-eject") == 0)
			mode = eject;
		else
		if (sscanf(args[i], "gracetime=%d", &gracetime) == 1)
			;
		else
		if (strcmp(args[i], "-help") == 0)
			mode = options;
		else
		if (strcmp(args[i], "-inq") == 0)
			mode = inq;
		else
		if (strcmp(args[i], "-load") == 0)
			mode = load;
		else
		if (strcmp(args[i], "-msinfo") == 0)
			mode = msinfo;
		else
		if (strcmp(args[i], "-multi") == 0)
			multi = 1;
		else
		if (strcmp(args[i], "-prcap") == 0)
			mode = prcap;
		else
		if (strcmp(args[i], "-scanbus") == 0)
			mode = scanbus;
		else
		if (sscanf(args[i], "speed=%d", &speed) == 1)
			;
		else
		if (strcmp(args[i], "-toc") == 0)
			mode = toc;
		else
		if (sscanf(args[i], "tsize=%ds", &tsize) == 1)
			tsize *= 2048;
		else
		if (strcmp(args[i], "-version") == 0)
			mode = version;
		else
		if (strcmp(args[i], "-waiti") == 0)
			waiti = 1;
		else
		if (strcmp(args[i], "-") == 0) {
			useStdin = 1;
			mode = files;
		}
		else
		if (args[i][0] != '-')
			mode = files;

	if (strlen(dev) > 0)
		readLastMode(dev);

	if (mode != options
		&& mode != scanbus
		&& mode != version
		&& strlen(dev) == 0)
		mode = help;

	switch (mode) {
		case eject:
		printf("ok\n");
		log(dev, "eject");
		return 0;

		case help:
		if (strlen(dev) == 0)
			printf("%s: No CD/DVD-Recorder device specified.\n",
				args[0]);
		printf("%s: Usage: %s [options] track1...trackn\n",
			args[0], args[0]);
		log(dev, "help");
		return 0;

		case load:
		printf("ok\n");
		log(dev, "load");
		return 0;

		case options:
		printf("Options:\n");
		printf("\tgracetime=#\tset the grace time\n");
		return 0;

		case prcap:
		printf("  Does read CD-R media\n");
		if (strstr(Identifikation[id], "CD-ROM") == NULL) {
			printf("  Does write CD-R media\n");
			printf("  Does write CD-RW media\n");
		}
		if (strstr(Identifikation[id], "DVD") != NULL)
			printf("  Does read DVD-ROM media\n");
		if (strstr(Identifikation[id], "DVD-") != NULL)
			printf("  Does write DVD-R media\n");
		if (strstr(Identifikation[id], "DVD+") != NULL)
			printf("  Does write DVD+R media\n");
		log(dev, "prcap");
		return 0;

		case msinfo:
		printf("blank\n");
		log(dev, "msinfo");
		return 0;

		case scanbus:
		for (int i = 0; i < 8; i++)
			if (strlen(Identifikation[i]) == 0)
				printf("   0,%d,0   %d) *\n", i, i);
			else
				printf("   0,%d,0   %d) 'webCDwriter' '%-16s'"
					" '2.60' Removable CD-ROM\n",
					i, i, Identifikation[i]);
		log(dev, "scanbus");
		return 0;

		case toc:
		printf("TOC\n");
		log(dev, "toc");
		return 0;

		case inq:
		if (strcmp(lastMode, "inq") != 0
			|| lastVal < 2)
			printError(args[0], 0x00, 0x2, 0x3A, 0x00,
				"medium not present");
		else {
			printf("Executing 'test unit ready' command\n");
			printf("CDB:  00 00 00 00 00 00\n");
			printf("cmd finished after 0.001s timeout 40s\n");
		}
		printf("Vendor_info    : 'webCDwriter'\n");
		printf("Identifikation : '%-16s'\n", Identifikation[id]);
		printf("Revision       : '2.60'\n");
		log(dev, "inq");
		return 0;

		case version:
		printf("%s %s\n", product, ::version);
		log(dev, "version");
		return 0;
	}

	log(dev, "write");
	printf("%s %s\n", product, ::version);
	if (strcmp(error, "reloadMedia") == 0)
		printf("%s: Drive needs to reload the media"
			" to return to proper status.\n", args[0]);
	else
	if (strcmp(error, "servoFailure") == 0) {
		printError(args[0], 0x5B, 0x4, 0x09, 0x01,
			"tracking servo failure");
		return 1;
	}
	else
	if (strcmp(error, "powerCalibration") == 0) {
		printError(args[0], 0x5B, 0x3, 0x73, 0x03,
			"power calibration area error");
		return 1;
	}

	// normal writing
	printf("TOC Type: 1 = CD-ROM\n");

	int bytesReadFromStdin = 0;
	if (useStdin && waiti) {
		printf("Waiting for data on stdin...\n");
		char buf[16 * 1024];
		int n = read(0, buf, sizeof buf);
		bytesReadFromStdin += n;
	}

	// calculate session size
	int trackNo = 0, sessionSize = 0;
	const char *trackType = "data";
	for (int i = 1; i < argc; i++) {
		struct stat statBuf;
		if (strcmp(args[i], "-audio") == 0)
			trackType = "audio";
		else
		if (strcmp(args[i], "-data") == 0)
			trackType = "data";
		else
		if (stat(args[i], &statBuf) == 0) {
			printf("Track %02d: %-5s %3d MB\n",
				++trackNo,
				trackType,
				statBuf.st_size / (1024 * 1024));
			sessionSize += statBuf.st_size;
		}
		else
		if (strcmp(args[i], "-") == 0) {
			printf("Track %02d: %-5s %3d MB\n",
				++trackNo,
				trackType,
				tsize / (1024 * 1024));
			sessionSize += tsize;
		}
	}
	int sessionSizeMB = sessionSize / (1024 * 1024);

	printf("Current Secsize: 2048\n");
	printf("ATIP info from disk:\n");
	printf("  Is not erasable\n");
	printf("Manuf. index: 24\n");
	int blocksTotal, blocksCurrent, blocksRemaining;
	blocksTotal = 335100;
	blocksCurrent = blocksTotal;
	blocksRemaining = blocksCurrent - sessionSize / 2048;
	printf("Blocks total: %d Blocks current: %d Blocks remaining: %d\n",
		blocksTotal, blocksCurrent, blocksRemaining);
	printf("Starting to write CD/DVD at speed %d in ", speed);
	if (dummy)
		printf("dummy");
	else
		printf("write");
	printf(" mode for ");
	if (multi)
		printf("multi");
	else
		printf("single");
	printf(" session.\n");

	for (int i = gracetime; i > 0; i--) {
		const char *mode = "real";
		if (dummy)
			mode = "dummy";
		printf("\rLast chance to quit, "
			"starting %s write in %d seconds.", mode, i);
		fflush(stdout);
		sleep(1);
	}
	printf("\n");

	printf("Starting new track at sector: 0\n");
	for (int i = 0; i <= sessionSizeMB; i++) {
		printf("\rTrack 01: %3d of %3d MB written (fifo 100%).",
			i, sessionSizeMB);
		fflush(stdout);
		if (useStdin)
			while (bytesReadFromStdin / (1024 * 1024) <= i + 1
				&& bytesReadFromStdin < tsize) {
				char buf[32 * 1024];
				int n = read(0, buf, sizeof buf);
				if (n == 0)
					break;
				bytesReadFromStdin += n;
			}
		usleep(1024 * 1000 * 1000 / (150 * speed));
	}
	printf("\n");
	printf("Writing  time: %8.3fs\n",
		1000 * sessionSizeMB / (150.0 * speed));
	printf("Fixating...\n");
	sleep(2);
	printf("Fixating time: %8.3fs\n", 2.0);
}
