/*
 * Copyright (c) 2004 Nokia. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of Nokia nor the names of its contributors may be
 * used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <string.h>

#include "KWQDictImpl.h"
#include "KWQAssertions.h"
#include "KWQMap.h"
#include <CoreFoundation/CoreFoundation.h>

typedef void (* DeleteFunction) (void *);

class KWQDictPrivate
{
public:
    KWQDictPrivate(int size, bool caseSensitive, DeleteFunction);
    KWQDictPrivate(const KWQDictPrivate &dp);
    ~KWQDictPrivate();
    
    QMap<QString,void*> map;
    DeleteFunction deleteFunc;
    bool modifyCase;
    KWQDictIteratorPrivate *iterators;    
};

class KWQDictIteratorPrivate
{
public:
    KWQDictIteratorPrivate(KWQDictPrivate *);
    ~KWQDictIteratorPrivate();
    
    void remove(const QString &key);
    void dictDestroyed();
    
    uint count;
    uint pos;
    QString **keys;
    void **values;
    KWQDictPrivate *dict;
    KWQDictIteratorPrivate *next;
    KWQDictIteratorPrivate *prev;
};

KWQDictPrivate::KWQDictPrivate(int size, bool caseSensitive,
                               DeleteFunction _deleteFunc)
    : deleteFunc(_deleteFunc),
      modifyCase(!caseSensitive),
      iterators(0)
{
}

KWQDictPrivate::KWQDictPrivate(const KWQDictPrivate &dp)
    : map(dp.map),
      deleteFunc(dp.deleteFunc),
      modifyCase(dp.modifyCase),
      iterators(0)
{
}

KWQDictPrivate::~KWQDictPrivate()
{
    for (KWQDictIteratorPrivate *it = iterators; it; it = it->next) {
        it->dictDestroyed();
    }
}


/*
 * No KWQDictImpl::~KWQDictImpl() because QDict::~QDict calls KWQDictImpl::clear()
 * on 
 */
KWQDictImpl::KWQDictImpl(int size, bool caseSensitive, void (*deleteFunc_)(void *))
  : d(new KWQDictPrivate(size, caseSensitive, deleteFunc_))
{
}

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

void KWQDictImpl::insert(const QString &key, const void *value)
{
    if (d->modifyCase)
	d->map.insert(key.lower(), const_cast<void*>(value));
    else
	d->map.insert(key, const_cast<void*>(value) );
}

bool KWQDictImpl::remove(const QString &key, bool deleteItem)
{
    QMapIterator<QString, void*> i;
    void* data;

    if (d->modifyCase)
	i = d->map.find(key.lower());
    else
	i = d->map.find(key);
    
    if (i == d->map.end())
	return false;
    
    data = *i;

    d->map.remove(i);    
    if (deleteItem && d->deleteFunc) {
      d->deleteFunc(data);
      return true;
    }

    for (KWQDictIteratorPrivate *it = d->iterators; it; it = it->next) {
        it->remove(key);
    }

    return false;
}

void KWQDictImpl::clear(bool deleteItem)
{
    if (deleteItem)
    {	
	QMapIterator<QString,void*> i = d->map.begin();
	QMapIterator<QString,void*> end = d->map.end();
	void *data;
	while (i!=end)
	{
	    data=*i;
	    if (d->deleteFunc) d->deleteFunc(data);
	    ++i;
	}
    }

    d->map.clear();
}

uint KWQDictImpl::count() const
{
    return d->map.count();
}
    
void *KWQDictImpl::find(const QString &key) const
{
    QMapConstIterator<QString,void*> i;
    if (d->modifyCase)
	i = d->map.find(key.lower());
    else
	i = d->map.find(key);

    if (i == d->map.end())
	return 0;
    return *i;
}

void KWQDictImpl::swap(KWQDictImpl &di)
{
    KWQDictPrivate *tmp;

    tmp = di.d;
    di.d = d;
    d = tmp;
}

KWQDictImpl &KWQDictImpl::assign(const KWQDictImpl &di, bool deleteItems)
{
    KWQDictImpl tmp(di);
    
    if (deleteItems) {
	clear(true);
    }

    swap(tmp);

    return *this;
}


KWQDictIteratorImpl::KWQDictIteratorImpl(const KWQDictImpl &di)
    : d(new KWQDictIteratorPrivate(di.d))
{
}

uint KWQDictIteratorImpl::count() const
{
    return d->count;
}

void* KWQDictIteratorImpl::current() const
{
    if (d->pos >= d->count) {
	return NULL;
    }
    return d->values[d->pos];
}

void* KWQDictIteratorImpl::toFirst()
{
    d->pos = 0;
    return current();
}

void* KWQDictIteratorImpl::operator++()
{
    ++d->pos;
    return current();
}

QString KWQDictIteratorImpl::currentStringKey() const
{
    if (d->pos >= d->count) {
        return QString();
    }
    return QString(*d->keys[d->pos]);
}


KWQDictIteratorPrivate::KWQDictIteratorPrivate(KWQDictPrivate *d) :
    count(d->map.count()),
    pos(0),
    keys(new QString * [count]),
    values(new void * [count]),
    dict(d),
    next(d->iterators),
    prev(0)
{
    d->iterators = this;
    if (next) {
        next->prev = this;
    }
    
    unsigned int i = 0;
    QMap<QString,void*>::Iterator it = d->map.begin();
    QMap<QString,void*>::Iterator end = d->map.end();    
    while (it != end) {
        keys[i] = new QString(it.key());
	values[i] = it.data();
	++i;
	++it;
    }
    ASSERT(i==count);	
}

KWQDictIteratorPrivate::~KWQDictIteratorPrivate()
{
    if (prev) {
        prev->next = next;
    } else if (dict) {
        dict->iterators = next;
    }
    if (next) {
        next->prev = prev;
    }
    
    delete [] keys;
    delete [] values;
}

void KWQDictIteratorPrivate::remove(const QString &key)
{
    for (uint i = 0; i < count; ) {
      if (*keys[i] != key) {
            ++i;
        } else {
            --count;
            if (pos > i) {
                --pos;
            }
            memmove(&keys[i], &keys[i+1], sizeof(keys[i]) * (count - i));
            memmove(&values[i], &values[i+1], sizeof(values[i]) * (count - i));
        }
    }
}

void KWQDictIteratorPrivate::dictDestroyed()
{
    count = 0;
    dict = 0;
}
