/*
    This file is part of libkcal.

    Copyright (c) 2003 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., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include <qdatetime.h>
#include <qstring.h>
#include <qptrlist.h>
#include <qregexp.h>
#include <qclipboard.h>
#include <qfile.h>
#include <qtextstream.h>
#include <qxml.h>

#include <kdebug.h>
#include <klocale.h>

#include "calendar.h"
#include "calendarlocal.h"

#include "qtopiaformat.h"

using namespace KCal;

class QtopiaParser : public QXmlDefaultHandler
{
  public:
    QtopiaParser( Calendar *calendar ) : mCalendar( calendar ) {
        oldCategories = 0;
    }
  
    bool startElement( const QString &, const QString &, const QString & qName, 
                       const QXmlAttributes &attributes )
    {
        if ( qName == "event" ) {
            Event *event = new Event;
            QString uid = "Qtopia" + attributes.value( "uid" );
            // event->setUid( uid );

            event->setSummary( attributes.value( "description" ) );
            event->setLocation( attributes.value( "location" ) );
            event->setDescription( attributes.value( "note" ) );
            event->setDtStart( toDateTime( attributes.value( "start" ) ) );
            event->setDtEnd( toDateTime( attributes.value( "end" ) ) );

            if ( attributes.value( "type" ) == "AllDay" ) {
                event->setFloats( true );
            } else {
                event->setFloats( false );
            }

            QString rtype = attributes.value( "rtype" );
            if ( !rtype.isEmpty() ) {
                QDate startDate = event->dtStart().date();
        
                QString freqStr = attributes.value( "rfreq" );
                int freq = freqStr.toInt();

                QString hasEndDateStr = attributes.value( "rhasenddate" );
                bool hasEndDate = hasEndDateStr == "1";

                QString endDateStr = attributes.value( "enddt" );
                QDate endDate = toDateTime( endDateStr ).date();

                QString weekDaysStr = attributes.value( "rweekdays" );
                int weekDaysNum = weekDaysStr.toInt();
                if ( weekDaysNum == 0 )
                    weekDaysNum = (1 << (event->dtStart().date().dayOfWeek()-1));
               
                QBitArray weekDays( 7 );
                weekDays.fill( false );
                int i;
                for( i = 0; i < 7; ++i ) {
                    weekDays.setBit( i , ( 1 << i ) & weekDaysNum ); 
                    qDebug("%d %d %d ",i, weekDaysNum, weekDays.at(i) );
                }

                QString posStr = attributes.value( "rposition" );
                int pos = posStr.toInt();

                Recurrence *r = event->recurrence();

                if ( rtype == "Daily" ) {
                    if ( hasEndDate ) r->setDaily( freq, endDate );
                    else r->setDaily( freq, -1 );
                } else if ( rtype == "Weekly" ) {
                    // fix needed here
                    // rweekdays not set in XML file
                    if ( hasEndDate ) r->setWeekly( freq, weekDays, endDate );
                    else r->setWeekly( freq, weekDays, -1 );
                } else if ( rtype == "MonthlyDate" ) {
                    if ( hasEndDate )
                        r->setMonthly( Recurrence::rMonthlyDay, freq, endDate );
                    else
                        r->setMonthly( Recurrence::rMonthlyDay, freq, -1 );
                    r->addMonthlyDay( startDate.day() );
                } else if ( rtype == "MonthlyDay" ) {
                    if ( hasEndDate )
                        r->setMonthly( Recurrence::rMonthlyPos, freq, endDate );
                    else
                        r->setMonthly( Recurrence::rMonthlyPos, freq, -1 );
                    QBitArray days( 7 );
                    days.fill( false );
                    days.setBit( startDate.dayOfWeek() - 1 );
                    r->addMonthlyPos( pos, days );
                } else if ( rtype == "Yearly" ) {
                    if ( hasEndDate )
                        r->setYearly( Recurrence::rYearlyMonth, freq, endDate );
                    else
                        r->setYearly( Recurrence::rYearlyMonth, freq, -1 );
                    r->addYearlyNum( startDate.month() );
                }
            }
        
            QString categoryList = attributes.value( "categories" );
            event->setCategories( lookupCategories( categoryList ) );

            QString alarmStr = attributes.value( "alarm" );
            if ( !alarmStr.isEmpty() ) {
                Alarm *alarm = new Alarm( event );
                alarm->setType( Alarm::Display );
                alarm->setEnabled( true );
                int alarmOffset = alarmStr.toInt();
                alarm->setStartOffset( alarmOffset * -60 );
                event->addAlarm( alarm );
            }
            // the following may not be
            //Event *oldEvent = mCalendar->event( uid );
            //if ( oldEvent ) mCalendar->deleteEvent( oldEvent );

            mCalendar->addEventNoDup( event );
        } else if ( qName == "Task" ) {
            Todo *todo = new Todo;

            QString uid = "Qtopia" + attributes.value( "Uid" );
            //todo->setUid( uid );
        
            QString description = attributes.value( "Description" );
            int pos = description.find( '\n' );
            if ( pos > 0 ) {
                QString summary = description.left( pos );
                todo->setSummary( summary );
                todo->setDescription( description );
            } else {
                todo->setSummary( description );
            }
        
            int priority = attributes.value( "Priority" ).toInt();
            if ( priority == 0 ) priority = 3;
            todo->setPriority( priority );
        
            QString categoryList = attributes.value( "Categories" );
            todo->setCategories( lookupCategories( categoryList ) );
        
            QString completedStr = attributes.value( "Completed" );
            if ( completedStr == "1" ) todo->setCompleted( true );
        
            QString hasDateStr = attributes.value( "HasDate" );
            if ( hasDateStr == "1" ) {
                int year = attributes.value( "DateYear" ).toInt();
                int month = attributes.value( "DateMonth" ).toInt();
                int day = attributes.value( "DateDay" ).toInt();
          
                todo->setDtDue( QDateTime( QDate( year, month, day ) ) );
                todo->setHasDueDate( true );
            }
        
            // Todo *oldTodo = mCalendar->todo( uid );
            //if ( oldTodo ) mCalendar->deleteTodo( oldTodo );

            mCalendar->addTodoNoDup( todo );
        } else if ( qName == "Category" ) {
            QString id = attributes.value( "id" );
            QString name = attributes.value( "name" );
            setCategory( id, name );
        }
      
        return true;
    }

    bool warning ( const QXmlParseException &exception )
    {
      printException( exception );
      return true;
    }
 
    bool error ( const QXmlParseException &exception )
    {
      printException( exception );
      return false;
    }
 
    bool fatalError ( const QXmlParseException &exception )
    {
      printException( exception );
      return false;
    }
 
    QString errorString ()
    {
      return "QtopiaParser: Error!";
    }
    void  setCategoriesList ( QStringList * c )
    {
        oldCategories = c;
    }

  protected:
    void printException( const QXmlParseException &exception )
    {
      kdError() << "XML Parse Error (line " << exception.lineNumber()
                << ", col " << exception.columnNumber() << "): "
                << exception.message() << "(public ID: '"
                << exception.publicId() << "' system ID: '"
                << exception.systemId() << "')" << endl;
    }

    QDateTime toDateTime( const QString &value )
    {
      QDateTime dt;
      dt.setTime_t( value.toUInt() );
      
      return dt;
    }

    QStringList lookupCategories( const QString &categoryList )
    {
      QStringList categoryIds = QStringList::split( ";", categoryList );
      QStringList categories;
      QStringList::ConstIterator it;
      for( it = categoryIds.begin(); it != categoryIds.end(); ++it ) {
          QString cate = category( *it );
          if ( oldCategories ) {
              if ( ! oldCategories->contains( cate ) )
                  oldCategories->append( cate );
              }
          categories.append(cate );
      }
      return categories;
    }

  private:
    Calendar *mCalendar;
    QStringList * oldCategories;
    static QString category( const QString &id )
    {
      QMap<QString,QString>::ConstIterator it = mCategoriesMap.find( id );
      if ( it == mCategoriesMap.end() ) return id;
      else return *it;
    }

    static void setCategory( const QString &id, const QString &name )
    {
      mCategoriesMap.insert( id, name );
    }

    static QMap<QString,QString> mCategoriesMap;
};

QMap<QString,QString> QtopiaParser::mCategoriesMap;

QtopiaFormat::QtopiaFormat()
{
    mCategories = 0;
}

QtopiaFormat::~QtopiaFormat()
{
}
#include <qdom.h> 
bool QtopiaFormat::load( Calendar *calendar, const QString &fileName )
{
  clearException();
  // qDebug("load QtopiaFormat: %s ",fileName.latin1() );
  QtopiaParser handler( calendar );
  handler.setCategoriesList( mCategories );
  QFile xmlFile( fileName );
  QXmlInputSource source( xmlFile );
  QXmlSimpleReader reader;
  reader.setContentHandler( &handler );
  return reader.parse( source );
}

bool QtopiaFormat::save( Calendar *calendar, const QString &fileName )
{

  clearException();

  QString text = toString( calendar );

  if ( text.isNull() ) return false;

  // TODO: write backup file

  QFile file( fileName );
  if (!file.open( IO_WriteOnly ) ) {
    setException(new ErrorFormat(ErrorFormat::SaveError,
                 i18n("Could not open file '%1'").arg(fileName)));
    return false;
  }
  QTextStream ts( &file );
  ts << text;
  file.close();

  return true;
}

bool QtopiaFormat::fromString( Calendar *, const QString & )
{
 
  return false;
}

QString QtopiaFormat::toString( Calendar * )
{
  return QString::null;
}