/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "cntrelationshipgroup.h"
#include <cntitem.h>

#include <QDebug>

/*!
 * Conatructor
 * 
 * \a contactDatabase CContactDatabase with established connection to the database
 * \a managerUri current contact manager uri
 */
CntRelationshipGroup::CntRelationshipGroup(CContactDatabase *contactDatabase, const QString &managerUri) :
    CntAbstractRelationship(contactDatabase, managerUri, QContactRelationship::HasMember)
{
}

/*!
 * Destructor
 */
CntRelationshipGroup::~CntRelationshipGroup()
{
}

/*!
 * Save a single relatonship
 * 
 * \a affectedContactIds will include the affected contact ids 
 * \a relationship to be saved
 * \a error t
 */
bool CntRelationshipGroup::saveRelationshipL(QSet<QContactLocalId> *affectedContactIds, QContactRelationship* relationship, QContactManager::Error* error)
{
    //get the ids of the relationship
    QScopedPointer<QContactId> groupId(new QContactId(relationship->first()));
    QScopedPointer<QContactId> contactId(new QContactId(relationship->second()));
    
    //cntmodel accepts contact id 0, which is considered an error in qt contacts
    database()->AddContactToGroupL(TContactItemId(contactId->localId()), TContactItemId(groupId->localId()));
    
    //add the group  and group member to the list of affected contacts
    //note if the value already exists in the QSet nothing happens
    affectedContactIds->insert(groupId->localId());
    affectedContactIds->insert(contactId->localId());
    
    *error = QContactManager::NoError;
    return true;
}

bool CntRelationshipGroup::removeRelationshipL(QSet<QContactLocalId> *affectedContactIds, const QContactRelationship& relationship, QContactManager::Error* error)
{
    //get the ids of the relationship
    QScopedPointer<QContactId> groupId(new QContactId(relationship.first()));
    QScopedPointer<QContactId> contactId(new QContactId(relationship.second()));

    //read the contacts from the database
    CContactItem* groupContact = database()->ReadContactLC(groupId->localId());
    CContactItem* contact      = database()->ReadContactLC(contactId->localId());
    
    //Check if contact is part of the group
    isGroupMemberL(contact, groupContact->Id());
    
    //remove contact doesn't return an error if the group is not part of the group
    database()->RemoveContactFromGroupL(contact->Id(), groupContact->Id());

    //add the removed group member to the list of affected contacts
    affectedContactIds->insert(groupId->localId());
    affectedContactIds->insert(contactId->localId());
    
    CleanupStack::PopAndDestroy(contact);
    CleanupStack::PopAndDestroy(groupContact);

    *error = QContactManager::NoError;
    return true;
}

#ifdef SYMBIAN_BACKEND_USE_CNTMODEL_V2
/*!
 * Save many relationships
 * 
 * \a affectedContactIds will include the affected contact ids 
 * \a relationships to be saved
 * \a error the error code
 * \a return true if relationships was attempted to be saved, false otherwise
 */
bool CntRelationshipGroup::saveRelationshipsL(QSet<QContactLocalId> *affectedContactIds, QList<QContactRelationship> *relationships, QContactManager::Error* error)
{
    RArray<TContactItemId> idList;

    if (relationships->count() == 0) {
        return true;
    }
    bool success = true;
    QContactLocalId groupId = relationships->at(0).first().localId();
    affectedContactIds->insert(groupId);
    
    foreach (QContactRelationship relationship, *relationships) {
        if (relationship.first().localId()!= groupId) {
            success = false;
            break;
        }
        affectedContactIds->insert(relationship.second().localId());
        idList.AppendL(TContactItemId(relationship.second().localId()));
    }

    *error = QContactManager::NoError;
    if (!success) {
        affectedContactIds->clear();
    }
    else {
        database()->AddContactsToGroupL(idList, TContactItemId(groupId));
    }
    idList.Close();  
    return success;
}

/*!
 * Delete many relationships
 * 
 * \a affectedContactIds will include the affected contact ids 
 * \a relationships to be deleted
 * \a error the error code in case of error, NoError otherwise
 * \a return true if the deleting was attempted to be handled, false otherwise
 */
bool CntRelationshipGroup::removeRelationshipsL(QSet<QContactLocalId> *affectedContactIds, const QList<QContactRelationship> &relationships, QContactManager::Error* error)
{
    RArray<TContactItemId> idList;

    if (relationships.count() == 0) {
        return true;
    }
    bool success = true;
    QContactLocalId groupId = relationships.at(0).first().localId();
    affectedContactIds->insert(groupId);

    foreach (QContactRelationship relationship, relationships) {
        if (relationship.first().localId()!= groupId) {
                    success = false;
                    break;
                }
        affectedContactIds->insert(relationship.second().localId());
        idList.AppendL(TContactItemId(relationship.second().localId()));
    }

    *error = QContactManager::NoError;
    if (!success) {
        affectedContactIds->clear();
    }
    else {
        database()->RemoveContactsFromGroupL(idList, TContactItemId(groupId));
    }
    idList.Close();
    return success;
}
#endif

bool CntRelationshipGroup::validateRelationship(const QContactRelationship &relationship, QContactManager::Error* error)
{
    // check that "second" is in this manager
    if (!relationship.second().managerUri().isEmpty() && relationship.second().managerUri() != managerUri()) {
        *error = QContactManager::InvalidRelationshipError;
        return false;
    }

    return true;
}

