/***************************************************************************
    copyright            : (C) 2005-2006 by Robby Stephenson
    email                : robby@periapsis.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of version 2 of the GNU General Public License as  *
 *   published by the Free Software Foundation;                            *
 *                                                                         *
 ***************************************************************************/

#include "calendarhandler.h"
#include "entry.h"
#include "tellico_kernel.h"
#include "tellico_debug.h"

#include <klocale.h>
#include <kstandarddirs.h>
#include <kconfig.h>

#ifdef USE_KCAL
#include <libkcal/calendarresources.h>
#include <libkcal/todo.h>
#include <libkcal/resourcelocal.h>
// #include <libkcal/resourceremote.h> // this file is moving around, API differences
#endif

// needed for ::readlink
#include <unistd.h>
#include <limits.h>

// most of this code came from konsolekalendar in tdepim

using Tellico::CalendarHandler;

void CalendarHandler::addLoans(Data::LoanVec loans_) {
#ifdef USE_KCAL
  addLoans(loans_, 0);
#else
  Q_UNUSED(loans_);
#endif
}

#ifdef USE_KCAL
void CalendarHandler::addLoans(Data::LoanVec loans_, KCal::CalendarResources* resources_) {
  if(loans_.isEmpty()) {
    return;
  }

  KCal::CalendarResources* calendarResources;
  if(resources_) {
    calendarResources = resources_;
  } else {
    calendarResources = new KCal::CalendarResources(timezone());
    calendarResources->readConfig();
    calendarResources->load();
    if(!checkCalendar(calendarResources)) {
      return;
    }
  }

  for(Data::LoanVec::Iterator loan = loans_.begin(); loan != loans_.end(); ++loan) {
    // only add loans with a due date
    if(loan->dueDate().isNull()) {
      continue;
    }

    KCal::Todo* todo = new KCal::Todo();
    populateTodo(todo, loan);

    calendarResources->addTodo(todo);
    loan->setInCalendar(true);
  }
  calendarResources->save();
  // don't close if a pointer was passed
  if(!resources_) {
    calendarResources->close();
    calendarResources->deleteLater();
  }
}
#endif

void CalendarHandler::modifyLoans(Data::LoanVec loans_) {
#ifndef USE_KCAL
  Q_UNUSED(loans_);
  return;
#else
  if(loans_.isEmpty()) {
    return;
  }

  KCal::CalendarResources calendarResources(timezone());
  calendarResources.readConfig();
  if(!checkCalendar(&calendarResources)) {
    return;
  }
  calendarResources.load();

  for(Data::LoanVec::Iterator loan = loans_.begin(); loan != loans_.end(); ++loan) {
    KCal::Todo* todo = calendarResources.todo(loan->uid());
    if(!todo) {
//      myDebug() << "couldn't find existing todo, adding a new todo" << endl;
      Data::LoanVec newLoans;
      newLoans.append(loan);
      addLoans(newLoans, &calendarResources); // add loan
      continue;
    }
    if(loan->dueDate().isNull()) {
      myDebug() << "removing todo" << endl;
      calendarResources.deleteIncidence(todo);
      continue;
    }

    populateTodo(todo, loan);
    todo->updated();

    loan->setInCalendar(true);
  }
  calendarResources.save();
  calendarResources.close();
#endif
}

void CalendarHandler::removeLoans(Data::LoanVec loans_) {
#ifndef USE_KCAL
  Q_UNUSED(loans_);
  return;
#else
  if(loans_.isEmpty()) {
    return;
  }

  KCal::CalendarResources calendarResources(timezone());
  calendarResources.readConfig();
  if(!checkCalendar(&calendarResources)) {
    return;
  }
  calendarResources.load();

  for(Data::LoanVec::Iterator loan = loans_.begin(); loan != loans_.end(); ++loan) {
    KCal::Todo* todo = calendarResources.todo(loan->uid());
    if(todo) {
      // maybe this is too much, we could just set the todo as done
      calendarResources.deleteIncidence(todo);
    }
  }
  calendarResources.save();
  calendarResources.close();
#endif
}

