/*
    This file is part of libkcal.

    Copyright (c) 1998 Preston Brown <pbrown@kde.org>
    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include <tqapplication.h>
#include <tqdatetime.h>
#include <tqstring.h>
#include <tqptrlist.h>
#include <tqregexp.h>
#include <tqclipboard.h>
#include <tqdialog.h>
#include <tqfile.h>

#include <kdebug.h>
#include <tdemessagebox.h>
#include <kiconloader.h>
#include <tdelocale.h>

#include "vcc.h"
#include "vobject.h"
extern "C" {
#include <libical/icaltime.h>
#include <libical/icaltimezone.h>
}
#include "vcaldrag.h"
#include "calendar.h"

#include "vcalformat.h"

using namespace KCal;

VCalFormat::VCalFormat()
{
}

VCalFormat::~VCalFormat()
{
}

bool VCalFormat::load(Calendar *calendar, const TQString &fileName)
{
  mCalendar = calendar;

  clearException();

  kdDebug(5800) << "VCalFormat::load() " << fileName << endl;

  VObject *vcal = 0;

  // this is not necessarily only 1 vcal.  Could be many vcals, or include
  // a vcard...
  vcal = Parse_MIME_FromFileName(const_cast<char *>(TQFile::encodeName(fileName).data()));

  if (!vcal) {
    setException(new ErrorFormat(ErrorFormat::CalVersionUnknown));
    return FALSE;
  }

  // any other top-level calendar stuff should be added/initialized here

  // put all vobjects into their proper places
  populate(vcal);

  // clean up from vcal API stuff
  cleanVObjects(vcal);
  cleanStrTbl();

  return true;
}


bool VCalFormat::save(Calendar *calendar, const TQString &fileName)
{
  mCalendar = calendar;

  TQString tmpStr;
  VObject *vcal, *vo;

  kdDebug(5800) << "VCalFormat::save(): " << fileName << endl;

  vcal = newVObject(VCCalProp);

  //  addPropValue(vcal,VCLocationProp, "0.0");
  addPropValue(vcal,VCProdIdProp, productId().latin1());
  addPropValue(vcal,VCVersionProp, _VCAL_VERSION);

  // TODO STUFF
  Todo::List todoList = mCalendar->rawTodos();
  Todo::List::ConstIterator it;
  for ( it = todoList.begin(); it != todoList.end(); ++it ) {
    vo = eventToVTodo( *it );
    addVObjectProp( vcal, vo );
  }

  // EVENT STUFF
  Event::List events = mCalendar->rawEvents();
  Event::List::ConstIterator it2;
  for( it2 = events.begin(); it2 != events.end(); ++it2 ) {
    vo = eventToVEvent( *it2 );
    addVObjectProp( vcal, vo );
  }

  writeVObjectToFile(TQFile::encodeName(fileName).data() ,vcal);
  cleanVObjects(vcal);
  cleanStrTbl();

  if (TQFile::exists(fileName)) {
    kdDebug(5800) << "No error" << endl;
    return true;
  } else  {
    kdDebug(5800) << "Error" << endl;
    return false; // error
  }

  return false;
}

bool VCalFormat::fromString( Calendar *calendar, const TQString &text )
{
  // TODO: Factor out VCalFormat::fromString()
  mCalendar = calendar;

  TQCString data = text.utf8();

  if ( !data.size() ) return false;

  VObject *vcal = Parse_MIME( data.data(), data.size());
  if ( !vcal ) return false;

  VObjectIterator i;
  VObject *curvo;
  initPropIterator( &i, vcal );

  // we only take the first object. TODO: parse all incidences.
  do  {
    curvo = nextVObject( &i );
  } while ( strcmp( vObjectName( curvo ), VCEventProp ) &&
            strcmp( vObjectName( curvo ), VCTodoProp ) );

  if ( strcmp( vObjectName( curvo ), VCEventProp ) == 0 ) {
    Event *event = VEventToEvent( curvo );
    calendar->addEvent( event );
  } else {
    kdDebug(5800) << "VCalFormat::fromString(): Unknown object type." << endl;
    deleteVObject( vcal );
    return false;
  }

  deleteVObject( vcal );

  return true;
}

TQString VCalFormat::toString( Calendar *calendar )
{
  // TODO: Factor out VCalFormat::asString()
  mCalendar = calendar;

  VObject *vcal = newVObject(VCCalProp);

  addPropValue( vcal, VCProdIdProp, CalFormat::productId().latin1() );
  addPropValue( vcal, VCVersionProp, _VCAL_VERSION );

  // TODO: Use all data.
  Event::List events = calendar->events();
  Event *event = events.first();
  if ( !event ) {
    cleanVObject( vcal );
    return TQString();
  }

  VObject *vevent = eventToVEvent( event );

  addVObjectProp( vcal, vevent );

  char *buf = writeMemVObject( 0, 0, vcal );

  TQString result( buf );

  cleanVObject( vcal );

  return result;
}

VObject *VCalFormat::eventToVTodo(const Todo *anEvent)
{
  VObject *vtodo;
  TQString tmpStr;

  vtodo = newVObject(VCTodoProp);

  // due date
  if (anEvent->hasDueDate()) {
    tmpStr = qDateTimeToISO(anEvent->dtDue(),
                            !anEvent->doesFloat());
    addPropValue(vtodo, VCDueProp, tmpStr.local8Bit());
  }

  // start date
  if (anEvent->hasStartDate()) {
    tmpStr = qDateTimeToISO(anEvent->dtStart(),
                            !anEvent->doesFloat());
    addPropValue(vtodo, VCDTstartProp, tmpStr.local8Bit());
  }

  // creation date
  tmpStr = qDateTimeToISO(anEvent->created());
  addPropValue(vtodo, VCDCreatedProp, tmpStr.local8Bit());

  // unique id
  addPropValue(vtodo, VCUniqueStringProp,
               anEvent->uid().local8Bit());

  // revision
  tmpStr.sprintf("%i", anEvent->revision());
  addPropValue(vtodo, VCSequenceProp, tmpStr.local8Bit());

  // last modification date
  tmpStr = qDateTimeToISO(anEvent->lastModified());
  addPropValue(vtodo, VCLastModifiedProp, tmpStr.local8Bit());

  // organizer stuff
  // @TODO: How about the common name?
  tmpStr = "MAILTO:" + anEvent->organizer().email();
  addPropValue(vtodo, ICOrganizerProp, tmpStr.local8Bit());

  // attendees
  if ( anEvent->attendeeCount() > 0 ) {
    Attendee::List::ConstIterator it;
    Attendee *curAttendee;
    for ( it = anEvent->attendees().begin(); it != anEvent->attendees().end();
          ++it ) {
      curAttendee = *it;
      if (!curAttendee->email().isEmpty() &&
          !curAttendee->name().isEmpty())
        tmpStr = "MAILTO:" + curAttendee->name() + " <" +
                 curAttendee->email() + ">";
      else if (curAttendee->name().isEmpty())
        tmpStr = "MAILTO: " + curAttendee->email();
      else if (curAttendee->email().isEmpty())
        tmpStr = "MAILTO: " + curAttendee->name();
      else if (curAttendee->name().isEmpty() &&
               curAttendee->email().isEmpty())
        kdDebug(5800) << "warning! this Event has an attendee w/o name or email!" << endl;
      VObject *aProp = addPropValue(vtodo, VCAttendeeProp, tmpStr.local8Bit());
      addPropValue(aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE");
      addPropValue(aProp, VCStatusProp, writeStatus(curAttendee->status()));
    }
  }

  // description BL:
  if (!anEvent->description().isEmpty()) {
    VObject *d = addPropValue(vtodo, VCDescriptionProp,
                              anEvent->description().local8Bit());
    if (anEvent->description().find('\n') != -1)
      addPropValue(d, VCEncodingProp, VCQuotedPrintableProp);
  }

  // summary
  if (!anEvent->summary().isEmpty())
    addPropValue(vtodo, VCSummaryProp, anEvent->summary().local8Bit());

  // location
  if (!anEvent->location().isEmpty())
    addPropValue(vtodo, VCLocationProp, anEvent->location().local8Bit());

  // completed
  // status
  // backward compatibility, KOrganizer used to interpret only these two values
  addPropValue(vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" :
                                                             "NEEDS_ACTION");
  // completion date
  if (anEvent->hasCompletedDate()) {
    tmpStr = qDateTimeToISO(anEvent->completed());
    addPropValue(vtodo, VCCompletedProp, tmpStr.local8Bit());
  }

  // priority
  tmpStr.sprintf("%i",anEvent->priority());
  addPropValue(vtodo, VCPriorityProp, tmpStr.local8Bit());

  // related event
  if (anEvent->relatedTo()) {
    addPropValue(vtodo, VCRelatedToProp,
                 anEvent->relatedTo()->uid().local8Bit());
  }

  // categories
  TQStringList tmpStrList = anEvent->categories();
  tmpStr = "";
  TQString catStr;
  for ( TQStringList::Iterator it = tmpStrList.begin();
        it != tmpStrList.end();
        ++it ) {
    catStr = *it;
    if (catStr[0] == ' ')
      tmpStr += catStr.mid(1);
    else
      tmpStr += catStr;
    // this must be a ';' character as the vCalendar specification requires!
    // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
    // read in.
    tmpStr += ";";
  }
  if (!tmpStr.isEmpty()) {
    tmpStr.truncate(tmpStr.length()-1);
    addPropValue(vtodo, VCCategoriesProp, tmpStr.local8Bit());
  }

  // alarm stuff
  kdDebug(5800) << "vcalformat::eventToVTodo was called" << endl;
  Alarm::List::ConstIterator it;
  for ( it = anEvent->alarms().begin(); it != anEvent->alarms().end(); ++it ) {
    Alarm *alarm = *it;
    if (alarm->enabled()) {
      VObject *a = addProp(vtodo, VCDAlarmProp);
      tmpStr = qDateTimeToISO(alarm->time());
      addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
      addPropValue(a, VCRepeatCountProp, "1");
      addPropValue(a, VCDisplayStringProp, "beep!");
      if (alarm->type() == Alarm::Audio) {
        a = addProp(vtodo, VCAAlarmProp);
        addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
        addPropValue(a, VCRepeatCountProp, "1");
        addPropValue(a, VCAudioContentProp, TQFile::encodeName(alarm->audioFile()));
      }
      else if (alarm->type() == Alarm::Procedure) {
        a = addProp(vtodo, VCPAlarmProp);
        addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
        addPropValue(a, VCRepeatCountProp, "1");
        addPropValue(a, VCProcedureNameProp, TQFile::encodeName(alarm->programFile()));
      }
    }
  }

  if (anEvent->pilotId()) {
    // pilot sync stuff
    tmpStr.sprintf("%lu",anEvent->pilotId());
    addPropValue(vtodo, KPilotIdProp, tmpStr.local8Bit());
    tmpStr.sprintf("%i",anEvent->syncStatus());
    addPropValue(vtodo, KPiloStatusProp, tmpStr.local8Bit());
  }

  return vtodo;
}

VObject* VCalFormat::eventToVEvent(const Event *anEvent)
{
  VObject *vevent;
  TQString tmpStr;

  vevent = newVObject(VCEventProp);

  // start and end time
  tmpStr = qDateTimeToISO(anEvent->dtStart(),
                          !anEvent->doesFloat());
  addPropValue(vevent, VCDTstartProp, tmpStr.local8Bit());

  // events that have time associated but take up no time should
  // not have both DTSTART and DTEND.
  if (anEvent->dtStart() != anEvent->dtEnd()) {
    tmpStr = qDateTimeToISO(anEvent->dtEnd(),
                            !anEvent->doesFloat());
    addPropValue(vevent, VCDTendProp, tmpStr.local8Bit());
  }

  // creation date
  tmpStr = qDateTimeToISO(anEvent->created());
  addPropValue(vevent, VCDCreatedProp, tmpStr.local8Bit());

  // unique id
  addPropValue(vevent, VCUniqueStringProp,
               anEvent->uid().local8Bit());

  // revision
  tmpStr.sprintf("%i", anEvent->revision());
  addPropValue(vevent, VCSequenceProp, tmpStr.local8Bit());

  // last modification date
  tmpStr = qDateTimeToISO(anEvent->lastModified());
  addPropValue(vevent, VCLastModifiedProp, tmpStr.local8Bit());

  // attendee and organizer stuff
  // TODO: What to do with the common name?
  tmpStr = "MAILTO:" + anEvent->organizer().email();
  addPropValue(vevent, ICOrganizerProp, tmpStr.local8Bit());

  // TODO: Put this functionality into Attendee class
  if ( anEvent->attendeeCount() > 0 ) {
    Attendee::List::ConstIterator it;
    for ( it = anEvent->attendees().begin(); it != anEvent->attendees().end();
          ++it ) {
      Attendee *curAttendee = *it;
      if (!curAttendee->email().isEmpty() &&
          !curAttendee->name().isEmpty())
        tmpStr = "MAILTO:" + curAttendee->name() + " <" +
                 curAttendee->email() + ">";
      else if (curAttendee->name().isEmpty())
        tmpStr = "MAILTO: " + curAttendee->email();
      else if (curAttendee->email().isEmpty())
        tmpStr = "MAILTO: " + curAttendee->name();
      else if (curAttendee->name().isEmpty() &&
               curAttendee->email().isEmpty())
        kdDebug(5800) << "warning! this Event has an attendee w/o name or email!" << endl;
      VObject *aProp = addPropValue(vevent, VCAttendeeProp, tmpStr.local8Bit());
      addPropValue(aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE");
      addPropValue(aProp, VCStatusProp, writeStatus(curAttendee->status()));
    }
  }

  // recurrence rule stuff
  const Recurrence *recur = anEvent->recurrence();
  if ( recur->doesRecur() ) {
    bool validRecur = true;
    TQString tmpStr2;
    switch ( recur->recurrenceType() ) {
    case Recurrence::rDaily:
      tmpStr.sprintf("D%i ",recur->frequency());
      break;
    case Recurrence::rWeekly:
      tmpStr.sprintf("W%i ",recur->frequency());
      for (int i = 0; i < 7; i++ ) {
        TQBitArray days ( recur->days() );
        if ( days.testBit(i) )
          tmpStr += dayFromNum(i);
      }
      break;
    case Recurrence::rMonthlyPos: {
      tmpStr.sprintf("MP%i ", recur->frequency());
      // write out all rMonthPos's
      TQValueList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions();
      for ( TQValueListConstIterator<RecurrenceRule::WDayPos> posit = tmpPositions.begin();
            posit != tmpPositions.end(); ++posit ) {
        int pos = (*posit).pos();
        tmpStr2.sprintf("%i", (pos>0) ? pos : (-pos) );
        if ( pos < 0)
          tmpStr2 += "- ";
        else
          tmpStr2 += "+ ";
        tmpStr += tmpStr2;
        tmpStr += dayFromNum( (*posit).day() - 1 );
      }
      break; }
    case Recurrence::rMonthlyDay: {
      tmpStr.sprintf("MD%i ", recur->frequency());
      // write out all rMonthDays;
      TQValueList<int> tmpDays = recur->monthDays();
      for ( TQValueListIterator<int> tmpDay = tmpDays.begin();
            tmpDay != tmpDays.end(); ++tmpDay ) {
        tmpStr2.sprintf( "%i ", *tmpDay );
        tmpStr += tmpStr2;
      }
      break; }
    case Recurrence::rYearlyMonth: {
      tmpStr.sprintf("YM%i ", recur->frequency());
      // write out all the months;'
      // TODO: Any way to write out the day within the month???
      TQValueList<int> months = recur->yearMonths();
      for ( TQValueListIterator<int> mit = months.begin();
            mit != months.end(); ++mit ) {
        tmpStr2.sprintf( "%i ", *mit );
        tmpStr += tmpStr2;
      }
      break; }
    case Recurrence::rYearlyDay: {
      tmpStr.sprintf("YD%i ", recur->frequency());
      // write out all the rYearNums;
      TQValueList<int> tmpDays = recur->yearDays();
      for ( TQValueListIterator<int> tmpDay = tmpDays.begin();
            tmpDay != tmpDays.end(); ++tmpDay ) {
        tmpStr2.sprintf( "%i ", *tmpDay );
        tmpStr += tmpStr2;
      }
      break; }
    default:
      // TODO: Write rYearlyPos and arbitrary rules!
      kdDebug(5800) << "ERROR, it should never get here in eventToVEvent!" << endl;
      validRecur = false;
      break;
    } // switch

    if (recur->duration() > 0) {
      tmpStr2.sprintf("#%i",recur->duration());
      tmpStr += tmpStr2;
    } else if (recur->duration() == -1) {
      tmpStr += "#0"; // defined as repeat forever
    } else {
      tmpStr += qDateTimeToISO(recur->endDateTime(), FALSE);
    }
    // Only write out the rrule if we have a valid recurrence (i.e. a known
    // type in thee switch above)
    if ( validRecur )
      addPropValue(vevent,VCRRuleProp, tmpStr.local8Bit());

  } // event repeats

  // exceptions to recurrence
  DateList dateList = recur->exDates();
  DateList::ConstIterator it;
  TQString tmpStr2;

  for (it = dateList.begin(); it != dateList.end(); ++it) {
    tmpStr = qDateToISO(*it) + ";";
    tmpStr2 += tmpStr;
  }
  if (!tmpStr2.isEmpty()) {
    tmpStr2.truncate(tmpStr2.length()-1);
    addPropValue(vevent, VCExDateProp, tmpStr2.local8Bit());
  }

  // description
  if (!anEvent->description().isEmpty()) {
    VObject *d = addPropValue(vevent, VCDescriptionProp,
                              anEvent->description().local8Bit());
    if (anEvent->description().find('\n') != -1)
      addPropValue(d, VCEncodingProp, VCQuotedPrintableProp);
  }

  // summary
  if (!anEvent->summary().isEmpty())
    addPropValue(vevent, VCSummaryProp, anEvent->summary().local8Bit());

  // location
  if (!anEvent->location().isEmpty())
    addPropValue(vevent, VCLocationProp, anEvent->location().local8Bit());

  // status
// TODO: define Event status
//  addPropValue(vevent, VCStatusProp, anEvent->statusStr().local8Bit());

  // secrecy
  const char *text = 0;
  switch (anEvent->secrecy()) {
    case Incidence::SecrecyPublic:
      text = "PUBLIC";
      break;
    case Incidence::SecrecyPrivate:
      text = "PRIVATE";
      break;
    case Incidence::SecrecyConfidential:
      text = "CONFIDENTIAL";
      break;
  }
  if (text) {
    addPropValue(vevent, VCClassProp, text);
  }

  // categories
  TQStringList tmpStrList = anEvent->categories();
  tmpStr = "";
  TQString catStr;
  for ( TQStringList::Iterator it = tmpStrList.begin();
        it != tmpStrList.end();
        ++it ) {
    catStr = *it;
    if (catStr[0] == ' ')
      tmpStr += catStr.mid(1);
    else
      tmpStr += catStr;
    // this must be a ';' character as the vCalendar specification requires!
    // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
    // read in.
    tmpStr += ";";
  }
  if (!tmpStr.isEmpty()) {
    tmpStr.truncate(tmpStr.length()-1);
    addPropValue(vevent, VCCategoriesProp, tmpStr.local8Bit());
  }

  // attachments
  // TODO: handle binary attachments!
  Attachment::List attachments = anEvent->attachments();
  Attachment::List::ConstIterator atIt;
  for ( atIt = attachments.begin(); atIt != attachments.end(); ++atIt )
    addPropValue( vevent, VCAttachProp, (*atIt)->uri().local8Bit() );

  // resources
  tmpStrList = anEvent->resources();
  tmpStr = tmpStrList.join(";");
  if (!tmpStr.isEmpty())
    addPropValue(vevent, VCResourcesProp, tmpStr.local8Bit());

  // alarm stuff
  Alarm::List::ConstIterator it2;
  for ( it2 = anEvent->alarms().begin(); it2 != anEvent->alarms().end(); ++it2 ) {
    Alarm *alarm = *it2;
    if (alarm->enabled()) {
      VObject *a = addProp(vevent, VCDAlarmProp);
      tmpStr = qDateTimeToISO(alarm->time());
      addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
      addPropValue(a, VCRepeatCountProp, "1");
      addPropValue(a, VCDisplayStringProp, "beep!");
      if (alarm->type() == Alarm::Audio) {
        a = addProp(vevent, VCAAlarmProp);
        addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
        addPropValue(a, VCRepeatCountProp, "1");
        addPropValue(a, VCAudioContentProp, TQFile::encodeName(alarm->audioFile()));
      }
      if (alarm->type() == Alarm::Procedure) {
        a = addProp(vevent, VCPAlarmProp);
        addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
        addPropValue(a, VCRepeatCountProp, "1");
        addPropValue(a, VCProcedureNameProp, TQFile::encodeName(alarm->programFile()));
      }
    }
  }

  // priority
  tmpStr.sprintf("%i",anEvent->priority());
  addPropValue(vevent, VCPriorityProp, tmpStr.local8Bit());

  // transparency
  tmpStr.sprintf("%i",anEvent->transparency());
  addPropValue(vevent, VCTranspProp, tmpStr.local8Bit());

  // related event
  if (anEvent->relatedTo()) {
    addPropValue(vevent, VCRelatedToProp,
                 anEvent->relatedTo()->uid().local8Bit());
  }

  if (anEvent->pilotId()) {
    // pilot sync stuff
    tmpStr.sprintf("%lu",anEvent->pilotId());
    addPropValue(vevent, KPilotIdProp, tmpStr.local8Bit());
    tmpStr.sprintf("%i",anEvent->syncStatus());
    addPropValue(vevent, KPiloStatusProp, tmpStr.local8Bit());
  }

  return vevent;
}

Todo *VCalFormat::VTodoToEvent(VObject *vtodo)
{
  VObject *vo;
  VObjectIterator voi;
  char *s;

  Todo *anEvent = new Todo;

  // creation date
  if ((vo = isAPropertyOf(vtodo, VCDCreatedProp)) != 0) {
      anEvent->setCreated(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
      deleteStr(s);
  }

  // unique id
  vo = isAPropertyOf(vtodo, VCUniqueStringProp);
  // while the UID property is preferred, it is not required.  We'll use the
  // default Event UID if none is given.
  if (vo) {
    anEvent->setUid(s = fakeCString(vObjectUStringZValue(vo)));
    deleteStr(s);
  }

  // last modification date
  if ((vo = isAPropertyOf(vtodo, VCLastModifiedProp)) != 0) {
    anEvent->setLastModified(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }
  else
    anEvent->setLastModified(TQDateTime(TQDate::currentDate(),
                                       TQTime::currentTime()));

  // organizer
  // if our extension property for the event's ORGANIZER exists, add it.
  if ((vo = isAPropertyOf(vtodo, ICOrganizerProp)) != 0) {
    anEvent->setOrganizer( s = fakeCString(vObjectUStringZValue(vo) ) );
    deleteStr(s);
  } else {
    anEvent->setOrganizer( mCalendar->getOwner() );
  }

  // attendees.
  initPropIterator(&voi, vtodo);
  while (moreIteration(&voi)) {
    vo = nextVObject(&voi);
    if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) {
      Attendee *a;
      VObject *vp;
      s = fakeCString(vObjectUStringZValue(vo));
      TQString tmpStr = TQString::fromLocal8Bit(s);
      deleteStr(s);
      tmpStr = tmpStr.simplifyWhiteSpace();
      int emailPos1, emailPos2;
      if ((emailPos1 = tmpStr.find('<')) > 0) {
        // both email address and name
        emailPos2 = tmpStr.findRev('>');
        a = new Attendee(tmpStr.left(emailPos1 - 1),
                         tmpStr.mid(emailPos1 + 1,
                                    emailPos2 - (emailPos1 + 1)));
      } else if (tmpStr.find('@') > 0) {
        // just an email address
        a = new Attendee(0, tmpStr);
      } else {
        // just a name
        // WTF??? Replacing the spaces of a name and using this as email?
        TQString email = tmpStr.replace( ' ', '.' );
        a = new Attendee(tmpStr,email);
      }

      // is there an RSVP property?
      if ((vp = isAPropertyOf(vo, VCRSVPProp)) != 0)
        a->setRSVP(vObjectStringZValue(vp));
      // is there a status property?
      if ((vp = isAPropertyOf(vo, VCStatusProp)) != 0)
        a->setStatus(readStatus(vObjectStringZValue(vp)));
      // add the attendee
      anEvent->addAttendee(a);
    }
  }

  // description for todo
  if ((vo = isAPropertyOf(vtodo, VCDescriptionProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    anEvent->setDescription(TQString::fromLocal8Bit(s));
    deleteStr(s);
  }

  // summary
  if ((vo = isAPropertyOf(vtodo, VCSummaryProp))) {
    s = fakeCString(vObjectUStringZValue(vo));
    anEvent->setSummary(TQString::fromLocal8Bit(s));
    deleteStr(s);
  }


  // location
  if ((vo = isAPropertyOf(vtodo, VCLocationProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    anEvent->setLocation( TQString::fromLocal8Bit(s) );
    deleteStr(s);
  }
  // completed
  // was: status
  if ((vo = isAPropertyOf(vtodo, VCStatusProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    if (strcmp(s,"COMPLETED") == 0) {
      anEvent->setCompleted(true);
    } else {
      anEvent->setCompleted(false);
    }
    deleteStr(s);
  }
  else
    anEvent->setCompleted(false);

  // completion date
  if ((vo = isAPropertyOf(vtodo, VCCompletedProp)) != 0) {
    anEvent->setCompleted(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }

  // priority
  if ((vo = isAPropertyOf(vtodo, VCPriorityProp))) {
    anEvent->setPriority(atoi(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }

  // due date
  if ((vo = isAPropertyOf(vtodo, VCDueProp)) != 0) {
    anEvent->setDtDue(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
    anEvent->setHasDueDate(true);
  } else {
    anEvent->setHasDueDate(false);
  }

  // start time
  if ((vo = isAPropertyOf(vtodo, VCDTstartProp)) != 0) {
    anEvent->setDtStart(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
    //    kdDebug(5800) << "s is " << //          s << ", ISO is " << ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))).toString() << endl;
    deleteStr(s);
    anEvent->setHasStartDate(true);
  } else {
    anEvent->setHasStartDate(false);
  }

  /* alarm stuff */
  //kdDebug(5800) << "vcalformat::VTodoToEvent called" << endl;
  if ((vo = isAPropertyOf(vtodo, VCDAlarmProp))) {
    Alarm* alarm = anEvent->newAlarm();
    VObject *a;
    if ((a = isAPropertyOf(vo, VCRunTimeProp))) {
      alarm->setTime(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(a))));
      deleteStr(s);
    }
    alarm->setEnabled(true);
    if ((vo = isAPropertyOf(vtodo, VCPAlarmProp))) {
      if ((a = isAPropertyOf(vo, VCProcedureNameProp))) {
        s = fakeCString(vObjectUStringZValue(a));
        alarm->setProcedureAlarm(TQFile::decodeName(s));
        deleteStr(s);
      }
    }
    if ((vo = isAPropertyOf(vtodo, VCAAlarmProp))) {
      if ((a = isAPropertyOf(vo, VCAudioContentProp))) {
        s = fakeCString(vObjectUStringZValue(a));
        alarm->setAudioAlarm(TQFile::decodeName(s));
        deleteStr(s);
      }
    }
  }

  // related todo
  if ((vo = isAPropertyOf(vtodo, VCRelatedToProp)) != 0) {
    anEvent->setRelatedToUid(s = fakeCString(vObjectUStringZValue(vo)));
    deleteStr(s);
    mTodosRelate.append(anEvent);
  }

  // categories
  if ((vo = isAPropertyOf(vtodo, VCCategoriesProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    TQString categories = TQString::fromLocal8Bit(s);
    deleteStr(s);
    TQStringList tmpStrList = TQStringList::split( ';', categories );
    anEvent->setCategories(tmpStrList);
  }

  /* PILOT SYNC STUFF */
  if ((vo = isAPropertyOf(vtodo, KPilotIdProp))) {
    anEvent->setPilotId(atoi(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }
  else
    anEvent->setPilotId(0);

  if ((vo = isAPropertyOf(vtodo, KPiloStatusProp))) {
    anEvent->setSyncStatus(atoi(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }
  else
    anEvent->setSyncStatus(Event::SYNCMOD);

  return anEvent;
}

Event* VCalFormat::VEventToEvent(VObject *vevent)
{
  VObject *vo;
  VObjectIterator voi;
  char *s;

  Event *anEvent = new Event;

  // creation date
  if ((vo = isAPropertyOf(vevent, VCDCreatedProp)) != 0) {
      anEvent->setCreated(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
      deleteStr(s);
  }

  // unique id
  vo = isAPropertyOf(vevent, VCUniqueStringProp);
  // while the UID property is preferred, it is not required.  We'll use the
  // default Event UID if none is given.
  if (vo) {
    anEvent->setUid(s = fakeCString(vObjectUStringZValue(vo)));
    deleteStr(s);
  }

  // revision
  // again NSCAL doesn't give us much to work with, so we improvise...
  if ((vo = isAPropertyOf(vevent, VCSequenceProp)) != 0) {
    anEvent->setRevision(atoi(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }
  else
    anEvent->setRevision(0);

  // last modification date
  if ((vo = isAPropertyOf(vevent, VCLastModifiedProp)) != 0) {
    anEvent->setLastModified(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }
  else
    anEvent->setLastModified(TQDateTime(TQDate::currentDate(),
                                       TQTime::currentTime()));

  // organizer
  // if our extension property for the event's ORGANIZER exists, add it.
  if ((vo = isAPropertyOf(vevent, ICOrganizerProp)) != 0) {
    // FIXME:  Also use the full name, not just the email address
    anEvent->setOrganizer( s = fakeCString(vObjectUStringZValue(vo) ) );
    deleteStr(s);
  } else {
    anEvent->setOrganizer( mCalendar->getOwner() );
  }

  // deal with attendees.
  initPropIterator(&voi, vevent);
  while (moreIteration(&voi)) {
    vo = nextVObject(&voi);
    if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) {
      Attendee *a;
      VObject *vp;
      s = fakeCString(vObjectUStringZValue(vo));
      TQString tmpStr = TQString::fromLocal8Bit(s);
      deleteStr(s);
      tmpStr = tmpStr.simplifyWhiteSpace();
      int emailPos1, emailPos2;
      if ((emailPos1 = tmpStr.find('<')) > 0) {
        // both email address and name
        emailPos2 = tmpStr.findRev('>');
        a = new Attendee(tmpStr.left(emailPos1 - 1),
                         tmpStr.mid(emailPos1 + 1,
                                    emailPos2 - (emailPos1 + 1)));
      } else if (tmpStr.find('@') > 0) {
        // just an email address
        a = new Attendee(0, tmpStr);
      } else {
        // just a name
        TQString email = tmpStr.replace( ' ', '.' );
        a = new Attendee(tmpStr,email);
      }

      // is there an RSVP property?
      if ((vp = isAPropertyOf(vo, VCRSVPProp)) != 0)
        a->setRSVP(vObjectStringZValue(vp));
      // is there a status property?
      if ((vp = isAPropertyOf(vo, VCStatusProp)) != 0)
        a->setStatus(readStatus(vObjectStringZValue(vp)));
      // add the attendee
      anEvent->addAttendee(a);
    }
  }

  // This isn't strictly true.  An event that doesn't have a start time
  // or an end time doesn't "float", it has an anchor in time but it doesn't
  // "take up" any time.
  /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
      (isAPropertyOf(vevent, VCDTendProp) == 0)) {
    anEvent->setFloats(TRUE);
    } else {
    }*/

  anEvent->setFloats(FALSE);

  // start time
  if ((vo = isAPropertyOf(vevent, VCDTstartProp)) != 0) {
    anEvent->setDtStart(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
    //    kdDebug(5800) << "s is " << //          s << ", ISO is " << ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))).toString() << endl;
    deleteStr(s);
    if (anEvent->dtStart().time().isNull())
      anEvent->setFloats(TRUE);
  }

  // stop time
  if ((vo = isAPropertyOf(vevent, VCDTendProp)) != 0) {
    anEvent->setDtEnd(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
      deleteStr(s);
      if (anEvent->dtEnd().time().isNull())
        anEvent->setFloats(TRUE);
  }

  // at this point, there should be at least a start or end time.
  // fix up for events that take up no time but have a time associated
  if (!(vo = isAPropertyOf(vevent, VCDTstartProp)))
    anEvent->setDtStart(anEvent->dtEnd());
  if (!(vo = isAPropertyOf(vevent, VCDTendProp)))
    anEvent->setDtEnd(anEvent->dtStart());

  ///////////////////////////////////////////////////////////////////////////

  // repeat stuff
  if ((vo = isAPropertyOf(vevent, VCRRuleProp)) != 0) {
    TQString tmpStr = (s = fakeCString(vObjectUStringZValue(vo)));
    deleteStr(s);
    tmpStr.simplifyWhiteSpace();
    tmpStr = tmpStr.upper();
// kdDebug() <<" We have a recurrence rule: " << tmpStr<< endl;

    // first, read the type of the recurrence
    int typelen = 1;
    uint type = Recurrence::rNone;
    if ( tmpStr.left(1) == "D") {
      type = Recurrence::rDaily;
    } else if ( tmpStr.left(1) == "W") {
      type = Recurrence::rWeekly;
    } else {
      typelen = 2;
      if ( tmpStr.left(2) == "MP") {
        type = Recurrence::rMonthlyPos;
      } else if ( tmpStr.left(2) == "MD" ) {
        type = Recurrence::rMonthlyDay;
      } else if ( tmpStr.left(2) == "YM" ) {
        type = Recurrence::rYearlyMonth;
      } else if ( tmpStr.left(2) == "YD" ) {
        type = Recurrence::rYearlyDay;
      }
    }

    if ( type != Recurrence::rNone ) {
// kdDebug() << " It's a supported type " << endl;

      // Immediately after the type is the frequency
      int index = tmpStr.find(' ');
      int last = tmpStr.findRev(' ') + 1; // find last entry
      int rFreq = tmpStr.mid(typelen, (index-1)).toInt();
      ++index; // advance to beginning of stuff after freq

      // Read the type-specific settings
      switch ( type ) {
        case Recurrence::rDaily:
               anEvent->recurrence()->setDaily(rFreq);
               break;

        case Recurrence::rWeekly: {
               TQBitArray qba(7);
               TQString dayStr;
               if( index == last ) {
                 // e.g. W1 #0
                 qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
               }
               else {
                 // e.g. W1 SU #0
                 while (index < last) {
                   dayStr = tmpStr.mid(index, 3);
                   int dayNum = numFromDay(dayStr);
                   qba.setBit(dayNum);
                   index += 3; // advance to next day, or possibly "#"
                 }
               }
               anEvent->recurrence()->setWeekly( rFreq, qba );
               break; }

        case Recurrence::rMonthlyPos: {
               anEvent->recurrence()->setMonthly( rFreq );

               TQBitArray qba(7);
               short tmpPos;
               if( index == last ) {
                 // e.g. MP1 #0
                 tmpPos = anEvent->dtStart().date().day()/7 + 1;
                 if( tmpPos == 5 )
                   tmpPos = -1;
                 qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
                 anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
               }
               else {
                 // e.g. MP1 1+ SU #0
                 while (index < last) {
                   tmpPos = tmpStr.mid(index,1).toShort();
                   index += 1;
                   if (tmpStr.mid(index,1) == "-")
                     // convert tmpPos to negative
                     tmpPos = 0 - tmpPos;
                   index += 2; // advance to day(s)
                   while (numFromDay(tmpStr.mid(index,3)) >= 0) {
                     int dayNum = numFromDay(tmpStr.mid(index,3));
                     qba.setBit(dayNum);
                     index += 3; // advance to next day, or possibly pos or "#"
                   }
                   anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
                   qba.detach();
                   qba.fill(FALSE); // clear out
                 } // while != "#"
               }
             break;}

        case Recurrence::rMonthlyDay:
             anEvent->recurrence()->setMonthly( rFreq );
             if( index == last ) {
               // e.g. MD1 #0
               short tmpDay = anEvent->dtStart().date().day();
               anEvent->recurrence()->addMonthlyDate( tmpDay );
             }
             else {
               // e.g. MD1 3 #0
               while (index < last) {
                 int index2 = tmpStr.find(' ', index);
                 short tmpDay = tmpStr.mid(index, (index2-index)).toShort();
                 index = index2-1;
                 if (tmpStr.mid(index, 1) == "-")
                   tmpDay = 0 - tmpDay;
                 index += 2; // advance the index;
                 anEvent->recurrence()->addMonthlyDate( tmpDay );
               } // while != #
             }
             break;

        case Recurrence::rYearlyMonth:
             anEvent->recurrence()->setYearly( rFreq );

             if( index == last ) {
               // e.g. YM1 #0
               short tmpMonth = anEvent->dtStart().date().month();
               anEvent->recurrence()->addYearlyMonth( tmpMonth );
             }
             else {
               // e.g. YM1 3 #0
               while (index < last) {
                 int index2 = tmpStr.find(' ', index);
                 short tmpMonth = tmpStr.mid(index, (index2-index)).toShort();
                 index = index2 + 1;
                 anEvent->recurrence()->addYearlyMonth( tmpMonth );
               } // while != #
             }
             break;

        case Recurrence::rYearlyDay:
             anEvent->recurrence()->setYearly( rFreq );

             if( index == last ) {
               // e.g. YD1 #0
               short tmpDay = anEvent->dtStart().date().dayOfYear();
               anEvent->recurrence()->addYearlyDay( tmpDay );
             }
             else {
               // e.g. YD1 123 #0
               while (index < last) {
                 int index2 = tmpStr.find(' ', index);
                 short tmpDay = tmpStr.mid(index, (index2-index)).toShort();
                 index = index2+1;
                 anEvent->recurrence()->addYearlyDay( tmpDay );
               } // while != #
             }
             break;

        default: break;
      }

      // find the last field, which is either the duration or the end date
      index = last;
      if ( tmpStr.mid(index,1) == "#") {
        // Nr of occurrences
        index++;
        int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt();
        if ( rDuration > 0 )
          anEvent->recurrence()->setDuration( rDuration );
      } else if ( tmpStr.find('T', index) != -1 ) {
        TQDate rEndDate = (ISOToTQDateTime(tmpStr.mid(index, tmpStr.length()-index))).date();
        anEvent->recurrence()->setEndDateTime( rEndDate );
      }
// anEvent->recurrence()->dump();

    } else {
      kdDebug(5800) << "we don't understand this type of recurrence!" << endl;
    } // if known recurrence type
  } // repeats


  // recurrence exceptions
  if ((vo = isAPropertyOf(vevent, VCExDateProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    TQStringList exDates = TQStringList::split(",",s);
    TQStringList::ConstIterator it;
    for(it = exDates.begin(); it != exDates.end(); ++it ) {
      anEvent->recurrence()->addExDate(ISOToTQDate(*it));
    }
    deleteStr(s);
  }

  // summary
  if ((vo = isAPropertyOf(vevent, VCSummaryProp))) {
    s = fakeCString(vObjectUStringZValue(vo));
    anEvent->setSummary(TQString::fromLocal8Bit(s));
    deleteStr(s);
  }

  // description
  if ((vo = isAPropertyOf(vevent, VCDescriptionProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    if (!anEvent->description().isEmpty()) {
      anEvent->setDescription(anEvent->description() + "\n" +
                              TQString::fromLocal8Bit(s));
    } else {
      anEvent->setDescription(TQString::fromLocal8Bit(s));
    }
    deleteStr(s);
  }

  // location
  if ((vo = isAPropertyOf(vevent, VCLocationProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    anEvent->setLocation( TQString::fromLocal8Bit(s) );
    deleteStr(s);
  }

  // some stupid vCal exporters ignore the standard and use Description
  // instead of Summary for the default field.  Correct for this.
  if (anEvent->summary().isEmpty() &&
      !(anEvent->description().isEmpty())) {
    TQString tmpStr = anEvent->description().simplifyWhiteSpace();
    anEvent->setDescription("");
    anEvent->setSummary(tmpStr);
  }

#if 0
  // status
  if ((vo = isAPropertyOf(vevent, VCStatusProp)) != 0) {
    TQString tmpStr(s = fakeCString(vObjectUStringZValue(vo)));
    deleteStr(s);
// TODO: Define Event status
//    anEvent->setStatus(tmpStr);
  }
  else
//    anEvent->setStatus("NEEDS ACTION");
#endif

  // secrecy
  int secrecy = Incidence::SecrecyPublic;
  if ((vo = isAPropertyOf(vevent, VCClassProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    if (strcmp(s,"PRIVATE") == 0) {
      secrecy = Incidence::SecrecyPrivate;
    } else if (strcmp(s,"CONFIDENTIAL") == 0) {
      secrecy = Incidence::SecrecyConfidential;
    }
    deleteStr(s);
  }
  anEvent->setSecrecy(secrecy);

  // categories
  if ((vo = isAPropertyOf(vevent, VCCategoriesProp)) != 0) {
    s = fakeCString(vObjectUStringZValue(vo));
    TQString categories = TQString::fromLocal8Bit(s);
    deleteStr(s);
    TQStringList tmpStrList = TQStringList::split( ',', categories );
    anEvent->setCategories(tmpStrList);
  }

  // attachments
  initPropIterator(&voi, vevent);
  while (moreIteration(&voi)) {
    vo = nextVObject(&voi);
    if (strcmp(vObjectName(vo), VCAttachProp) == 0) {
      s = fakeCString(vObjectUStringZValue(vo));
      anEvent->addAttachment(new Attachment(TQString(s)));
      deleteStr(s);
    }
  }

  // resources
  if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != 0) {
    TQString resources = (s = fakeCString(vObjectUStringZValue(vo)));
    deleteStr(s);
    TQStringList tmpStrList = TQStringList::split( ';', resources );
    anEvent->setResources(tmpStrList);
  }

  /* alarm stuff */
  if ((vo = isAPropertyOf(vevent, VCDAlarmProp))) {
    Alarm* alarm = anEvent->newAlarm();
    VObject *a;
    if ((a = isAPropertyOf(vo, VCRunTimeProp))) {
      alarm->setTime(ISOToTQDateTime(s = fakeCString(vObjectUStringZValue(a))));
      deleteStr(s);
    }
    alarm->setEnabled(true);
    if ((vo = isAPropertyOf(vevent, VCPAlarmProp))) {
      if ((a = isAPropertyOf(vo, VCProcedureNameProp))) {
        s = fakeCString(vObjectUStringZValue(a));
        alarm->setProcedureAlarm(TQFile::decodeName(s));
        deleteStr(s);
      }
    }
    if ((vo = isAPropertyOf(vevent, VCAAlarmProp))) {
      if ((a = isAPropertyOf(vo, VCAudioContentProp))) {
        s = fakeCString(vObjectUStringZValue(a));
        alarm->setAudioAlarm(TQFile::decodeName(s));
        deleteStr(s);
      }
    }
  }

  // priority
  if ((vo = isAPropertyOf(vevent, VCPriorityProp))) {
    anEvent->setPriority(atoi(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }

  // transparency
  if ((vo = isAPropertyOf(vevent, VCTranspProp)) != 0) {
    int i = atoi(s = fakeCString(vObjectUStringZValue(vo)));
    anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque );
    deleteStr(s);
  }

  // related event
  if ((vo = isAPropertyOf(vevent, VCRelatedToProp)) != 0) {
    anEvent->setRelatedToUid(s = fakeCString(vObjectUStringZValue(vo)));
    deleteStr(s);
    mEventsRelate.append(anEvent);
  }

  /* PILOT SYNC STUFF */
  if ((vo = isAPropertyOf(vevent, KPilotIdProp))) {
    anEvent->setPilotId(atoi(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }
  else
    anEvent->setPilotId(0);

  if ((vo = isAPropertyOf(vevent, KPiloStatusProp))) {
    anEvent->setSyncStatus(atoi(s = fakeCString(vObjectUStringZValue(vo))));
    deleteStr(s);
  }
  else
    anEvent->setSyncStatus(Event::SYNCMOD);

  return anEvent;
}


TQString VCalFormat::qDateToISO(const TQDate &qd)
{
  TQString tmpStr;

  Q_ASSERT(qd.isValid());

  tmpStr.sprintf("%.2d%.2d%.2d",
                 qd.year(), qd.month(), qd.day());
  return tmpStr;

}

/* Return the offset of the named zone as seconds. tt is a time
   indicating the date for which you want the offset */
int vcaltime_utc_offset( TQDateTime ictt, TQString tzid )
{
  // libical-0.23 stuff:
  //  struct icaltimetype tt = icaltime_from_timet( ictt.toTime_t(), false );
  //  return icaltime_utc_offset( tt, tzid.latin1() );
  int daylight;
  struct icaltimetype tt = icaltime_from_timet_with_zone( ictt.toTime_t(), false, NULL);
  return icaltimezone_get_utc_offset(
    icaltimezone_get_builtin_timezone( tzid.latin1() ),
    &tt, &daylight );
}

TQString VCalFormat::qDateTimeToISO(const TQDateTime &qdt, bool zulu)
{
  TQString tmpStr;

  Q_ASSERT(qdt.date().isValid());
  Q_ASSERT(qdt.time().isValid());
  if (zulu) {
    TQDateTime tmpDT(qdt);
    // correct to GMT:
    tmpDT = tmpDT.addSecs(-vcaltime_utc_offset( tmpDT, mCalendar->timeZoneId()));
    tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2dZ",
                    tmpDT.date().year(), tmpDT.date().month(),
                    tmpDT.date().day(), tmpDT.time().hour(),
                    tmpDT.time().minute(), tmpDT.time().second());
  } else {
    tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d",
                    qdt.date().year(), qdt.date().month(),
                    qdt.date().day(), qdt.time().hour(),
                    qdt.time().minute(), qdt.time().second());
  }
  return tmpStr;
}

TQDateTime VCalFormat::ISOToTQDateTime(const TQString & dtStr)
{
  TQDate tmpDate;
  TQTime tmpTime;
  TQString tmpStr;
  int year, month, day, hour, minute, second;

  tmpStr = dtStr;
  year = tmpStr.left(4).toInt();
  month = tmpStr.mid(4,2).toInt();
  day = tmpStr.mid(6,2).toInt();
  hour = tmpStr.mid(9,2).toInt();
  minute = tmpStr.mid(11,2).toInt();
  second = tmpStr.mid(13,2).toInt();
  tmpDate.setYMD(year, month, day);
  tmpTime.setHMS(hour, minute, second);

  Q_ASSERT(tmpDate.isValid());
  Q_ASSERT(tmpTime.isValid());
  TQDateTime tmpDT(tmpDate, tmpTime);
  // correct for GMT if string is in Zulu format
  if (dtStr.at(dtStr.length()-1) == 'Z') {
    tmpDT = tmpDT.addSecs(vcaltime_utc_offset( tmpDT, mCalendar->timeZoneId()));
  }
  return tmpDT;
}

TQDate VCalFormat::ISOToTQDate(const TQString &dateStr)
{
  int year, month, day;

  year = dateStr.left(4).toInt();
  month = dateStr.mid(4,2).toInt();
  day = dateStr.mid(6,2).toInt();

  return(TQDate(year, month, day));
}

// take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
// and break it down from it's tree-like format into the dictionary format
// that is used internally in the VCalFormat.
void VCalFormat::populate(VObject *vcal)
{
  // this function will populate the caldict dictionary and other event
  // lists. It turns vevents into Events and then inserts them.

  VObjectIterator i;
  VObject *curVO, *curVOProp;
  Event *anEvent;

  if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != 0) {
    char *methodType = 0;
    methodType = fakeCString(vObjectUStringZValue(curVO));
    kdDebug(5800) << "This calendar is an iTIP transaction of type '"
              << methodType << "'" << endl;
    deleteStr(methodType);
  }

  // warn the user that we might have trouble reading non-known calendar.
  if ((curVO = isAPropertyOf(vcal, VCProdIdProp)) != 0) {
    char *s = fakeCString(vObjectUStringZValue(curVO));
    if (strcmp(productId().local8Bit(), s) != 0)
      kdDebug(5800) << "This vCalendar file was not created by KOrganizer "
                   "or any other product we support. Loading anyway..." << endl;
    mLoadedProductId = s;
    deleteStr(s);
  }

  // warn the user we might have trouble reading this unknown version.
  if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != 0) {
    char *s = fakeCString(vObjectUStringZValue(curVO));
    if (strcmp(_VCAL_VERSION, s) != 0)
      kdDebug(5800) << "This vCalendar file has version " << s
                << "We only support " << _VCAL_VERSION << endl;
    deleteStr(s);
  }

#if 0
  // set the time zone (this is a property of the view, so just discard!)
  if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != 0) {
    char *s = fakeCString(vObjectUStringZValue(curVO));
    mCalendar->setTimeZone(s);
    deleteStr(s);
  }
#endif

  // Store all events with a relatedTo property in a list for post-processing
  mEventsRelate.clear();
  mTodosRelate.clear();

  initPropIterator(&i, vcal);

  // go through all the vobjects in the vcal
  while (moreIteration(&i)) {
    curVO = nextVObject(&i);

    /************************************************************************/

    // now, check to see that the object is an event or todo.
    if (strcmp(vObjectName(curVO), VCEventProp) == 0) {

      if ((curVOProp = isAPropertyOf(curVO, KPiloStatusProp)) != 0) {
        char *s;
        s = fakeCString(vObjectUStringZValue(curVOProp));
        // check to see if event was deleted by the kpilot conduit
        if (atoi(s) == Event::SYNCDEL) {
          deleteStr(s);
          kdDebug(5800) << "skipping pilot-deleted event" << endl;
          goto SKIP;
        }
        deleteStr(s);
      }

      // this code checks to see if we are trying to read in an event
      // that we already find to be in the calendar.  If we find this
      // to be the case, we skip the event.
      if ((curVOProp = isAPropertyOf(curVO, VCUniqueStringProp)) != 0) {
        char *s = fakeCString(vObjectUStringZValue(curVOProp));
        TQString tmpStr(s);
        deleteStr(s);

        if (mCalendar->incidence(tmpStr)) {
          goto SKIP;
        }
      }

      if ((!(curVOProp = isAPropertyOf(curVO, VCDTstartProp))) &&
          (!(curVOProp = isAPropertyOf(curVO, VCDTendProp)))) {
        kdDebug(5800) << "found a VEvent with no DTSTART and no DTEND! Skipping..." << endl;
        goto SKIP;
      }

      anEvent = VEventToEvent(curVO);
      // we now use addEvent instead of insertEvent so that the
      // signal/slot get connected.
      if (anEvent) {
              if ( !anEvent->dtStart().isValid() || !anEvent->dtEnd().isValid() ) {
          kdDebug(5800) << "VCalFormat::populate(): Event has invalid dates."
                    << endl;
        } else {
          mCalendar->addEvent(anEvent);
              }
      } else {
        // some sort of error must have occurred while in translation.
        goto SKIP;
      }
    } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) {
      Todo *aTodo = VTodoToEvent(curVO);
      mCalendar->addTodo(aTodo);
    } else if ((strcmp(vObjectName(curVO), VCVersionProp) == 0) ||
               (strcmp(vObjectName(curVO), VCProdIdProp) == 0) ||
               (strcmp(vObjectName(curVO), VCTimeZoneProp) == 0)) {
      // do nothing, we know these properties and we want to skip them.
      // we have either already processed them or are ignoring them.
      ;
    } else {
      kdDebug(5800) << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"" << endl;
    }
  SKIP:
    ;
  } // while

  // Post-Process list of events with relations, put Event objects in relation
  Event::List::ConstIterator eIt;
  for ( eIt = mEventsRelate.begin(); eIt != mEventsRelate.end(); ++eIt ) {
    (*eIt)->setRelatedTo( mCalendar->incidence( (*eIt)->relatedToUid() ) );
  }
  Todo::List::ConstIterator tIt;
  for ( tIt = mTodosRelate.begin(); tIt != mTodosRelate.end(); ++tIt ) {
    (*tIt)->setRelatedTo( mCalendar->incidence( (*tIt)->relatedToUid() ) );
   }
}

const char *VCalFormat::dayFromNum(int day)
{
  const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " };

  return days[day];
}

int VCalFormat::numFromDay(const TQString &day)
{
  if (day == "MO ") return 0;
  if (day == "TU ") return 1;
  if (day == "WE ") return 2;
  if (day == "TH ") return 3;
  if (day == "FR ") return 4;
  if (day == "SA ") return 5;
  if (day == "SU ") return 6;

  return -1; // something bad happened. :)
}

Attendee::PartStat VCalFormat::readStatus(const char *s) const
{
  TQString statStr = s;
  statStr = statStr.upper();
  Attendee::PartStat status;

  if (statStr == "X-ACTION")
    status = Attendee::NeedsAction;
  else if (statStr == "NEEDS ACTION")
    status = Attendee::NeedsAction;
  else if (statStr== "ACCEPTED")
    status = Attendee::Accepted;
  else if (statStr== "SENT")
    status = Attendee::NeedsAction;
  else if (statStr== "TENTATIVE")
    status = Attendee::Tentative;
  else if (statStr== "CONFIRMED")
    status = Attendee::Accepted;
  else if (statStr== "DECLINED")
    status = Attendee::Declined;
  else if (statStr== "COMPLETED")
    status = Attendee::Completed;
  else if (statStr== "DELEGATED")
    status = Attendee::Delegated;
  else {
    kdDebug(5800) << "error setting attendee mStatus, unknown mStatus!" << endl;
    status = Attendee::NeedsAction;
  }

  return status;
}

TQCString VCalFormat::writeStatus(Attendee::PartStat status) const
{
  switch(status) {
    default:
    case Attendee::NeedsAction:
      return "NEEDS ACTION";
      break;
    case Attendee::Accepted:
      return "ACCEPTED";
      break;
    case Attendee::Declined:
      return "DECLINED";
      break;
    case Attendee::Tentative:
      return "TENTATIVE";
      break;
    case Attendee::Delegated:
      return "DELEGATED";
      break;
    case Attendee::Completed:
      return "COMPLETED";
      break;
    case Attendee::InProcess:
      return "NEEDS ACTION";
      break;
  }
}