/*
 * Function will leave with KErrNotFound if contact is not part of the specific goup
 * 
 */
void CntRelationshipGroup::isGroupMemberL(const CContactItem* contactItem, const TContactItemId groupId) const
{
    if(contactItem &&
            (contactItem->Type() == KUidContactCard
            || contactItem->Type() == KUidContactOwnCard
            || contactItem->Type() == KUidContactGroup
            || contactItem->Type() == KUidContactICCEntry))
    {
        //cast the contact item to base class for groups and contacts
        const CContactItemPlusGroup *contact = static_cast<const CContactItemPlusGroup *>(contactItem);
        
        const CContactIdArray *idArray = contact->GroupsJoined();
        
        //array is null if contact is not part of any groups
        if(idArray)
        {
            //find the group from the array
            TInt arrayIndex = idArray->Find(groupId);
            
            //if it doesn't exist leave
            if(arrayIndex == KErrNotFound)
            {
                User::Leave(KErrNotFound);
            }
        }
    }
    else
    {
        User::Leave(KErrNotFound);
    }
}

//retrieve all the groups that the contact is part of
QList<QContactRelationship> CntRelationshipGroup::relationshipsL(const QContactId& participantId, QContactRelationship::Role role, QContactManager::Error* error)
{
    QList<QContactRelationship> returnValue;
    
    if (participantId != QContactId())
    {
        //role is a group
        if(role == QContactRelationship::First || role == QContactRelationship::Either)
        {
            fetchGroupMembersL(participantId.localId(), &returnValue);
        }
    
        //role is member of a group
        if(role == QContactRelationship::Second || role == QContactRelationship::Either)
        {
            fetchMemberOfGroupsL(participantId.localId(), &returnValue);
        }
    }
    else
    {
        // If participantId is the default-constructed id, role is ignored and
        // all relationships of the given relationshipType are returned.
        fetchMembersOfAllGroupsL(&returnValue);
    }
    
    if (returnValue.isEmpty())
        *error = QContactManager::DoesNotExistError;

    return returnValue;
}

void CntRelationshipGroup::fetchGroupMembersL(const QContactLocalId& participantId, QList<QContactRelationship> *relationships)
{
    //fetch the contact item from the database
    CContactItem *contactItem = database()->ReadContactLC(TContactItemId(participantId));

    //make sure it's a group
    if(contactItem && contactItem->Type() == KUidContactGroup)
    {
        //cast the contact to a group
        CContactGroup *groupContact = static_cast<CContactGroup*>( contactItem );

        //create the relationship
        QContactRelationship relationship;
        relationship.setRelationshipType(QContactRelationship::HasMember);

        //get the group contacts
        const CContactIdArray *idArray = groupContact->ItemsContained();

        QContactId groupId;

        //loop through all the contacts and add them to the list
        for(int i = 0; i < idArray->Count(); i++ )
        {
            //set participant id as first id
            QContactId first;
            first.setLocalId(participantId);
            first.setManagerUri(managerUri());
            relationship.setFirst(first);

            //set the manager uri
            groupId.setManagerUri(managerUri());

            groupId.setLocalId(idArray->operator[](i));
            relationship.setSecond(groupId);

            *relationships << relationship;
        }
    }

    CleanupStack::PopAndDestroy(contactItem);
}


void CntRelationshipGroup::fetchMemberOfGroupsL(const QContactLocalId& participantId, QList<QContactRelationship> *relationships)
{
    CContactItem *contactItem = database()->ReadContactLC(TContactItemId(participantId));

    if(contactItem &&
            (contactItem->Type() == KUidContactCard
            || contactItem->Type() == KUidContactOwnCard
            || contactItem->Type() == KUidContactGroup
            || contactItem->Type() == KUidContactICCEntry))
    {
        //cast the contact item to base class for groups and contacts
        CContactItemPlusGroup *contact = static_cast<CContactItemPlusGroup *>(contactItem);

        //create the relationship
        QContactRelationship relationship;
        relationship.setRelationshipType(QContactRelationship::HasMember);

        const CContactIdArray *idArray = contact->GroupsJoined();

        if(idArray) {
            QContactId groupId;

            //loop through all the contacts and add them to the list
            for(int i = 0; i < idArray->Count(); i++ )
            {
               //set the manager uri
               groupId.setManagerUri(managerUri());
               groupId.setLocalId(idArray->operator[](i));

               //set the group as first
               relationship.setFirst(groupId);

               //set participant id as member of group
               QContactId second;
               second.setLocalId(participantId);
               second.setManagerUri(managerUri());
               relationship.setSecond(second);

               *relationships << relationship;
            }
        }
    }

    CleanupStack::PopAndDestroy(contactItem);
}

void CntRelationshipGroup::fetchMembersOfAllGroupsL(QList<QContactRelationship> *relationships)
{
    CContactIdArray *groupIdList = database()->GetGroupIdListL();
    CleanupStack::PushL(groupIdList);
    
    int count = groupIdList->Count();
    for (int i=0; i<count; i++)
    {
        fetchGroupMembersL(QContactLocalId((*groupIdList)[i]), relationships);
    }
    
    CleanupStack::PopAndDestroy(groupIdList);
}
