/* KPilot
**
** Copyright (C) 1998-2001 by Dan Pilone
** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
**
** This is a C++ wrapper for the Pilot's datebook structures.
*/

/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation; either version 2.1 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
** MA 02110-1301, USA.
*/

/*
** Bug reports and questions can be sent to kde-pim@kde.org
*/

#include "options.h"

#include <stdlib.h>

#include <tqdatetime.h>
#include <tqnamespace.h>
#include <tqregexp.h>

#include <tdeglobal.h>

#include "pilotDateEntry.h"

static const char *default_date_category_names[] = {
	"Unfiled",
	"Business",
	"Personal",
	0L
} ;

void PilotDateInfo::resetToDefault()
{
	FUNCTIONSETUP;
	// Reset to all 0s
	memset(&fInfo,0,sizeof(fInfo));
	// Fill up default categories
	for (unsigned int i=0; (i<4) && default_date_category_names[i]; ++i)
	{
		strncpy(fInfo.category.name[i],default_date_category_names[i],sizeof(fInfo.category.name[0]));
	}

	fInfo.startOfWeek = 0;

}


PilotDateEntry::PilotDateEntry():PilotRecordBase()
{
	::memset(&fAppointmentInfo, 0, sizeof(struct Appointment));
}

/* initialize the entry from another one. If rec==NULL, this constructor does the same as PilotDateEntry()
*/
PilotDateEntry::PilotDateEntry(PilotRecord * rec) :
	PilotRecordBase(rec)
{
	::memset(&fAppointmentInfo, 0, sizeof(fAppointmentInfo));
	if (rec)
	{
		// Construct a fake pi_buffer for unpack_Appointment.
		// No ownership changes occur here.
		pi_buffer_t b = { (unsigned char*)rec->data(), (size_t)rec->size(), (size_t)rec->size() } ;
		unpack_Appointment(&fAppointmentInfo, &b, datebook_v1);
	}
	return;

}

void PilotDateEntry::_copyExceptions(const PilotDateEntry & e)
{
	if (e.fAppointmentInfo.exceptions > 0)
	{
		size_t blocksize = e.fAppointmentInfo.exceptions *
			sizeof(struct tm);

		fAppointmentInfo.exception = (struct tm *)::malloc(blocksize);

		if (fAppointmentInfo.exception)
		{
			fAppointmentInfo.exceptions =
				e.fAppointmentInfo.exceptions;
			::memcpy(fAppointmentInfo.exception,
				e.fAppointmentInfo.exception, blocksize);
		}
		else
		{
			WARNINGKPILOT << "malloc() failed, exceptions not copied" << endl;
			fAppointmentInfo.exceptions = 0;
		}
	}
	else
	{
		fAppointmentInfo.exceptions = 0;
		fAppointmentInfo.exception = 0L;
	}
}


PilotDateEntry::PilotDateEntry(const PilotDateEntry & e) :
	PilotRecordBase(e)
{
	::memcpy(&fAppointmentInfo, &e.fAppointmentInfo,
		sizeof(struct Appointment));
	// See operator = for explanation
	fAppointmentInfo.exception = 0L;
	fAppointmentInfo.description = 0L;
	fAppointmentInfo.note = 0L;

	_copyExceptions(e);
	setDescriptionP(e.fAppointmentInfo.description);
	setNoteP(e.fAppointmentInfo.note);
}


PilotDateEntry & PilotDateEntry::operator = (const PilotDateEntry & e)
{
	if (this != &e)		// Pointer equality!
	{
		KPILOT_FREE(fAppointmentInfo.exception);
		KPILOT_FREE(fAppointmentInfo.description);
		KPILOT_FREE(fAppointmentInfo.note);
		::memcpy(&fAppointmentInfo, &e.fAppointmentInfo,
			sizeof(fAppointmentInfo));

		// The original pointers were already freed; since we're now
		// got the pointers from the new structure and we're going
		// to use the standard set functions make sure that
		// we don't free() the copies-of-pointers from e, which
		// would be disastrous.
		//
		//
		fAppointmentInfo.exception = 0L;
		fAppointmentInfo.description = 0L;
		fAppointmentInfo.note = 0L;

		_copyExceptions(e);
		setDescriptionP(e.fAppointmentInfo.description);
		setNoteP(e.fAppointmentInfo.note);
	}

	return *this;
}				// end of assignment operator


