/****************************************************************************
**
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
**
** This file is part of the sql module of the TQt Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.TQPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about TQt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for TQPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "qsqlcachedresult.h"

#include <tqvariant.h>
#include <tqdatetime.h>
#include <tqvaluevector.h>

static const uint initial_cache_size = 128;

class TQSqlCachedResultPrivate
{
public:
    TQSqlCachedResultPrivate();
    bool canSeek(int i) const;
    inline int cacheCount() const;
    void init(int count, bool fo);
    void cleanup();
    int nextIndex();
    void revertLast();

    TQSqlCachedResult::ValueCache cache;
    int rowCacheEnd;
    int colCount;
    bool forwardOnly;
};

TQSqlCachedResultPrivate::TQSqlCachedResultPrivate():
    rowCacheEnd(0), colCount(0), forwardOnly(false)
{
}

void TQSqlCachedResultPrivate::cleanup()
{
    cache.clear();
    forwardOnly = false;
    colCount = 0;
    rowCacheEnd = 0;
}

void TQSqlCachedResultPrivate::init(int count, bool fo)
{
    Q_ASSERT(count);
    cleanup();
    forwardOnly = fo;
    colCount = count;
    if (fo) {
        cache.resize(count);
        rowCacheEnd = count;
    } else {
        cache.resize(initial_cache_size * count);
    }
}

int TQSqlCachedResultPrivate::nextIndex()
{
    if (forwardOnly)
        return 0;
    int newIdx = rowCacheEnd;
    if (rowCacheEnd == (int)cache.size())
        cache.resize(cache.size() * 2);
/*    if (newIdx + colCount > cache.size()){
        if(cache.size() * 2 < cache.size() + 10000)
            cache.resize(cache.size() * 2);
        else
            cache.resize(cache.size() + 10000);
    }*/
    rowCacheEnd += colCount;

    return newIdx;
}

bool TQSqlCachedResultPrivate::canSeek(int i) const
{
    if (forwardOnly || i < 0)
        return false;
    return rowCacheEnd >= (i + 1) * colCount;
}

void TQSqlCachedResultPrivate::revertLast()
{
    if (forwardOnly)
        return;
    rowCacheEnd -= colCount;
}

inline int TQSqlCachedResultPrivate::cacheCount() const
{
    Q_ASSERT(!forwardOnly);
    Q_ASSERT(colCount);
    return rowCacheEnd / colCount;
}

//////////////

TQSqlCachedResult::TQSqlCachedResult(const TQSqlDriver * db): TQSqlResult (db)
{
    d = new TQSqlCachedResultPrivate();
}

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

void TQSqlCachedResult::init(int colCount)
{
    d->init(colCount, isForwardOnly());
}

bool TQSqlCachedResult::fetch(int i)
{
    if ((!isActive()) || (i < 0))
        return false;
    if (at() == i)
        return true;
    if (d->forwardOnly) {
        // speed hack - do not copy values if not needed
        if (at() > i || at() == TQSql::AfterLast)
            return false;
        while(at() < i - 1) {
            if (!gotoNext(d->cache, -1))
                return false;
            setAt(at() + 1);
        }
        if (!gotoNext(d->cache, 0))
            return false;
        setAt(at() + 1);
        return true;
    }
    if (d->canSeek(i)) {
        setAt(i);
        return true;
    }
    if (d->rowCacheEnd > 0)
        setAt(d->cacheCount()-1);
    while (at() < i) {
        if (!cacheNext())
            return false;
    }
    return true;
}

bool TQSqlCachedResult::fetchNext()
{
    if (d->canSeek(at() + 1)) {
        setAt(at() + 1);
        return true;
    }
    return cacheNext();
}

bool TQSqlCachedResult::fetchPrevious()
{
    return fetch(at() - 1);
}

bool TQSqlCachedResult::fetchFirst()
{
    if (d->forwardOnly && at() != TQSql::BeforeFirst) {
        return false;
    }
    if (d->canSeek(0)) {
        setAt(0);
        return true;
    }
    return cacheNext();
}

bool TQSqlCachedResult::fetchLast()
{
    if (at() == TQSql::AfterLast) {
        if (d->forwardOnly)
            return false;
        else
            return fetch(d->cacheCount() - 1);
    }

    int i = at();
    while (fetchNext())
        ++i; /* brute force */
    if (d->forwardOnly && at() == TQSql::AfterLast) {
        setAt(i);
        return true;
    } else {
        return fetch(i);
    }
}

TQVariant TQSqlCachedResult::data(int i)
{
    int idx = d->forwardOnly ? i : at() * d->colCount + i;
    if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
        return TQVariant();

    return d->cache.at(idx);
}

bool TQSqlCachedResult::isNull(int i)
{
    int idx = d->forwardOnly ? i : at() * d->colCount + i;
    if (i > d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
        return true;

    return d->cache.at(idx).isNull();
}

void TQSqlCachedResult::cleanup()
{
    setAt(TQSql::BeforeFirst);
    setActive(false);
    d->cleanup();
}

bool TQSqlCachedResult::cacheNext()
{
    if (!gotoNext(d->cache, d->nextIndex())) {
        d->revertLast();
        return false;
    }
    setAt(at() + 1);
    return true;
}

int TQSqlCachedResult::colCount() const
{
    return d->colCount;
}

TQSqlCachedResult::ValueCache &TQSqlCachedResult::cache()
{
    return d->cache;
}

