/*
    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 <kdebug.h>

#include "incidence.h"
#include "todo.h"

#include "alarm.h"

using namespace KCal;

Alarm::Alarm(Incidence *parent)
 : mParent(parent),
   mType(Invalid),
   mDescription(""),    // to make operator==() not fail
   mFile(""),           // to make operator==() not fail
   mMailSubject(""),    // to make operator==() not fail
   mAlarmSnoozeTime(5),
   mAlarmRepeatCount(0),
   mEndOffset(false),
   mHasTime(false),
   mAlarmEnabled(false)
{
}

Alarm::~Alarm()
{
}

Alarm *Alarm::clone()
{
  return new Alarm( *this );
}

Alarm &Alarm::operator=( const Alarm &a )
{
  mParent = a.mParent;
  mType = a.mType;
  mDescription = a.mDescription;
  mFile = a.mFile;
  mMailAttachFiles = a.mMailAttachFiles;
  mMailAddresses = a.mMailAddresses;
  mMailSubject = a.mMailSubject;
  mAlarmSnoozeTime = a.mAlarmSnoozeTime;
  mAlarmRepeatCount = a.mAlarmRepeatCount;
  mAlarmTime = a.mAlarmTime;
  mOffset = a.mOffset;
  mEndOffset = a.mEndOffset;
  mHasTime = a.mHasTime;
  mAlarmEnabled = a.mAlarmEnabled;
  return *this;
}

bool Alarm::operator==( const Alarm& rhs ) const
{
  if ( mType != rhs.mType ||
       mAlarmSnoozeTime != rhs.mAlarmSnoozeTime ||
       mAlarmRepeatCount != rhs.mAlarmRepeatCount ||
       mAlarmEnabled != rhs.mAlarmEnabled ||
       mHasTime != rhs.mHasTime)
    return false;

  if (mHasTime) {
    if (mAlarmTime != rhs.mAlarmTime)
      return false;
  } else {
    if (mOffset != rhs.mOffset ||
        mEndOffset != rhs.mEndOffset)
      return false;
  }

  switch (mType) {
    case Display:
      return mDescription == rhs.mDescription;

    case Email:
      return mDescription == rhs.mDescription &&
             mMailAttachFiles == rhs.mMailAttachFiles &&
             mMailAddresses == rhs.mMailAddresses &&
             mMailSubject == rhs.mMailSubject;

    case Procedure:
      return mFile == rhs.mFile &&
             mDescription == rhs.mDescription;

    case Audio:
      return mFile == rhs.mFile;

    case Invalid:
      break;
  }
  return false;
}

void Alarm::setType(Alarm::Type type)
{
  if (type == mType)
    return;

  switch (type) {
    case Display:
      mDescription = "";
      break;
    case Procedure:
      mFile = mDescription = "";
      break;
    case Audio:
      mFile = "";
      break;
    case Email:
      mMailSubject = mDescription = "";
      mMailAddresses.clear();
      mMailAttachFiles.clear();
      break;
    case Invalid:
      break;
    default:
      return;
  }
  mType = type;
  if ( mParent ) mParent->updated();
}

Alarm::Type Alarm::type() const
{
  return mType;
}

void Alarm::setAudioAlarm(const TQString &audioFile)
{
  mType = Audio;
  mFile = audioFile;
  if ( mParent ) mParent->updated();
}

void Alarm::setAudioFile(const TQString &audioFile)
{
  if (mType == Audio) {
    mFile = audioFile;
    if ( mParent ) mParent->updated();
  }
}

TQString Alarm::audioFile() const
{
  return (mType == Audio) ? mFile : TQString();
}

void Alarm::setProcedureAlarm(const TQString &programFile, const TQString &arguments)
{
  mType = Procedure;
  mFile = programFile;
  mDescription = arguments;
  if ( mParent ) mParent->updated();
}

void Alarm::setProgramFile(const TQString &programFile)
{
  if (mType == Procedure) {
    mFile = programFile;
    if ( mParent ) mParent->updated();
  }
}

TQString Alarm::programFile() const
{
  return (mType == Procedure) ? mFile : TQString();
}

void Alarm::setProgramArguments(const TQString &arguments)
{
  if (mType == Procedure) {
    mDescription = arguments;
    if ( mParent ) mParent->updated();
  }
}

TQString Alarm::programArguments() const
{
  return (mType == Procedure) ? mDescription : TQString();
}

void Alarm::setEmailAlarm(const TQString &subject, const TQString &text,
                          const TQValueList<Person> &addressees, const TQStringList &attachments)
{
  mType = Email;
  mMailSubject = subject;
  mDescription = text;
  mMailAddresses = addressees;
  mMailAttachFiles = attachments;
  if ( mParent ) mParent->updated();
}

void Alarm::setMailAddress(const Person &mailAddress)
{
  if (mType == Email) {
    mMailAddresses.clear();
    mMailAddresses += mailAddress;
    if ( mParent ) mParent->updated();
  }
}

void Alarm::setMailAddresses(const TQValueList<Person> &mailAddresses)
{
  if (mType == Email) {
    mMailAddresses = mailAddresses;
    if ( mParent ) mParent->updated();
  }
}

void Alarm::addMailAddress(const Person &mailAddress)
{
  if (mType == Email) {
    mMailAddresses += mailAddress;
    if ( mParent ) mParent->updated();
  }
}

TQValueList<Person> Alarm::mailAddresses() const
{
  return (mType == Email) ? mMailAddresses : TQValueList<Person>();
}

void Alarm::setMailSubject(const TQString &mailAlarmSubject)
{
  if (mType == Email) {
    mMailSubject = mailAlarmSubject;
    if ( mParent ) mParent->updated();
  }
}

TQString Alarm::mailSubject() const
{
  return (mType == Email) ? mMailSubject : TQString();
}

void Alarm::setMailAttachment(const TQString &mailAttachFile)
{
  if (mType == Email) {
    mMailAttachFiles.clear();
    mMailAttachFiles += mailAttachFile;
    if ( mParent ) mParent->updated();
  }
}

void Alarm::setMailAttachments(const TQStringList &mailAttachFiles)
{
  if (mType == Email) {
    mMailAttachFiles = mailAttachFiles;
    if ( mParent ) mParent->updated();
  }
}

void Alarm::addMailAttachment(const TQString &mailAttachFile)
{
  if (mType == Email) {
    mMailAttachFiles += mailAttachFile;
    if ( mParent ) mParent->updated();
  }
}

TQStringList Alarm::mailAttachments() const
{
  return (mType == Email) ? mMailAttachFiles : TQStringList();
}

void Alarm::setMailText(const TQString &text)
{
  if (mType == Email) {
    mDescription = text;
    if ( mParent ) mParent->updated();
  }
}

TQString Alarm::mailText() const
{
  return (mType == Email) ? mDescription : TQString();
}

void Alarm::setDisplayAlarm(const TQString &text)
{
  mType = Display;
  if ( !text.isNull() )
    mDescription = text;
  if ( mParent ) mParent->updated();
}

void Alarm::setText(const TQString &text)
{
  if (mType == Display) {
    mDescription = text;
    if ( mParent ) mParent->updated();
  }
}

TQString Alarm::text() const
{
  return (mType == Display) ? mDescription : TQString();
}

void Alarm::setTime(const TQDateTime &alarmTime)
{
  mAlarmTime = alarmTime;
  mHasTime = true;

  if ( mParent ) mParent->updated();
}

TQDateTime Alarm::time() const
{
  if ( hasTime() ) {
    return mAlarmTime;
  } else if ( mParent ) {
    if ( mEndOffset ) {
      if ( mParent->type() == "Todo" ) {
        Todo *t = static_cast<Todo*>( mParent );
        return mOffset.end( t->dtDue() );
      } else {
        return mOffset.end( mParent->dtEnd() );
      }
    } else {
      return mOffset.end( mParent->dtStart() );
    }
  } else {
    return TQDateTime();
  }
}

bool Alarm::hasTime() const
{
  return mHasTime;
}

void Alarm::setSnoozeTime(const Duration &alarmSnoozeTime)
{
  if (alarmSnoozeTime.value() > 0) {
    mAlarmSnoozeTime = alarmSnoozeTime;
    if ( mParent ) mParent->updated();
  }
}

Duration Alarm::snoozeTime() const
{
  return mAlarmSnoozeTime;
}

void Alarm::setRepeatCount(int alarmRepeatCount)
{
  mAlarmRepeatCount = alarmRepeatCount;
  if ( mParent ) mParent->updated();
}

int Alarm::repeatCount() const
{
  return mAlarmRepeatCount;
}

Duration Alarm::duration() const
{
  return Duration( mAlarmSnoozeTime.value() * mAlarmRepeatCount,
                   mAlarmSnoozeTime.type() );
}

TQDateTime Alarm::nextRepetition(const TQDateTime& preTime) const
{
  // This method is coded to avoid 32-bit integer overflow using
  // TQDateTime::secsTo(), which occurs with time spans > 68 years.
  TQDateTime at = time();
  if (at > preTime)
    return at;
  if (!mAlarmRepeatCount)
    return TQDateTime();   // there isn't an occurrence after the specified time
  int snoozeSecs = mAlarmSnoozeTime * 60;
  TQDateTime lastRepetition = at.addSecs(mAlarmRepeatCount * snoozeSecs);
  if (lastRepetition <= preTime)
    return TQDateTime();    // all repetitions have finished before the specified time
  int repetition = (at.secsTo(preTime) + snoozeSecs) / snoozeSecs;
  return at.addSecs(repetition * snoozeSecs);
}

TQDateTime Alarm::previousRepetition(const TQDateTime& afterTime) const
{
  // This method is coded to avoid 32-bit integer overflow using
  // TQDateTime::secsTo(), which occurs with time spans > 68 years.
  TQDateTime at = time();
  if (at >= afterTime)
    return TQDateTime();    // alarm's first/only time is at/after the specified time
  if (!mAlarmRepeatCount)
    return at;
  int snoozeSecs = mAlarmSnoozeTime * 60;
  TQDateTime lastRepetition = at.addSecs(mAlarmRepeatCount * snoozeSecs);
  if (lastRepetition < afterTime)
    return lastRepetition;   // all repetitions have finished before the specified time
  int repetition = (at.secsTo(afterTime) - 1) / snoozeSecs;
  return at.addSecs(repetition * snoozeSecs);
}

TQDateTime Alarm::endTime() const
{
  if (mAlarmRepeatCount)
    return time().addSecs(mAlarmRepeatCount * mAlarmSnoozeTime * 60);
  else
    return time();
}

void Alarm::toggleAlarm()
{
  mAlarmEnabled = !mAlarmEnabled;
  if ( mParent ) mParent->updated();
}

void Alarm::setEnabled(bool enable)
{
  mAlarmEnabled = enable;
  if ( mParent ) mParent->updated();
}

bool Alarm::enabled() const
{
  return mAlarmEnabled;
}

void Alarm::setStartOffset( const Duration &offset )
{
  mOffset = offset;
  mEndOffset = false;
  mHasTime = false;
  if ( mParent ) mParent->updated();
}

Duration Alarm::startOffset() const
{
  return (mHasTime || mEndOffset) ? Duration( 0 ) : mOffset;
}

bool Alarm::hasStartOffset() const
{
  return !mHasTime && !mEndOffset;
}

bool Alarm::hasEndOffset() const
{
  return !mHasTime && mEndOffset;
}

void Alarm::setEndOffset( const Duration &offset )
{
  mOffset = offset;
  mEndOffset = true;
  mHasTime = false;
  if ( mParent ) mParent->updated();
}

Duration Alarm::endOffset() const
{
  return (mHasTime || !mEndOffset) ? Duration( 0 ) : mOffset;
}

void Alarm::setParent( Incidence *parent )
{
  mParent = parent;
}

void Alarm::customPropertyUpdated()
{
  if ( mParent ) mParent->updated();
}