TQString PilotDateEntry::getTextRepresentation(TQt::TextFormat richText)
{
	TQString text, tmp;
	TQString par = (richText==TQt::RichText) ?CSL1("<p>"):TQString();
	TQString ps = (richText==TQt::RichText) ?CSL1("</p>"):CSL1("\n");
	TQString br = (richText==TQt::RichText) ?CSL1("<br/>"):CSL1("\n");

	// title + name
	text += par;
	tmp=richText?CSL1("<b><big>%1</big></b>"):CSL1("%1");
	text += tmp.arg(rtExpand(getDescription(), richText));
	text += ps;

	TQDateTime dt(readTm(getEventStart()));
	TQString startDate(dt.toString(TQt::LocalDate));
	text+=par;
	text+=i18n("Start date: %1").arg(startDate);
	text+=ps;

	if (isEvent())
	{
		text+=par;
		text+=i18n("Whole-day event");
		text+=ps;
	}
	else
	{
		dt=readTm(getEventEnd());
		TQString endDate(dt.toString(TQt::LocalDate));
		text+=par;
		text+=i18n("End date: %1").arg(endDate);
		text+=ps;
	}

	if ( isAlarmEnabled() )
	{
		text+=par;
		tmp=i18n("%1 is the duration, %2 is the time unit", "Alarm: %1 %2 before event starts").
			arg(getAdvance());
		switch (getAdvanceUnits())
		{
			case advMinutes: tmp=tmp.arg(i18n("minutes")); break;
			case advHours: tmp=tmp.arg(i18n("hours")); break;
			case advDays: tmp=tmp.arg(i18n("days")); break;
			default: tmp=tmp.arg(TQString()); break;;
		}
		text+=tmp;
		text+=ps;
	}

	if (getRepeatType() != repeatNone)
	{
		text+=par;
		tmp=i18n("Recurrence: every %1 %2");
		int freq = getRepeatFrequency();
		tmp=tmp.arg(freq);

		switch(getRepeatType())
		{
			case repeatDaily: tmp=tmp.arg(i18n("day(s)")); break;
			case repeatWeekly: tmp=tmp.arg(i18n("week(s)")); break;
			case repeatMonthlyByDay:
			case repeatMonthlyByDate: tmp=tmp.arg(i18n("month(s)")); break;
			case repeatYearly: tmp=tmp.arg(i18n("year(s)")); break;
			default: tmp=tmp.arg(TQString()); break;
		}
		text+=tmp;
		text+=br;

		bool repeatsForever = getRepeatForever();
		if (repeatsForever)
		{
			text+=i18n("Repeats indefinitely");
		}
		else
		{
			dt = readTm(getRepeatEnd()).date();
			text+=i18n("Until %1").arg(dt.toString(TQt::LocalDate));
		}
		text+=br;

		if (getRepeatType()==repeatMonthlyByDay) text+=i18n("Repeating on the i-th day of week j")+br;
		if (getRepeatType()==repeatMonthlyByDate) text+=i18n("Repeating on the n-th day of the month")+br;
		// TODO: show the dayArray when repeating weekly
		/*TQBitArray dayArray(7);
		if (getRepeatType()==repeatWeekly) text+=i18n("Repeat day flags: %1").arg(getRepeatDays
		const int *days = dateEntry->getRepeatDays();
		// Rotate the days of the week, since day numbers on the Pilot and
		// in vCal / Events are different.
		if (days[0]) dayArray.setBit(6);
		for (int i = 1; i < 7; i++)
		{
			if (days[i]) dayArray.setBit(i-1);
		}*/
		text+=ps;
	}

	if (getExceptionCount()>0 )
	{
		text+=par;
		text+=i18n("Exceptions:")+br;
		for (int i = 0; i < getExceptionCount(); i++)
		{
			TQDate exdt=readTm(getExceptions()[i]).date();
			text+=exdt.toString(TQt::LocalDate);
			text+=br;
		}
		text+=ps;
	}

	if (!getNote().isEmpty())
	{
		text += richText?CSL1("<hr/>"):CSL1("-------------------------\n");
		text+=par;
		text+=richText?i18n("<b><em>Note:</em></b><br>"):i18n("Note:\n");
		text+=rtExpand(getNote(), richText);
		text+=ps;
	}

	return text;
}

TQDateTime PilotDateEntry::dtStart() const
{
	FUNCTIONSETUP;
	return readTm( getEventStart() );
}

TQDateTime PilotDateEntry::dtEnd() const
{
	FUNCTIONSETUP;
	return readTm( getEventEnd() );
}

TQDateTime PilotDateEntry::dtRepeatEnd() const
{
	FUNCTIONSETUP;
	return readTm( getRepeatEnd() );
}

