// Copyright (c) 2003 David Muse
// See the COPYING file for more information

#include <rudiments/timezonefile.h>
#include <rudiments/file.h>
#include <rudiments/filedescriptor.h>
#include <rudiments/charstring.h>

#include <stdio.h>
#include <stdlib.h>

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class timezonefileprivate {
	friend class timezonefile;
	private:
		uint64_t	_ttisgmtcnt;
		uint64_t	_ttisstdcnt;
		uint64_t	_leapcnt;
		uint64_t	_timecnt;
		uint64_t	_typecnt;
		uint64_t	_charcnt;
		uint64_t	*_transitiontime;
		unsigned char	*_localtime;
		ttinfo		*_ti;
		uint64_t	*_leapsecondtime;
		uint64_t	*_totalleapseconds;
		unsigned char	*_transstdwall;
		unsigned char	*_transutclocal;
		unsigned char	*_rawtimezonestring;
		unsigned char	**_timezonestrings;
};

timezonefile::timezonefile() {
	pvt=new timezonefileprivate;
	pvt->_ttisgmtcnt=0;
	pvt->_ttisstdcnt=0;
	pvt->_leapcnt=0;
	pvt->_timecnt=0;
	pvt->_typecnt=0;
	pvt->_charcnt=0;
	pvt->_transitiontime=0;
	pvt->_localtime=NULL;
	pvt->_ti=NULL;
	pvt->_leapsecondtime=NULL;
	pvt->_totalleapseconds=NULL;
	pvt->_transstdwall=NULL;
	pvt->_transutclocal=NULL;
	pvt->_rawtimezonestring=NULL;
	pvt->_timezonestrings=NULL;
}

timezonefile::timezonefile(const timezonefile &t) {
	timezonefileClone(t);
}

timezonefile &timezonefile::operator=(const timezonefile &t) {
	if (this!=&t) {
		timezonefileClean();
		timezonefileClone(t);
	}
	return *this;
}

void timezonefile::timezonefileClean() {
	delete[] pvt->_timezonestrings;
	delete[] pvt->_rawtimezonestring;
	delete[] pvt->_transutclocal;
	delete[] pvt->_transstdwall;
	delete[] pvt->_leapsecondtime;
	delete[] pvt->_totalleapseconds;
	delete[] pvt->_transitiontime;
	delete[] pvt->_localtime;
	delete[] pvt->_ti;
	delete pvt;
}

void timezonefile::timezonefileClone(const timezonefile &t) {
	pvt=new timezonefileprivate;
	pvt->_ttisgmtcnt=t.pvt->_ttisgmtcnt;
	pvt->_ttisstdcnt=t.pvt->_ttisstdcnt;
	pvt->_leapcnt=t.pvt->_leapcnt;
	pvt->_timecnt=t.pvt->_timecnt;
	pvt->_typecnt=t.pvt->_typecnt;
	pvt->_charcnt=t.pvt->_charcnt;
	pvt->_transitiontime=new uint64_t[pvt->_timecnt];
	pvt->_localtime=new unsigned char[pvt->_timecnt];
	for (uint64_t i=0; i<pvt->_timecnt; i++) {
		pvt->_transitiontime[i]=t.pvt->_transitiontime[i];
		pvt->_localtime[i]=t.pvt->_localtime[i];
	}
	pvt->_ti=new ttinfo[pvt->_typecnt];
	for (uint64_t i=0; i<pvt->_timecnt; i++) {
		pvt->_ti[i]=t.pvt->_ti[i];
	}
	pvt->_leapsecondtime=new uint64_t[pvt->_leapcnt];
	pvt->_totalleapseconds=new uint64_t[pvt->_leapcnt];
	for (uint64_t i=0; i<pvt->_leapcnt; i++) {
		pvt->_leapsecondtime[i]=t.pvt->_leapsecondtime[i];
		pvt->_totalleapseconds[i]=t.pvt->_totalleapseconds[i];
	}
	pvt->_transstdwall=new unsigned char[pvt->_ttisstdcnt];
	for (uint64_t i=0; i<pvt->_ttisstdcnt; i++) {
		pvt->_transstdwall[i]=t.pvt->_transstdwall[i];
	}
	pvt->_transutclocal=new unsigned char[pvt->_ttisgmtcnt];
	for (uint64_t i=0; i<pvt->_ttisgmtcnt; i++) {
		pvt->_transutclocal[i]=t.pvt->_transutclocal[i];
	}
	pvt->_rawtimezonestring=new unsigned char[pvt->_charcnt+1];
	for (uint64_t i=0; i<pvt->_charcnt+1; i++) {
		pvt->_rawtimezonestring[i]=t.pvt->_rawtimezonestring[i];
	}
	pvt->_timezonestrings=new unsigned char *[pvt->_typecnt];
	for (uint64_t i=0; i<pvt->_typecnt; i++) {
		pvt->_timezonestrings[i]=
			&pvt->_rawtimezonestring[pvt->_ti[i].tt_abbrind];
	}
}

