/*
  This file is part of the kcal library.

  Copyright (c) 2002,2006,2010 David Jarvie <djarvie@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.
*/
/**
  @file
  This file is part of the API for handling calendar data and
  defines the CustomProperties class.

  @brief
  A class to manage custom calendar properties.

  @author David Jarvie \<djarvie@kde.org\>
*/

#include "customproperties.h"
#include <kdebug.h>
#include <QtCore/QByteArray>

using namespace KCal;

//@cond PRIVATE
static bool checkName( const QByteArray &name );

class CustomProperties::Private
{
  public:
    bool operator==( const Private &other ) const;
    QMap<QByteArray, QString> mProperties;   // custom calendar properties
};

bool CustomProperties::Private::operator==( const CustomProperties::Private &other ) const
{
  if ( mProperties.count() != other.mProperties.count() ) {
    return false;
  }
  for ( QMap<QByteArray, QString>::ConstIterator it = mProperties.begin();
        it != mProperties.end(); ++it ) {
    QMap<QByteArray, QString>::ConstIterator itOther =
               other.mProperties.find( it.key() );
    if ( itOther == other.mProperties.end() || itOther.value() != it.value() ) {
      return false;
    }
  }
  return true;
}
//@endcond

CustomProperties::CustomProperties()
  : d( new Private )
{
}

CustomProperties::CustomProperties( const CustomProperties &cp )
  : d( new Private( *cp.d ) )
{
}

CustomProperties &CustomProperties::operator=( const CustomProperties &other )
{
  // check for self assignment
  if ( &other == this ) {
    return *this;
  }

  *d = *other.d;
  return *this;
}

CustomProperties::~CustomProperties()
{
  delete d;
}

bool CustomProperties::operator==( const CustomProperties &other ) const
{
  return *d == *other.d;
}

void CustomProperties::setCustomProperty( const QByteArray &app, const QByteArray &key,
                                          const QString &value )
{
  if ( value.isNull() || key.isEmpty() || app.isEmpty() ) {
    return;
  }
  QByteArray property = "X-KDE-" + app + '-' + key;
  if ( !checkName( property ) ) {
    return;
  }
  d->mProperties[property] = value;
  customPropertyUpdated();
}

void CustomProperties::removeCustomProperty( const QByteArray &app, const QByteArray &key )
{
  removeNonKDECustomProperty( QByteArray( "X-KDE-" + app + '-' + key ) );
}

QString CustomProperties::customProperty( const QByteArray &app, const QByteArray &key ) const
{
  return nonKDECustomProperty( QByteArray( "X-KDE-" + app + '-' + key ) );
}

QByteArray CustomProperties::customPropertyName( const QByteArray &app, const QByteArray &key )
{
  QByteArray property( "X-KDE-" + app + '-' + key );
  if ( !checkName( property ) ) {
    return QByteArray();
  }
  return property;
}

void CustomProperties::setNonKDECustomProperty( const QByteArray &name, const QString &value )
{
  if ( value.isNull() || !checkName( name ) ) {
    return;
  }
  d->mProperties[name] = value;
  customPropertyUpdated();
}

void CustomProperties::removeNonKDECustomProperty( const QByteArray &name )
{
  if ( d->mProperties.remove( name ) ) {
    customPropertyUpdated();
  }
}

QString CustomProperties::nonKDECustomProperty( const QByteArray &name ) const
{
  return d->mProperties.value( name );
}

void CustomProperties::setCustomProperties( const QMap<QByteArray, QString> &properties )
{
  bool changed = false;
  for ( QMap<QByteArray, QString>::ConstIterator it = properties.begin();
        it != properties.end();  ++it ) {
    // Validate the property name and convert any null string to empty string
    if ( checkName( it.key() ) ) {
      d->mProperties[it.key()] = it.value().isNull() ? QString( "" ) : it.value();
      changed = true;
    }
  }
  if ( changed ) {
    customPropertyUpdated();
  }
}

QMap<QByteArray, QString> CustomProperties::customProperties() const
{
  return d->mProperties;
}

//@cond PRIVATE
bool checkName( const QByteArray &name )
{
  // Check that the property name starts with 'X-' and contains
  // only the permitted characters
  const char *n = name;
  int len = name.length();
  if ( len < 2 ||  n[0] != 'X' || n[1] != '-' ) {
    return false;
  }
  for ( int i = 2;  i < len;  ++i ) {
    char ch = n[i];
    if ( ( ch >= 'A' && ch <= 'Z' ) ||
         ( ch >= 'a' && ch <= 'z' ) ||
         ( ch >= '0' && ch <= '9' ) ||
         ch == '-' ) {
      continue;
    }
    return false;   // invalid character found
  }
  return true;
}
//@endcond
