/*
 * Copyright (C) 2015 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "config.h"
#include "IndexValueStore.h"

#if ENABLE(INDEXED_DATABASE)

#include "IDBError.h"
#include "IDBKeyRangeData.h"
#include "Logging.h"
#include "MemoryIndex.h"
#include <wtf/text/StringBuilder.h>

namespace WebCore {
namespace IDBServer {

IndexValueStore::IndexValueStore(bool unique)
    : m_unique(unique)
{
}

const IDBKeyData* IndexValueStore::lowestValueForKey(const IDBKeyData& key) const
{
    const auto& entry = m_records.get(key);
    if (!entry)
        return nullptr;

    return entry->getLowest();
}

Vector<IDBKeyData> IndexValueStore::allValuesForKey(const IDBKeyData& key, uint32_t limit) const
{
    const auto& entry = m_records.get(key);
    if (!entry)
        return { };

    Vector<IDBKeyData> results;
    for (auto iterator = entry->begin(); results.size() < limit && iterator.isValid(); ++iterator)
        results.append(iterator.key());

    return results;
}

uint64_t IndexValueStore::countForKey(const IDBKeyData& key) const
{
    const auto& entry = m_records.get(key);
    if (!entry)
        return 0;

    return entry->getCount();
}

bool IndexValueStore::contains(const IDBKeyData& key) const
{
    const auto& entry = m_records.get(key);
    if (!entry)
        return false;

    ASSERT(entry->getCount());

    return true;
}

IDBError IndexValueStore::addRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey)
{
    auto result = m_records.add(indexKey, nullptr);

    if (!result.isNewEntry && m_unique)
        return IDBError(ConstraintError);

    if (result.isNewEntry)
        result.iterator->value = std::make_unique<IndexValueEntry>(m_unique);

    result.iterator->value->addKey(valueKey);
    m_orderedKeys.insert(indexKey);

    return IDBError { };
}

void IndexValueStore::removeRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey)
{
    auto iterator = m_records.find(indexKey);
    if (!iterator->value)
        return;

    if (iterator->value->removeKey(valueKey))
        m_records.remove(iterator);
}

void IndexValueStore::removeEntriesWithValueKey(MemoryIndex& index, const IDBKeyData& valueKey)
{
    Vector<IDBKeyData> entryKeysToRemove;
    entryKeysToRemove.reserveInitialCapacity(m_records.size());

    for (auto& entry : m_records) {
        if (entry.value->removeKey(valueKey))
            index.notifyCursorsOfValueChange(entry.key, valueKey);
        if (!entry.value->getCount())
            entryKeysToRemove.uncheckedAppend(entry.key);
    }

    for (auto& entry : entryKeysToRemove) {
        m_orderedKeys.erase(entry);
        m_records.remove(entry);
    }
}

IDBKeyData IndexValueStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& range) const
{
    LOG(IndexedDB, "IndexValueStore::lowestKeyWithRecordInRange - %s", range.loggingString().utf8().data());

    if (range.isExactlyOneKey())
        return m_records.contains(range.lowerKey) ? range.lowerKey : IDBKeyData();

    auto iterator = lowestIteratorInRange(range);
    if (iterator == m_orderedKeys.end())
        return { };

    return *iterator;
}

IDBKeyDataSet::iterator IndexValueStore::lowestIteratorInRange(const IDBKeyRangeData& range) const
{
    auto lowestInRange = m_orderedKeys.lower_bound(range.lowerKey);

    if (lowestInRange == m_orderedKeys.end())
        return lowestInRange;

    if (range.lowerOpen && *lowestInRange == range.lowerKey) {
        ++lowestInRange;

        if (lowestInRange == m_orderedKeys.end())
            return lowestInRange;
    }

    if (!range.upperKey.isNull()) {
        if (lowestInRange->compare(range.upperKey) > 0)
            return m_orderedKeys.end();
        if (range.upperOpen && *lowestInRange == range.upperKey)
            return m_orderedKeys.end();
    }

    return lowestInRange;
}

IDBKeyDataSet::reverse_iterator IndexValueStore::highestReverseIteratorInRange(const IDBKeyRangeData& range) const
{
    auto highestInRange = IDBKeyDataSet::reverse_iterator(m_orderedKeys.upper_bound(range.upperKey));

    if (highestInRange == m_orderedKeys.rend())
        return highestInRange;

    if (range.upperOpen && *highestInRange == range.upperKey) {
        ++highestInRange;

        if (highestInRange == m_orderedKeys.rend())
            return highestInRange;
    }

    if (!range.lowerKey.isNull()) {
        if (highestInRange->compare(range.lowerKey) < 0)
            return m_orderedKeys.rend();
        if (range.lowerOpen && *highestInRange == range.lowerKey)
            return m_orderedKeys.rend();
    }

    return highestInRange;
}

IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, bool open)
{
    IDBKeyRangeData range;
    if (!key.isNull())
        range.lowerKey = key;
    else
        range.lowerKey = IDBKeyData::minimum();
    range.lowerOpen = open;

    auto iterator = lowestIteratorInRange(range);
    if (iterator == m_orderedKeys.end())
        return { };

    auto record = m_records.get(*iterator);
    ASSERT(record);

    auto primaryIterator = record->begin();
    ASSERT(primaryIterator.isValid());
    return { *this, iterator, primaryIterator };
}

IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, const IDBKeyData& primaryKey)
{
    ASSERT(!key.isNull());
    ASSERT(!primaryKey.isNull());

    IDBKeyRangeData range;
    range.lowerKey = key;
    range.lowerOpen = false;

    auto iterator = lowestIteratorInRange(range);
    if (iterator == m_orderedKeys.end())
        return { };

    auto record = m_records.get(*iterator);
    ASSERT(record);

    // If the main record iterator is not equal to the key we were looking for,
    // we know the primary key record should be the first.
    if (*iterator != key) {
        auto primaryIterator = record->begin();
        ASSERT(primaryIterator.isValid());

        return { *this, iterator, primaryIterator };
    }

    auto primaryIterator = record->find(primaryKey);
    if (primaryIterator.isValid())
        return { *this, iterator, primaryIterator };

    // If we didn't find a primary key iterator in this entry,
    // we need to move on to start of the next record.
    iterator++;
    if (iterator == m_orderedKeys.end())
        return { };

    record = m_records.get(*iterator);
    ASSERT(record);

    primaryIterator = record->begin();
    ASSERT(primaryIterator.isValid());

    return { *this, iterator, primaryIterator };
}

IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, CursorDuplicity duplicity, bool open)
{
    IDBKeyRangeData range;
    if (!key.isNull())
        range.upperKey = key;
    else
        range.upperKey = IDBKeyData::maximum();
    range.upperOpen = open;

    auto iterator = highestReverseIteratorInRange(range);
    if (iterator == m_orderedKeys.rend())
        return { };

    auto record = m_records.get(*iterator);
    ASSERT(record);

    auto primaryIterator = record->reverseBegin(duplicity);
    ASSERT(primaryIterator.isValid());
    return { *this, duplicity, iterator, primaryIterator };
}

IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, const IDBKeyData& primaryKey, CursorDuplicity duplicity)
{
    ASSERT(!key.isNull());
    ASSERT(!primaryKey.isNull());

    IDBKeyRangeData range;
    range.upperKey = key;
    range.upperOpen = false;

    auto iterator = highestReverseIteratorInRange(range);
    if (iterator == m_orderedKeys.rend())
        return { };

    auto record = m_records.get(*iterator);
    ASSERT(record);

    auto primaryIterator = record->reverseFind(primaryKey, duplicity);
    if (primaryIterator.isValid())
        return { *this, duplicity, iterator, primaryIterator };

    // If we didn't find a primary key iterator in this entry,
    // we need to move on to start of the next record.
    iterator++;
    if (iterator == m_orderedKeys.rend())
        return { };

    record = m_records.get(*iterator);
    ASSERT(record);

    primaryIterator = record->reverseBegin(duplicity);
    ASSERT(primaryIterator.isValid());

    return { *this, duplicity, iterator, primaryIterator };
}


IndexValueStore::Iterator::Iterator(IndexValueStore& store, IDBKeyDataSet::iterator iterator, IndexValueEntry::Iterator primaryIterator)
    : m_store(&store)
    , m_forwardIterator(iterator)
    , m_primaryKeyIterator(primaryIterator)
{
}

IndexValueStore::Iterator::Iterator(IndexValueStore& store, CursorDuplicity duplicity, IDBKeyDataSet::reverse_iterator iterator, IndexValueEntry::Iterator primaryIterator)
    : m_store(&store)
    , m_forward(false)
    , m_duplicity(duplicity)
    , m_reverseIterator(iterator)
    , m_primaryKeyIterator(primaryIterator)
{
}

IndexValueStore::Iterator& IndexValueStore::Iterator::nextIndexEntry()
{
    if (!m_store)
        return *this;

    if (m_forward) {
        ++m_forwardIterator;
        if (m_forwardIterator == m_store->m_orderedKeys.end()) {
            invalidate();
            return *this;
        }

        auto* entry = m_store->m_records.get(*m_forwardIterator);
        ASSERT(entry);

        m_primaryKeyIterator = entry->begin();
        ASSERT(m_primaryKeyIterator.isValid());
    } else {
        ++m_reverseIterator;
        if (m_reverseIterator == m_store->m_orderedKeys.rend()) {
            invalidate();
            return *this;
        }

        auto* entry = m_store->m_records.get(*m_reverseIterator);
        ASSERT(entry);

        m_primaryKeyIterator = entry->reverseBegin(m_duplicity);
        ASSERT(m_primaryKeyIterator.isValid());
    }

    return *this;
}

IndexValueStore::Iterator& IndexValueStore::Iterator::operator++()
{
    if (!isValid())
        return *this;

    ++m_primaryKeyIterator;
    if (m_primaryKeyIterator.isValid())
        return *this;

    // Ran out of primary key records, so move the main index iterator.
    return nextIndexEntry();
}

void IndexValueStore::Iterator::invalidate()
{
    m_store = nullptr;
    m_primaryKeyIterator.invalidate();
}

bool IndexValueStore::Iterator::isValid()
{
    return m_store && m_primaryKeyIterator.isValid();
}

const IDBKeyData& IndexValueStore::Iterator::key()
{
    ASSERT(isValid());
    return m_forward ? *m_forwardIterator : *m_reverseIterator;
}

const IDBKeyData& IndexValueStore::Iterator::primaryKey()
{
    ASSERT(isValid());
    return m_primaryKeyIterator.key();
}

#if !LOG_DISABLED
String IndexValueStore::loggingString() const
{
    StringBuilder builder;
    for (auto& key : m_orderedKeys) {
        builder.appendLiteral("Key: ");
        builder.append(key.loggingString());
        builder.appendLiteral("  Entry has ");
        builder.appendNumber(m_records.get(key)->getCount());
        builder.appendLiteral(" entries");
    }
    return builder.toString();
}
#endif

} // namespace IDBServer
} // namespace WebCore

#endif // ENABLE(INDEXED_DATABASE)