timezonefile::~timezonefile() {
	timezonefileClean();
}

bool timezonefile::parseFile(const char *filename) {

	file	tzfile;
	if (!tzfile.open(filename,O_RDONLY)) {
		return false;
	}

	char	magic[5];
	if (tzfile.read(magic,4*sizeof(char))!=4*sizeof(char)) {
		printf("read magic failed\n");
		return false;
	}
	magic[4]='\0';
	if (charstring::compare(magic,"TZif")) {
		printf("magic not TZif\n");
		return false;
	}

	unsigned char	future[16];
	if (tzfile.read(future,16)!=16) {
		printf("read future failed\n");
		return false;
	}

	unsigned long	tmp;	
	if (tzfile.read(&tmp,sizeof(unsigned long))!=sizeof(unsigned long)) {
		printf("read ttisgmtcnt failed\n");
		return false;
	}
	pvt->_ttisgmtcnt=tzfile.netToHost((uint64_t)tmp);

	if (tzfile.read(&tmp,sizeof(unsigned long))!=sizeof(unsigned long)) {
		printf("read ttisstdcnt failed\n");
		return false;
	}
	pvt->_ttisstdcnt=tzfile.netToHost((uint64_t)tmp);

	if (tzfile.read(&tmp,sizeof(unsigned long))!=sizeof(unsigned long)) {
		printf("read leapcnt failed\n");
		return false;
	}
	pvt->_leapcnt=tzfile.netToHost((uint64_t)tmp);

	if (tzfile.read(&tmp,sizeof(unsigned long))!=sizeof(unsigned long)) {
		printf("read timecnt failed\n");
		return false;
	}
	pvt->_timecnt=tzfile.netToHost((uint64_t)tmp);

	if (tzfile.read(&tmp,sizeof(unsigned long))!=sizeof(unsigned long)) {
		printf("read typecnt failed\n");
		return false;
	}
	pvt->_typecnt=tzfile.netToHost((uint64_t)tmp);

	if (tzfile.read(&tmp,sizeof(unsigned long))!=sizeof(unsigned long)) {
		printf("read charcnt failed\n");
		return false;
	}
	pvt->_charcnt=tzfile.netToHost((uint64_t)tmp);

	uint64_t	i;
	pvt->_transitiontime=new uint64_t[pvt->_timecnt];
	for (i=0; i<pvt->_timecnt; i++) {
		if (tzfile.read(&tmp,
				sizeof(unsigned long))!=sizeof(unsigned long)) {
			printf("read transitiontime[%ld] failed\n",i);
			return false;
		}
		pvt->_transitiontime[i]=tzfile.netToHost((uint64_t)tmp);
	}

	pvt->_localtime=new unsigned char[pvt->_timecnt];
	for (i=0; i<pvt->_timecnt; i++) {
		if (tzfile.read(&pvt->_localtime[i],sizeof(unsigned char))!=
						sizeof(unsigned char)) {
			printf("read localtime[%ld] failed\n",i);
			return false;
		}
	}

	pvt->_ti=new ttinfo[pvt->_typecnt];
	for (i=0; i<pvt->_typecnt; i++) {
		long	signedtmp;
		if (tzfile.read(&signedtmp,sizeof(long))!=sizeof(long)) {
			printf("read ttinfo.tt_gmtoff[%ld] failed\n",i);
			return false;
		}
		pvt->_ti[i].tt_gmtoff=(int64_t)tzfile.netToHost(
						(uint64_t)signedtmp);
		if (tzfile.read(&pvt->_ti[i].tt_isdst,
					sizeof(char))!=sizeof(char)) {
			printf("read ttinfo.tt_isdst[%ld] failed\n",i);
			return false;
		}
		if (tzfile.read(&pvt->_ti[i].tt_abbrind,
				sizeof(unsigned char))!=sizeof(unsigned char)) {
			printf("read ttinfo.tt_abbrind[%ld] failed\n",i);
			return false;
		}
	}

	pvt->_rawtimezonestring=new unsigned char[pvt->_charcnt+1];
	if ((uint64_t)tzfile.read(pvt->_rawtimezonestring,pvt->_charcnt)!=
								pvt->_charcnt) {
		printf("read rawtimezonestring failed\n");
		return false;
	}
	pvt->_rawtimezonestring[pvt->_charcnt]='\0';

	// set pointers to the timezones
	pvt->_timezonestrings=new unsigned char *[pvt->_typecnt];
	uint64_t	counter;
	for (counter=0; counter<pvt->_typecnt; counter++) {
		pvt->_timezonestrings[counter]=
			&pvt->_rawtimezonestring[pvt->_ti[counter].tt_abbrind];
	}

	pvt->_leapsecondtime=new uint64_t[pvt->_leapcnt];
	pvt->_totalleapseconds=new uint64_t[pvt->_leapcnt];
	for (i=0; i<pvt->_leapcnt; i++) {
		if (tzfile.read(&tmp,sizeof(uint64_t))!=sizeof(uint64_t)) {
			printf("read leapsecondtime[%ld] failed\n",i);
			return false;
		}
		pvt->_leapsecondtime[i]=tzfile.netToHost((uint64_t)tmp);
		if (tzfile.read(&pvt->_totalleapseconds[i],
				sizeof(uint64_t))!=sizeof(uint64_t)) {
			printf("read totalleapseconds[%ld] failed\n",i);
			return false;
		}
		pvt->_totalleapseconds[i]=tzfile.netToHost((uint64_t)tmp);
	}

	pvt->_transstdwall=new unsigned char[pvt->_ttisstdcnt];
	for (i=0; i<pvt->_ttisstdcnt; i++) {
		if (tzfile.read(&pvt->_transstdwall[i],
				sizeof(unsigned char))!=sizeof(unsigned char)) {
			printf("read transstdwall[%ld] failed\n",i);
			return false;
		}
	}

	pvt->_transutclocal=new unsigned char[pvt->_ttisgmtcnt];
	for (i=0; i<pvt->_ttisgmtcnt; i++) {
		if (tzfile.read(&pvt->_transutclocal[i],
				sizeof(unsigned char))!=sizeof(unsigned char)) {
			printf("read transutclocal[%ld] failed\n",i);
			return false;
		}
	}

	tzfile.close();

	return true;
}


