/*
 * @(#)Log.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.
 */

#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include "Config.h"
#include "Log.h"
#include "Thread.h"

Log::Log() {
	pthread_mutex_init(&mutex, NULL);
	counter = 0;
	lastLine = "";
	lastTime = 0;
	outStream = NULL;
	buf = new StringBuffer(8 * 1024, 8 * 1024);
	mark0 = mark1 = mark2 = linesCounter = 0;
	initMark = 2411;
	thread = NULL;
}

void Log::debug(const char *tag, const char *format, ...) {
	if (config.getLogLevel() < 9
			&& config.getDebug().indexOf(tag) < 0)
		return;

	char str[1024];
	va_list ap;
	va_start(ap, format);
	vsnprintf(str, sizeof str, format, ap);
	va_end(ap);

	writeLine(config.getLogLevel(), S.e + tag + " " + str);
}

void Log::debug(const char *tag, String &message) {
	if (config.getLogLevel() < 9
			&& config.getDebug().indexOf(tag) < 0) {
		delete &message;
		return;
	}

	writeLine(config.getLogLevel(), S.e + tag + " " + message);
	delete &message;
}

void Log::error(String &message) {
	put(1, message);
	pthread_mutex_lock(&mutex);
	buf.append("--> error\n");
//	mark1 = mark2 = buf.length();
//	if (linesCounter > 200)
//		linesCounter = 200;
	pthread_mutex_unlock(&mutex);
}

String *Log::getSupportLog() {
	pthread_mutex_lock(&mutex);
	String *str = new String(buf);
	buf.delete_(mark0, buf.length());
	pthread_mutex_unlock(&mutex);
	return str;
}

void Log::put(int level, const char *message) {
	put(level, S.e + message);
}

void Log::put(int level, String &message) {
	writeLine(level, message);
}

void Log::put(const char *format, ...) {
	char line[1024];
	va_list ap;
	va_start(ap, format);
	vsnprintf(line, sizeof line, format, ap);
	va_end(ap);
	writeLine(3, S.e + line);
}

void Log::putv(const char *format, ...) {
	char line[1024];
	va_list ap;
	va_start(ap, format);
	vsnprintf(line, sizeof line, format, ap);
	va_end(ap);
	writeLine(4, S.e + line);
}

void Log::restart() {
	if (outStream == NULL || outStream == stdout)
		return;

	File logFile = new File(config.getLogDir(), "log");

	pthread_mutex_lock(&mutex);
	fclose(outStream);
	outStream = fopen(logFile.getPath().getBytes(), "a");
	pthread_mutex_unlock(&mutex);
}

void Log::start(int toStdOut) {
	if (toStdOut)
		outStream = stdout;
	else {
		File logFile = new File(config.getLogDir(), "log");
		outStream = fopen(logFile.getPath().getBytes(), "a");
	}
}

void Log::writeLine(int level, String &line) {
	if (initMark != 2411)
		// object not initialized yet
		return;

	if ((!config.getSupportForm() || level > 5)
			&& level > config.getLogLevel()) {
		delete &line;
		return;
	}

	if (Thread::currentThread() == thread) {
		// probably a recursive StringBuffer call
		delete &line;
		return;
	}

	StringBuffer workBuf = new StringBuffer(120, 40);
	for (int i = 0; i < line.length(); i++)
		if (line.charAt(i) == '\n')
			workBuf.append("<br>");
		else
			workBuf.append((char)line.charAt(i));
	delete &line;
	workBuf.append("\n");
	String line2 = new String(workBuf);

	if (outStream == NULL)
		return;

	if (pthread_mutex_lock(&mutex) != 0)
		return;
	try {
	thread = Thread::currentThread();
	if (line2.equals(lastLine))
		counter++;
	else {
		if (counter > 0) {
			String str = "repeated ";
			str = str + counter + " times\n";
			if (lastLineLevel <= config.getLogLevel())
				fwrite(str.getBytes(), str.length(), 1, outStream);
			counter = 0;
		}

		time_t t;
		time(&t);
		t -= 24 * 60 * 60;
		struct tm *tm = localtime(&t);

		if (config.getLogLevel() > 1 && tm->tm_hour < lastTime
				&& outStream != NULL && outStream != stdout) {
			StringBuffer workBuf = new StringBuffer(40);
			File logFile = new File(config.getLogDir(), "log");
			workBuf.append(logFile.getPath().getBytes());
			workBuf.append("-");
			appendFormatted(workBuf, 1900 + tm->tm_year, 4, '0');
			appendFormatted(workBuf, tm->tm_mon + 1, 2, '0');
			appendFormatted(workBuf, tm->tm_mday, 2, '0');
			workBuf.append(".txt");
			String newName = new String(workBuf);

			String msg = "End of ";
			msg = msg + newName + "\n";
			fwrite(msg.getBytes(), msg.length(), 1, outStream);
			fclose(outStream);
			outStream = NULL;

			File newFile = new File(newName);
			for (char ch = 'a'; newFile.exists() && ch <= 'z'; ch++) {
				String str = newName;
				str = str + (char) ch;
				newFile = new File(str);
			}
			logFile.renameTo(newFile);
			outStream = fopen(logFile.getPath().getBytes(), "a");
		}
		lastTime = tm->tm_hour;

		StringBuffer workBuf = new StringBuffer(40);
		appendFormatted(workBuf, tm->tm_hour, 2, '0');
		appendFormatted(workBuf, tm->tm_min, 2, '0');
		appendFormatted(workBuf, tm->tm_sec, 2, '0');
		workBuf.append(" T");
		int threadID = 0;
		if (thread != NULL)
			threadID = thread->ID();
		appendFormatted(workBuf, threadID, 5, '0');
		workBuf.append(" L");
		workBuf.append(level);
		workBuf.append(" ");
		String prefix = new String(workBuf);
		prefix = prefix + line2;

		if (level <= config.getLogLevel())
			fwrite(prefix.getBytes(), prefix.length(), 1, outStream);

		if (config.getSupportForm()) {
			buf.append(prefix.getBytes());
			linesCounter++;
			if (linesCounter <= 200) {
				mark0 = mark1 = mark2 = buf.length();
				if (line2.startsWith("waiting for request..."))
					linesCounter = 200;
			}
			else
			if (linesCounter == 600)
				mark2 = buf.length();
			else
			if (linesCounter == 1000) {
				buf.delete_(mark1, mark2);
				buf.insert(mark1, "<-- cut -->\n");
				mark2 = buf.length();
				linesCounter = 600;
			}
		}
	}
	lastLine = line2;
	lastLineLevel = level;
	}
	catch (Exception *e) {
		delete e;
	}
	thread = NULL;
	pthread_mutex_unlock(&mutex);

	fflush(outStream);
}