unsigned int PilotDateEntry::alarmLeadTime() const
{
	FUNCTIONSETUP;
	if (!isAlarmEnabled()) return 0;

	int adv = getAdvance();
	if ( adv < 0 )
	{
		return 0; // Not possible to enter on the pilot
	}
	unsigned int t = adv;
	int u = getAdvanceUnits();


	switch(u)
	{
	case advMinutes : t *= 60; break;
	case advHours : t *= 3600; break;
	case advDays : t *= 3600 * 24; break;
	default: t = 0;
	}

	return t;
}

PilotRecord *PilotDateEntry::pack() const
{
	int i;

	pi_buffer_t *b = pi_buffer_new( sizeof(fAppointmentInfo) );
	i = pack_Appointment(const_cast<Appointment_t *>(&fAppointmentInfo), b, datebook_v1);
	if (i<0)
	{
		// Generic error from the pack_*() functions.
		return 0;
	}

	// pack_Appointment sets b->used
	return new PilotRecord( b, this );
}

/* setExceptions sets a new set of exceptions. Note that
	PilotDateEntry assumes ownership of the array and will
	delete the old one. */
void PilotDateEntry::setExceptions(struct tm *e) {
	if (fAppointmentInfo.exception != e)
	{
		KPILOT_FREE(fAppointmentInfo.exception);
	}
	fAppointmentInfo.exception=e;
}


void PilotDateEntry::setDescriptionP(const char *desc, int l)
{
	FUNCTIONSETUP;
	KPILOT_FREE(fAppointmentInfo.description);

	if (desc && *desc)
	{
		if (-1 == l) l=::strlen(desc);
		fAppointmentInfo.description =
			(char *) ::malloc(l + 1);
		if (fAppointmentInfo.description)
		{
			strlcpy(fAppointmentInfo.description, desc, l+1);
		}
		else
		{
			WARNINGKPILOT << "malloc() failed, description not set" << endl;
		}
	}
	else
	{
		fAppointmentInfo.description = 0L;
	}
}

void PilotDateEntry::setNoteP(const char *note, int l)
{
	FUNCTIONSETUP;
	KPILOT_FREE(fAppointmentInfo.note);

	if (note && *note)
	{
		if (-1 == l) l=::strlen(note);
		fAppointmentInfo.note = (char *)::malloc(l + 1);
		if (fAppointmentInfo.note)
		{
			strlcpy(fAppointmentInfo.note, note,l+1);
		}
		else
		{
			WARNINGKPILOT << "malloc() failed, note not set" << endl;
		}
	}
	else
	{
		fAppointmentInfo.note = 0L;
	}
}

void PilotDateEntry::setNote(const TQString &s)
{
	TQCString t = Pilot::toPilot(s);
	setNoteP( t.data(),t.length() );
}

void PilotDateEntry::setLocation(const TQString &s)
{
	TQString note = Pilot::fromPilot(getNoteP());
	TQRegExp rxp = TQRegExp("^[Ll]ocation:[^\n]+\n");

	// per TQString docs, this covers null and 0 length
	if( s.isEmpty() )
	{
		note.replace(rxp,"");
	}
	else
	{
		TQString location = "Location: " + s + "\n";
		int pos = note.find(rxp);

		if(pos >= 0)
		{
			note.replace( rxp, location );
		}
		else
		{
			note = location + note;
			setNote( note );
		}
	}
}

TQString PilotDateEntry::getLocation() const
{
	// Read the complete note here and not the filtered
	// one from PilotDateEntry::getNote();
	TQString note = Pilot::fromPilot(getNoteP());
	TQRegExp rxp = TQRegExp("^[Ll]ocation:[^\n]+\n");
	int pos = note.find(rxp, 0);

	if(pos >= 0)
	{
		TQString location = rxp.capturedTexts().first();
		rxp = TQRegExp("^[Ll]ocation:[\\s|\t]*");
		location.replace(rxp,"");
		location.replace("\n", "");
		return location;
	}
	else
	{
		return "";
	}
}

void PilotDateEntry::setDescription(const TQString &s)
{
	TQCString t = Pilot::toPilot(s);
	setDescriptionP( t.data(),t.length() );
}

TQString PilotDateEntry::getNote() const
{
	TQString note = Pilot::fromPilot(getNoteP());
	TQRegExp rxp = TQRegExp("^[Ll]ocation:[^\n]+\n");
	note.replace(rxp, "" );
	return note;
}

TQString PilotDateEntry::getDescription() const
{
	return Pilot::fromPilot(getDescriptionP());
}