uint64_t timezonefile::getIsGMTCount() {
	return pvt->_ttisgmtcnt;
}

uint64_t timezonefile::getIsSTDCount() {
	return pvt->_ttisstdcnt;
}

uint64_t timezonefile::getLeapCount() {
	return pvt->_leapcnt;
}

uint64_t timezonefile::getTimeCount() {
	return pvt->_timecnt;
}

uint64_t timezonefile::getTypeCount() {
	return pvt->_typecnt;
}

uint64_t timezonefile::getCharacterCount() {
	return pvt->_charcnt;
}

uint64_t *timezonefile::getTransitionTimes() {
	return pvt->_transitiontime;
}

uint64_t timezonefile::getTransitionTime(int index) {
	return pvt->_transitiontime[index];
}

unsigned char *timezonefile::getLocalTimes() {
	return pvt->_localtime;
}

unsigned char timezonefile::getLocalTimes(int index) {
	return pvt->_localtime[index];
}

ttinfo **timezonefile::getTimeTypeInfos() {
	return &pvt->_ti;
}

ttinfo *timezonefile::getTimeTypeInfo(int index) {
	return &pvt->_ti[index];
}

uint64_t *timezonefile::getLeapSecondTimes() {
	return pvt->_leapsecondtime;
}

uint64_t timezonefile::getLeapSecondTime(int index) {
	return pvt->_leapsecondtime[index];
}

