/*
    This file is part of oxaccess.

    Copyright (c) 2009 Tobias Koenig <tokoe@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 "contactutils.h"

#include "davutils.h"
#include "oxutils.h"
#include "users.h"

#include <akonadi/contact/contactgroupexpandjob.h>

#include <QtCore/QBuffer>
#include <QtXml/QDomElement>

using namespace OXA;

void OXA::ContactUtils::parseContact( const QDomElement &propElement, Object &object )
{
  bool isDistributionList = false;
  QDomElement distributionListElement = propElement.firstChildElement( QLatin1String("distributionlist_flag") );
  if ( !distributionListElement.isNull() ) {
    if ( OXUtils::readBoolean( distributionListElement.text() ) == true )
      isDistributionList = true;
  }

  if ( isDistributionList ) {
    KABC::ContactGroup contactGroup;

    QDomElement element = propElement.firstChildElement();
    while ( !element.isNull() ) {
      const QString tagName = element.tagName();
      const QString text = OXUtils::readString( element.text() );

      if ( tagName == QLatin1String( "displayname" ) ) {
        contactGroup.setName( text );
      } else if ( tagName == QLatin1String( "distributionlist" ) ) {
        QDomElement emailElement = element.firstChildElement();
        while ( !emailElement.isNull() ) {
          const QString tagName = emailElement.tagName();
          const QString text = OXUtils::readString( emailElement.text() );

          if ( tagName == QLatin1String( "email" ) ) {
            const int emailField = OXUtils::readNumber( emailElement.attribute( QLatin1String( "emailfield" ) ) );
            if ( emailField == 0 ) { // internal data
              KABC::ContactGroup::Data data;
              data.setName( OXUtils::readString( emailElement.attribute( QLatin1String( "displayname" ) ) ) );
              data.setEmail( text );

              contactGroup.append( data );
            } else { // external reference
              // we convert them to internal data, seems like a more stable approach
              KABC::ContactGroup::Data data;
              const qlonglong uid = OXUtils::readNumber( emailElement.attribute( QLatin1String( "id" ) ) );

              const User user = Users::self()->lookupUid( uid );
              if ( user.isValid() ) {
                data.setName( user.name() );
                data.setEmail( user.email() );
              } else {
                // fallback: use the data from the element
                data.setName( OXUtils::readString( emailElement.attribute( QLatin1String( "displayname" ) ) ) );
                data.setEmail( text );
              }

              contactGroup.append( data );
            }
          }

          emailElement = emailElement.nextSiblingElement();
        }
      }
      element = element.nextSiblingElement();
    }

    object.setContactGroup( contactGroup );
  } else {
    KABC::Addressee contact;
    KABC::Address homeAddress( KABC::Address::Home );
    KABC::Address workAddress( KABC::Address::Work );
    KABC::Address otherAddress( KABC::Address::Dom );

    QDomElement element = propElement.firstChildElement();
    while ( !element.isNull() ) {
      const QString tagName = element.tagName();
      const QString text = OXUtils::readString( element.text() );

      // name
      if ( tagName == QLatin1String( "title" ) ) {
        contact.setTitle( text );
      } else if ( tagName == QLatin1String( "first_name" ) ) {
        contact.setGivenName( text );
      } else if ( tagName == QLatin1String( "second_name" ) ) {
        contact.setAdditionalName( text );
      } else if ( tagName == QLatin1String( "last_name" ) ) {
        contact.setFamilyName( text );
      } else if ( tagName == QLatin1String( "suffix" ) ) {
        contact.setSuffix( text );
      } else if ( tagName == QLatin1String( "displayname" ) ) {
        contact.setFormattedName( text );
      } else if ( tagName == QLatin1String( "nickname" ) ) {
        contact.setNickName( text );
      // dates
      } else if ( tagName == QLatin1String( "birthday" ) ) {
        contact.setBirthday( OXUtils::readDateTime( element.text() ) );
      } else if ( tagName == QLatin1String( "anniversary" ) ) {
        contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-Anniversary" ), OXUtils::readDateTime( element.text() ).toString( Qt::ISODate ) );
      } else if ( tagName == QLatin1String( "spouse_name" ) ) {
        contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-SpousesName" ), text );
      // addresses
      } else if ( tagName == QLatin1String( "street" ) ) {
        homeAddress.setStreet( text );
      } else if ( tagName == QLatin1String( "postal_code" ) ) {
        homeAddress.setPostalCode( text );
      } else if ( tagName == QLatin1String( "city" ) ) {
        homeAddress.setLocality( text );
      } else if ( tagName == QLatin1String( "country" ) ) {
        homeAddress.setCountry( text );
      } else if ( tagName == QLatin1String( "state" ) ) {
        homeAddress.setRegion( text );
      } else if ( tagName == QLatin1String( "business_street" ) ) {
        workAddress.setStreet( text );
      } else if ( tagName == QLatin1String( "business_postal_code" ) ) {
        workAddress.setPostalCode( text );
      } else if ( tagName == QLatin1String( "business_city" ) ) {
        workAddress.setLocality( text );
      } else if ( tagName == QLatin1String( "business_country" ) ) {
        workAddress.setCountry( text );
      } else if ( tagName == QLatin1String( "business_state" ) ) {
        workAddress.setRegion( text );
      } else if ( tagName == QLatin1String( "second_street" ) ) {
        otherAddress.setStreet( text );
      } else if ( tagName == QLatin1String( "second_postal_code" ) ) {
        otherAddress.setPostalCode( text );
      } else if ( tagName == QLatin1String( "second_city" ) ) {
        otherAddress.setLocality( text );
      } else if ( tagName == QLatin1String( "second_country" ) ) {
        otherAddress.setCountry( text );
      } else if ( tagName == QLatin1String( "second_state" ) ) {
        otherAddress.setRegion( text );
      } else if ( tagName == QLatin1String( "defaultaddress" ) ) {
        const int number = text.toInt();
        if ( number == 1 )
          workAddress.setType( workAddress.type() | KABC::Address::Pref );
        else if ( number == 2 )
          homeAddress.setType( homeAddress.type() | KABC::Address::Pref );
        else if ( number == 3 )
          otherAddress.setType( otherAddress.type() | KABC::Address::Pref );
      // further information
      } else if ( tagName == QLatin1String( "note" ) ) {
        contact.setNote( text );
      } else if ( tagName == QLatin1String( "url" ) ) {
        contact.setUrl( text );
      } else if ( tagName == QLatin1String( "image1" ) ) {
        const QByteArray data = text.toUtf8();
        contact.setPhoto( KABC::Picture( QImage::fromData( QByteArray::fromBase64( data ) ) ) );
      // company information
      } else if ( tagName == QLatin1String( "company" ) ) {
        contact.setOrganization( text );
      } else if ( tagName == QLatin1String( "department" ) ) {
        contact.setDepartment( text );
      } else if ( tagName == QLatin1String( "assistants_name" ) ) {
        contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-AssistantsName" ), text );
      } else if ( tagName == QLatin1String( "managers_name" ) ) {
        contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-ManagersName" ), text );
      } else if ( tagName == QLatin1String( "position" ) ) {
        contact.setRole( text );
      } else if ( tagName == QLatin1String( "profession" ) ) {
        contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-Profession" ), text );
      } else if ( tagName == QLatin1String( "room_number" ) ) {
        contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-Office" ), text );
      // communication
      } else if ( tagName == QLatin1String( "email1" ) ) {
        contact.insertEmail( text, true );
      } else if ( tagName == QLatin1String( "email2" ) ||
                  tagName == QLatin1String( "email3" ) ) {
        contact.insertEmail( text );
      } else if ( tagName == QLatin1String( "mobile1" ) ) {
        contact.insertPhoneNumber( KABC::PhoneNumber( text, KABC::PhoneNumber::Cell ) );
      } else if ( tagName == QLatin1String( "instant_messenger" ) ) {
        contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-IMAddress" ), text );
      } else if ( tagName.startsWith( QLatin1String( "phone_" ) ) ) {
        KABC::PhoneNumber number;
        number.setNumber( text );
        bool supportedType = false;

        if ( tagName.endsWith( QLatin1String( "_business" ) ) ) {
          number.setType( KABC::PhoneNumber::Work );
          supportedType = true;
        } else if ( tagName.endsWith( QLatin1String( "_home" ) ) ) {
          number.setType( KABC::PhoneNumber::Home );
          supportedType = true;
        } else if ( tagName.endsWith( QLatin1String( "_other" ) ) ) {
          number.setType( KABC::PhoneNumber::Voice );
          supportedType = true;
        } else if ( tagName.endsWith( QLatin1String( "_car" ) ) ) {
          number.setType( KABC::PhoneNumber::Car );
          supportedType = true;
        }

        if ( supportedType )
          contact.insertPhoneNumber( number );
      } else if ( tagName.startsWith( QLatin1String( "fax_" ) ) ) {
        KABC::PhoneNumber number;
        number.setNumber( text );
        bool supportedType = false;

        if ( tagName.endsWith( QLatin1String( "_business" ) ) ) {
          number.setType( KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work );
          supportedType = true;
        } else if ( tagName.endsWith( QLatin1String( "_home" ) ) ) {
          number.setType( KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home );
          supportedType = true;
        } else if ( tagName.endsWith( QLatin1String( "_other" ) ) ) {
          number.setType( KABC::PhoneNumber::Fax | KABC::PhoneNumber::Voice );
          supportedType = true;
        }

        if ( supportedType )
          contact.insertPhoneNumber( number );
      } else if ( tagName == QLatin1String( "pager" ) ) {
        contact.insertPhoneNumber( KABC::PhoneNumber( text, KABC::PhoneNumber::Pager ) );
      } else if ( tagName == QLatin1String( "categories" ) ) {
        contact.setCategories( text.split( QRegExp( QLatin1String( ",\\s*" ) ) ) );
      }

      element = element.nextSiblingElement();
    }

    if ( !homeAddress.isEmpty() )
      contact.insertAddress( homeAddress );
    if ( !workAddress.isEmpty() )
      contact.insertAddress( workAddress );
    if ( !otherAddress.isEmpty() )
      contact.insertAddress( otherAddress );

    object.setContact( contact );
  }
}

void OXA::ContactUtils::addContactElements( QDomDocument &document, QDomElement &propElement, const Object &object, void *preloadedData )
{
  if ( !object.contact().isEmpty() ) {
    // it is a contact payload

    const KABC::Addressee contact = object.contact();

    // name
    DAVUtils::addOxElement( document, propElement, QLatin1String( "title" ), OXUtils::writeString( contact.title() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "first_name" ), OXUtils::writeString( contact.givenName() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "second_name" ), OXUtils::writeString( contact.additionalName() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "last_name" ), OXUtils::writeString( contact.familyName() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "suffix" ), OXUtils::writeString( contact.suffix() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "displayname" ), OXUtils::writeString( contact.formattedName() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "nickname" ), OXUtils::writeString( contact.nickName() ) );

    // dates
    if ( contact.birthday().date().isValid() )
      DAVUtils::addOxElement( document, propElement, QLatin1String( "birthday" ), OXUtils::writeDate( contact.birthday().date() ) );
    else
      DAVUtils::addOxElement( document, propElement, QLatin1String( "birthday" ) );

    // since QDateTime::to/fromString() doesn't carry timezone information, we have to fake it here
    const QDate anniversary = QDate::fromString( contact.custom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-Anniversary" ) ), Qt::ISODate );
    if ( anniversary.isValid() )
      DAVUtils::addOxElement( document, propElement, QLatin1String( "anniversary" ), OXUtils::writeDate( anniversary ) );
    else
      DAVUtils::addOxElement( document, propElement, QLatin1String( "anniversary" ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "spouse_name" ), OXUtils::writeString( contact.custom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-SpousesName" ) ) ) );

    // addresses
    const KABC::Address homeAddress = contact.address( KABC::Address::Home );
    if ( !homeAddress.isEmpty() ) {
      DAVUtils::addOxElement( document, propElement, QLatin1String( "street" ), OXUtils::writeString( homeAddress.street() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "postal_code" ), OXUtils::writeString( homeAddress.postalCode() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "city" ), OXUtils::writeString( homeAddress.locality() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "state" ), OXUtils::writeString( homeAddress.region() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "country" ), OXUtils::writeString( homeAddress.country() ) );
    }
    const KABC::Address workAddress = contact.address( KABC::Address::Work );
    if ( !workAddress.isEmpty() ) {
      DAVUtils::addOxElement( document, propElement, QLatin1String( "business_street" ), OXUtils::writeString( workAddress.street() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "business_postal_code" ), OXUtils::writeString( workAddress.postalCode() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "business_city" ), OXUtils::writeString( workAddress.locality() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "business_state" ), OXUtils::writeString( workAddress.region() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "business_country" ), OXUtils::writeString( workAddress.country() ) );
    }
    const KABC::Address otherAddress = contact.address( KABC::Address::Dom );
    if ( !otherAddress.isEmpty() ) {
      DAVUtils::addOxElement( document, propElement, QLatin1String( "second_street" ), OXUtils::writeString( otherAddress.street() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "second_postal_code" ), OXUtils::writeString( otherAddress.postalCode() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "second_city" ), OXUtils::writeString( otherAddress.locality() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "second_state" ), OXUtils::writeString( otherAddress.region() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "second_country" ), OXUtils::writeString( otherAddress.country() ) );
    }

    // further information
    DAVUtils::addOxElement( document, propElement, QLatin1String( "note" ), OXUtils::writeString( contact.note() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "url" ), OXUtils::writeString( contact.url().url() ) );

    // image
    const KABC::Picture photo = contact.photo();
    if ( !photo.data().isNull() ) {
      QByteArray imageData;
      QBuffer buffer( &imageData );
      buffer.open( QIODevice::WriteOnly );

      QString contentType;
      if ( !photo.data().hasAlphaChannel() ) {
        photo.data().save( &buffer, "JPEG" );
        contentType = QLatin1String( "image/jpg" );
      } else {
        photo.data().save( &buffer, "PNG" );
        contentType = QLatin1String( "image/png" );
      }

      buffer.close();

      DAVUtils::addOxElement( document, propElement, QLatin1String( "image1" ), QString::fromLatin1( imageData.toBase64() ) );
      DAVUtils::addOxElement( document, propElement, QLatin1String( "image_content_type" ), contentType );
    } else {
      DAVUtils::addOxElement( document, propElement, QLatin1String( "image1" ) );
    }

    // company information
    DAVUtils::addOxElement( document, propElement, QLatin1String( "company" ), OXUtils::writeString( contact.organization() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "department" ), OXUtils::writeString( contact.department() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "assistants_name" ), OXUtils::writeString( contact.custom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-AssistantsName" ) ) ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "managers_name" ), OXUtils::writeString( contact.custom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-ManagersName" ) ) ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "position" ), OXUtils::writeString( contact.role() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "profession" ), OXUtils::writeString( contact.custom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-Profession" ) ) ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "room_number" ), OXUtils::writeString( contact.custom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-Office" ) ) ) );

    // communication
    const QStringList emails = contact.emails();
    for ( int i = 0; i < 3 && i < emails.count(); ++i ) {
      DAVUtils::addOxElement( document, propElement, QString::fromLatin1( "email%1" ).arg( i + 1 ), OXUtils::writeString( emails.at( i ) ) );
    }

    DAVUtils::addOxElement( document, propElement, QLatin1String( "mobile1" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Cell ).number() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "instant_messenger" ), OXUtils::writeString( contact.custom( QLatin1String( "KADDRESSBOOK" ), QLatin1String( "X-IMAddress" ) ) ) );

    DAVUtils::addOxElement( document, propElement, QLatin1String( "phone_business" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Work ).number() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "phone_home" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Home ).number() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "phone_other" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Voice ).number() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "phone_car" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Car ).number() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "fax_business" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ).number() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "fax_home" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ).number() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "fax_other" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Fax | KABC::PhoneNumber::Voice ).number() ) );

    DAVUtils::addOxElement( document, propElement, QLatin1String( "pager" ), OXUtils::writeString( contact.phoneNumber( KABC::PhoneNumber::Pager ).number() ) );

    DAVUtils::addOxElement( document, propElement, QLatin1String( "categories" ), OXUtils::writeString( contact.categories().join( QLatin1String( "," ) ) ) );
  } else {
    // it is a distribution list payload

    const KABC::ContactGroup contactGroup = object.contactGroup();

    DAVUtils::addOxElement( document, propElement, QLatin1String( "displayname" ), OXUtils::writeString( contactGroup.name() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "last_name" ), OXUtils::writeString( contactGroup.name() ) );
    DAVUtils::addOxElement( document, propElement, QLatin1String( "distributionlist_flag" ), OXUtils::writeBoolean( true ) );

    QDomElement distributionList = DAVUtils::addOxElement( document, propElement, QLatin1String( "distributionlist" ) );

    if ( preloadedData ) {
      // the contact group contains contact references that has been preloaded
      KABC::Addressee::List *contacts = static_cast<KABC::Addressee::List*>( preloadedData );
      foreach ( const KABC::Addressee &contact, *contacts ) {
        QDomElement email = DAVUtils::addOxElement( document, distributionList, QLatin1String( "email" ),
                                                    OXUtils::writeString( contact.preferredEmail() ) );

        DAVUtils::setOxAttribute( email, QLatin1String( "folder_id" ), OXUtils::writeNumber( 0 ) );
        DAVUtils::setOxAttribute( email, QLatin1String( "emailfield" ), OXUtils::writeNumber( 0 ) );
        DAVUtils::setOxAttribute( email, QLatin1String( "id" ), OXUtils::writeNumber( 0 ) );
        DAVUtils::setOxAttribute( email, QLatin1String( "displayname" ), OXUtils::writeString( contact.realName() ) );
      }

      delete contacts;
    } else {
      // the contact group contains only internal contact data
      for ( uint i = 0; i < contactGroup.dataCount(); ++i ) {
        const KABC::ContactGroup::Data &data = contactGroup.data( i );
        QDomElement email = DAVUtils::addOxElement( document, distributionList, QLatin1String( "email" ),
                                                    OXUtils::writeString( data.email() ) );

        DAVUtils::setOxAttribute( email, QLatin1String( "folder_id" ), OXUtils::writeNumber( 0 ) );
        DAVUtils::setOxAttribute( email, QLatin1String( "emailfield" ), OXUtils::writeNumber( 0 ) );
        DAVUtils::setOxAttribute( email, QLatin1String( "id" ), OXUtils::writeNumber( 0 ) );
        DAVUtils::setOxAttribute( email, QLatin1String( "displayname" ), OXUtils::writeString( data.name() ) );
      }
    }
  }
}

KJob* OXA::ContactUtils::preloadJob( const Object &object )
{
  Akonadi::ContactGroupExpandJob *job = new Akonadi::ContactGroupExpandJob( object.contactGroup() );
  return job;
}

void* OXA::ContactUtils::preloadData( const Object&, KJob *job )
{
  Akonadi::ContactGroupExpandJob *expandJob = qobject_cast<Akonadi::ContactGroupExpandJob*>( job );
  Q_ASSERT( expandJob );

  return new KABC::Addressee::List( expandJob->contacts() );
}
