#include "stdafx.h"


#ifndef _WIN32
#error PORTME
#endif

static bool is_spacing(char c) {return c == ' ' || c==10 || c==13 || c == '\t';}

static bool is_spacing(const char * str, t_size len) {
	for(t_size walk = 0; walk < len; ++walk) if (!is_spacing(str[walk])) return false;
	return true;
}

typedef exception_io_data exception_time_error;

static unsigned ParseDateElem(const char * ptr, t_size len) {
	unsigned ret = 0;
	for(t_size walk = 0; walk < len; ++walk) {
		const char c = ptr[walk];
		if (c < '0' || c > '9') throw exception_time_error();
		ret = ret * 10 + (unsigned)(c - '0');
	}
	return ret;
}

t_filetimestamp foobar2000_io::filetimestamp_from_string(const char * date) {
	// Accepted format
	// YYYY-MM-DD HH:MM:SS
	try {
		t_size remaining = strlen(date);
		SYSTEMTIME st = {};
		st.wDay = 1; st.wMonth = 1;
		for(;;) {
#define ADVANCE(n) { PFC_ASSERT( remaining >= n); date += n; remaining -= n; }
#define ADVANCE_TEST(n) { if (remaining < n) throw exception_time_error(); }
#define PARSE(var, digits) { ADVANCE_TEST(digits); var = (WORD) ParseDateElem(date, digits); ADVANCE(digits) ; }
#define TEST_END( durationIncrement ) 
#define SKIP(c) { ADVANCE_TEST(1); if (date[0] != c) throw exception_time_error(); ADVANCE(1); }
#define SKIP_SPACING() {while(remaining > 0 && is_spacing(*date)) ADVANCE(1);}
			SKIP_SPACING();
			PARSE( st.wYear, 4 ); if (st.wYear < 1601) throw exception_time_error();
			TEST_END(wYear); SKIP('-');
			PARSE( st.wMonth, 2 ); if (st.wMonth < 1 || st.wMonth > 12) throw exception_time_error();
			TEST_END(wMonth); SKIP('-');
			PARSE( st.wDay, 2); if (st.wDay < 1 || st.wDay > 31) throw exception_time_error();
			TEST_END(wDay); SKIP(' ');
			PARSE( st.wHour, 2); if (st.wHour >= 24) throw exception_time_error();
			TEST_END(wHour); SKIP(':');
			PARSE( st.wMinute, 2); if (st.wMinute >= 60) throw exception_time_error();
			TEST_END(wMinute); SKIP(':');
			PARSE( st.wSecond, 2); if (st.wSecond >= 60) throw exception_time_error();
			SKIP_SPACING();
			TEST_END( wSecond );
#undef ADVANCE
#undef ADVANCE_TEST
#undef PARSE
#undef TEST_END
#undef SKIP
#undef SKIP_SPACING
			if (remaining > 0) throw exception_time_error();
			break;
		}
		t_filetimestamp base, out;
		if (!SystemTimeToFileTime(&st, (FILETIME*) &base)) throw exception_time_error();
		if (!LocalFileTimeToFileTime((const FILETIME*)&base, (FILETIME*)&out)) throw exception_time_error();
		return out;
	} catch(exception_time_error) {
		return filetimestamp_invalid;
	}
}

static const char g_invalidMsg[] = "<invalid timestamp>";

format_filetimestamp::format_filetimestamp(t_filetimestamp p_timestamp) {
	try {
		SYSTEMTIME st; FILETIME ft;
		if (FileTimeToLocalFileTime((FILETIME*)&p_timestamp,&ft)) {
			if (FileTimeToSystemTime(&ft,&st)) {
				m_buffer 
					<< pfc::format_uint(st.wYear,4) << "-" << pfc::format_uint(st.wMonth,2) << "-" << pfc::format_uint(st.wDay,2) << " " 
					<< pfc::format_uint(st.wHour,2) << ":" << pfc::format_uint(st.wMinute,2) << ":" << pfc::format_uint(st.wSecond,2);
				return;
			}
		}
	} catch(...) {}
	m_buffer = g_invalidMsg;
}

format_filetimestamp_utc::format_filetimestamp_utc(t_filetimestamp p_timestamp) {
	try {
		SYSTEMTIME st;
		if (FileTimeToSystemTime((const FILETIME*)&p_timestamp,&st)) {
			m_buffer 
				<< pfc::format_uint(st.wYear,4) << "-" << pfc::format_uint(st.wMonth,2) << "-" << pfc::format_uint(st.wDay,2) << " " 
				<< pfc::format_uint(st.wHour,2) << ":" << pfc::format_uint(st.wMinute,2) << ":" << pfc::format_uint(st.wSecond,2);
			return;
		} 
	} catch(...) {}
	m_buffer = g_invalidMsg;
}
