#include <stddef.h>
#include <stdio.h>
#include <time.h>
#include <string>
#include <vector>

#include "accesslog.h"
#include "client.h"
#include "log.h"
#include "timespec.h"

using namespace std;

AccessLogThread::AccessLogThread()
{
}

AccessLogThread::AccessLogThread(const string &filename)
	: filename(filename) {
}

void AccessLogThread::write(const ClientStats& client)
{
	{
		lock_guard<mutex> lock(mu);
		pending_writes.push_back(client);
	}
	wakeup();
}

void AccessLogThread::do_work()
{
	// Open the file.
	if (filename.empty()) {
		logfp = nullptr;
	} else {
		logfp = fopen(filename.c_str(), "a+e");
		if (logfp == nullptr) {
			log_perror(filename.c_str());
			// Continue as before.
		}
	}

	while (!should_stop()) {
		// Empty the queue.
		vector<ClientStats> writes;
		{
			lock_guard<mutex> lock(mu);
			swap(pending_writes, writes);
		}

		if (logfp != nullptr) {
			// Do the actual writes.
			timespec now_monotonic;
			timespec now_realtime;
			if (clock_gettime(CLOCK_MONOTONIC_COARSE, &now_monotonic) == -1) {
				log_perror("clock_gettime(CLOCK_MONOTONIC_COARSE)");
			} else if (clock_gettime(CLOCK_REALTIME, &now_realtime) == -1) {
				log_perror("clock_gettime(CLOCK_REALTIME)");
			} else {
				timespec realtime_offset = clock_diff(now_monotonic, now_realtime);
				for (size_t i = 0; i < writes.size(); ++i) {
					timespec connect_time_realtime = clock_add(writes[i].connect_time, realtime_offset);
					timespec time_since_connect = clock_diff(writes[i].connect_time, now_monotonic);
					fprintf(logfp, "%llu %s %s %d %llu %llu %llu \"%s\" \"%s\"\n",
						(long long unsigned)(connect_time_realtime.tv_sec),
						writes[i].remote_addr.c_str(),
						writes[i].url.c_str(),
						int(time_since_connect.tv_sec),
						(long long unsigned)(writes[i].bytes_sent),
						(long long unsigned)(writes[i].bytes_lost),
						(long long unsigned)(writes[i].num_loss_events),
						writes[i].referer.c_str(),
						writes[i].user_agent.c_str());
				}
				fflush(logfp);
			}
		}

		// Wait until we are being woken up, either to quit or because
		// there is material in pending_writes.
		wait_for_wakeup(nullptr);
	}

	if (logfp != nullptr) {	
		if (fclose(logfp) == EOF) {
			log_perror("fclose");
		}
	}

	logfp = nullptr;
}