uint64_t *timezonefile::getTotalLeapSeconds() {
	return pvt->_totalleapseconds;
}

uint64_t timezonefile::getTotalLeapSeconds(int index) {
	return pvt->_totalleapseconds[index];
}

unsigned char *timezonefile::getStandardOrWallIndicators() {
	return pvt->_transstdwall;
}

unsigned char timezonefile::getStandardOrWallIndicator(int index) {
	return pvt->_transstdwall[index];
}

unsigned char *timezonefile::getUTCOrLocalIndicators() {
	return pvt->_transutclocal;
}

unsigned char timezonefile::getUTCOrLocalIndicator(int index) {
	return pvt->_transutclocal[index];
}

unsigned char *timezonefile::getRawTimeZoneString() {
	return pvt->_rawtimezonestring;
}

unsigned char **timezonefile::getTimeZoneStrings() {
	return pvt->_timezonestrings;
}

unsigned char *timezonefile::getTimeZoneString(int index) {
	return pvt->_timezonestrings[index];
}

void timezonefile::print() {
	printf("ttisgmtcnt: %ld\n",pvt->_ttisgmtcnt);
	printf("ttisstdcnt: %ld\n",pvt->_ttisstdcnt);
	printf("leapcnt: %ld\n",pvt->_leapcnt);
	printf("timecnt: %ld\n",pvt->_timecnt);
	printf("typecnt: %ld\n",pvt->_typecnt);
	printf("charcnt: %ld\n",pvt->_charcnt);
	uint64_t	i;
	for (i=0; i<pvt->_timecnt; i++) {
		printf("transitiontime[%ld]: %ld\n",i,pvt->_transitiontime[i]);
	}
	for (i=0; i<pvt->_timecnt; i++) {
		printf("localtime[%ld]: %d\n",i,pvt->_localtime[i]);
	}
	for (i=0; i<pvt->_typecnt; i++) {
		printf("ttinfo[%ld] {\n",i);
		printf("	tt_gmtoff: %ld\n",pvt->_ti[i].tt_gmtoff);
		printf("	tt_isdst: %d\n",pvt->_ti[i].tt_isdst);
		printf("	tt_abbrind: %d\n",pvt->_ti[i].tt_abbrind);
		printf("}\n");
	}
	printf("rawtimezonestring: ");
	for (uint64_t i=0; i<pvt->_charcnt; i++) {
		if (pvt->_rawtimezonestring[i]=='\0') {
			printf(" ");
		}
		printf("%c",pvt->_rawtimezonestring[i]);
	}
	printf("\n");
	for (i=0; i<pvt->_leapcnt; i++) {
		printf("leapsecondtime[%ld]: %ld\n",i,
					pvt->_leapsecondtime[i]);
		printf("totalleapseconds[%ld]: %ld\n",i,
					pvt->_totalleapseconds[i]);
	}
	for (uint64_t counter=0; counter<pvt->_typecnt; counter++) {
		printf("timezonestrings[%ld]=%s\n",counter,
					pvt->_timezonestrings[counter]);
	}
	for (i=0; i<pvt->_ttisstdcnt; i++) {
		printf("transstdwall[%ld]: %d\n",i,pvt->_transstdwall[i]);
	}
	for (i=0; i<pvt->_ttisgmtcnt; i++) {
		printf("transutclocal[%ld]: %d\n",i,pvt->_transutclocal[i]);
	}
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1