#ifdef USE_KCAL
bool CalendarHandler::checkCalendar(KCal::CalendarResources* resources) {
  KCal::CalendarResourceManager* manager = resources->resourceManager();
  if(manager->isEmpty()) {
    kdWarning() << "Tellico::CalendarHandler::checkCalendar() - adding default calendar" << endl;
    KConfig config(TQString::tqfromLatin1("korganizerrc"));
    config.setGroup("General");
    TQString fileName = config.readPathEntry("Active Calendar");

    TQString resourceName;
    KCal::ResourceCalendar* defaultResource = 0;
    if(fileName.isEmpty()) {
      fileName = locateLocal("appdata", TQString::tqfromLatin1("std.ics"));
      resourceName = i18n("Default Calendar");
      defaultResource = new KCal::ResourceLocal(fileName);
    } else {
      KURL url = KURL::fromPathOrURL(fileName);
      if(url.isLocalFile()) {
        defaultResource = new KCal::ResourceLocal(url.path());
      } else {
//        defaultResource = new KCal::ResourceRemote(url);
        Kernel::self()->sorry(i18n("At the moment, Tellico only supports local calendar resources. "
                                   "The active calendar is remotely located, so your loans will not "
                                   "be added."));
        return false;
      }
      resourceName = i18n("Active Calendar");
    }

    defaultResource->setResourceName(resourceName);

    manager->add(defaultResource);
    manager->setStandardResource(defaultResource);
  }
  return true;
}

void CalendarHandler::populateTodo(KCal::Todo* todo_, Data::LoanPtr loan_) {
  if(!todo_ || !loan_) {
    return;
  }

  todo_->setUid(loan_->uid());

  todo_->setDtStart(loan_->loanDate());
  todo_->setHasStartDate(true);
  todo_->setDtDue(loan_->dueDate());
  todo_->setHasDueDate(true);
  TQString person = loan_->borrower()->name();
  TQString summary = i18n("Tellico: %1 is due to return \"%2\"").tqarg(person).tqarg(loan_->entry()->title());
  todo_->setSummary(summary);
  TQString note = loan_->note();
  if(note.isEmpty()) {
    note = summary;
  }
  todo_->setDescription(note);
  todo_->setSecrecy(KCal::Incidence::SecrecyPrivate); // private by default

  todo_->clearAttendees();
  // not adding email of borrower for now, but request RSVP?
  KCal::Attendee* attendee = new KCal::Attendee(loan_->borrower()->name(),
                                                TQString(), false /*rsvp*/,
                                                KCal::Attendee::NeedsAction,
                                                KCal::Attendee::ReqParticipant,
                                                loan_->borrower()->uid());
  todo_->addAttendee(attendee);

  todo_->clearAlarms();
  KCal::Alarm* alarm = todo_->newAlarm();
  alarm->setDisplayAlarm(summary);
  alarm->setEnabled(true);
}

// taken from kpimprefs.cpp
TQString CalendarHandler::timezone() {
  TQString zone;

  KConfig korgcfg(locate(TQString::tqfromLatin1("config"), TQString::tqfromLatin1("korganizerrc")));
  korgcfg.setGroup("Time & Date");
  TQString tz(korgcfg.readEntry("TimeZoneId"));
  if(!tz.isEmpty()) {
    zone = tz;
  } else {
    char zonefilebuf[PATH_MAX];

    int len = ::readlink("/etc/localtime", zonefilebuf, PATH_MAX);
    if(len > 0 && len < PATH_MAX) {
      zone = TQString::fromLocal8Bit(zonefilebuf, len);
      zone = zone.mid(zone.find(TQString::tqfromLatin1("zoneinfo/")) + 9);
    } else {
      tzset();
      zone = tzname[0];
    }
  }
  return zone;
}

#endif
