//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//

static const char revid[] = "$Id: Key.cpp,v 1.57 2003/10/02 21:54:40 merrells Exp $";

#include "dbxml_config.h"
#include "dbxml/XmlPortability.hpp"
#include "dbxml/XmlValue.hpp"
#include "QueryContext.hpp"
#include "Key.hpp"
#include "KeyGenerator.hpp"
#include "Value.hpp"
#include "SyntaxManager.hpp"
#include "Name.hpp"
#include "Container.hpp"
#include "db_utils.h"
#include "ScopedDbt.hpp"

#include <string>
#include <sstream>

using namespace DbXml;

Key::Key()
	: index_(Index::NONE),
	id1_(0),
	id2_(0),
	value_(0)
{}

Key::~Key()
{
	delete value_;
}

void Key::set(const Index &i, const ID &id1, const ID &id2)
{
	index_ = i;
	id1_ = id1;
	id2_ = id2;
}

void Key::set(const Index &i, const ID &id1, const ID &id2, const char *p)
{
	index_ = i;
	id1_ = id1;
	id2_ = id2;
	setValue(p, (p ? strlen(p) : 0));
}

void Key::set(const Index &i, const ID &id1, const ID &id2, XmlValue &v, QueryContext *context)
{
	index_ = i;
	id1_ = id1;
	id2_ = id2;
	if (v.isNull()) {
		if (value_ != 0) {
			value_->reset();
		}
	} else {
		setValue(v, context);
	}
}

void Key::set(const Key &key, const char *p, size_t l)
{
	index_ = key.index_;
	id1_ = key.id1_;
	id2_ = key.id2_;
	setValue(p, l);
}

void Key::reset()
{
	index_ = Index::NONE;
	id1_ = 0;
	id2_ = 0;
	if (value_ != 0) {
		value_->reset();
	}
}

const Syntax *Key::getSyntax() const
{
	return SyntaxManager::getInstance()->getSyntax(getSyntaxType());
}

void Key::addKeyToStash(KeyStash &stash, bool swap) const
{
	switch(getSyntax()->countKeys(index_, getValue(), getValueSize()))
	{
	case 0:
		break;
	case 1:
		doAddKeyToStash(stash, getValue(), getValueSize(), swap);
		break;
	default:
		{
		const char *keyValue= 0;
		size_t keyLength= 0;
		KeyGenerator::Ptr kg= getSyntax()->getKeyGenerator(index_, getValue(), getValueSize());
		while(kg->next(keyValue, keyLength))
		{
			doAddKeyToStash(stash, keyValue, keyLength, swap);
		}
		}
		break;
	}
}

void Key::doAddKeyToStash(KeyStash &stash, const char *value, size_t length, bool swap) const
{
	//
	// xxxxxxxx index specification
	// xxxxxxxx	key size
	// xx       key prefix
	// xxxxxxxx id1_
	// xxxxxxxx id2_
	// xx...    value
	//
	stash.write(&index_, sizeof(index_));
	size_t n = keyBufferSize(length);
	stash.write(&n, sizeof(n));
	writeKeyBufferToBuffer(stash.getBuffer(), value, length, swap);
}

size_t Key::keyBufferSize(size_t length) const
{
	size_t n = sizeof(unsigned char); // prefix
	switch (index_.getPath()) {
	case Index::PATH_NONE:
		break;
	case Index::PATH_NODE:
		n += sizeof(unsigned long);
		break;
	case Index::PATH_EDGE:
		n += sizeof(unsigned long);
		n += sizeof(unsigned long);
		break;
	default:
		break;
	}
	switch (index_.getKey()) {
	case Index::KEY_NONE:
		break;
	case Index::KEY_PRESENCE:
		break;
	case Index::KEY_EQUALITY:
		n += getValueSize();
		break;
	case Index::KEY_SUBSTRING:
		n += (value_ == 0 ? 0 : length);
		break;
	default:
		break;
	}
	return n;
}

void Key::writeKeyBufferToBuffer(Buffer &buffer, const char *value, size_t length, bool swap) const
{
	unsigned char prefix = index_.getKeyPrefix();
	buffer.write(&prefix, sizeof(prefix));
	unsigned long id;
	switch (index_.getPath()) {
	case Index::PATH_NONE:
		break;
	case Index::PATH_NODE:
		id = id1_.raw();
		if (swap)
			M_32_SWAP(id);
		buffer.write(&id, sizeof(id));
		break;
	case Index::PATH_EDGE:
		id = id1_.raw();
		if (swap)
			M_32_SWAP(id);
		buffer.write(&id, sizeof(id));
		id = id2_.raw();
		if (swap)
			M_32_SWAP(id);
		buffer.write(&id, sizeof(id));
		break;
	default:
		break;
	}
	switch (index_.getKey()) {
	case Index::KEY_NONE:
		break;
	case Index::KEY_PRESENCE:
		break;
	case Index::KEY_EQUALITY:
	case Index::KEY_SUBSTRING:
 		if(value!=0)
 			buffer.write(value, length);
		break;
	default:
		break;
	}
}

void Key::readKeyFromBuffer(Buffer &buffer, bool swap)
{
	unsigned char prefix;
	buffer.read(&prefix, sizeof(prefix));
	index_.setFromPrefix(prefix);
	unsigned long id;
	switch (index_.getPath()) {
	case Index::PATH_NONE:
		break;
	case Index::PATH_NODE:
		buffer.read(&id, sizeof(id));
		if (swap)
			M_32_SWAP(id);
		id1_ = id;
		break;
	case Index::PATH_EDGE:
		buffer.read(&id, sizeof(id));
		if (swap)
			M_32_SWAP(id);
		id1_ = id;
		buffer.read(&id, sizeof(id));
		if (swap)
			M_32_SWAP(id);
		id2_ = id;
		break;
	default:
		break;
	}
	switch (index_.getKey()) {
	case Index::KEY_NONE:
		break;
	case Index::KEY_PRESENCE:
		break;
	case Index::KEY_EQUALITY:
		// FALL THROUGH
	case Index::KEY_SUBSTRING:
		setValue((const char *)buffer.getCursor(), buffer.getRemaining());
		break;
	default:
		break;
	}
}

void Key::setValue(XmlValue &v, QueryContext *context)
{
	const std::string s(((Value*)v)->asString(context));
	setValue(s.c_str(), s.length());
	index_.set(((Value*)v)->getSyntaxType(context)); // jcm - perf - 2nd lookup if value is a variable.
}

const char *Key::getValue() const
{
	return (value_ == 0 ? 0 : (const char *)value_->getBuffer());
}

size_t Key::getValueSize() const
{
	return (value_ == 0 ? 0 : value_->getOccupancy());
}

void Key::setValue(const char *p, size_t l)
{
	if (value_ == 0)
		value_ = new Buffer(0, 64); // Let's just assume that most keys are <64 bytes
	else
		value_->reset();
	if (p && l > 0)
		value_->write(p, l);
}

void Key::addValue(const char *p, size_t l)
{
	if (value_ == 0)
		value_ = new Buffer(0, 64); // Let's just assume that most keys are <64 bytes
	if (p && l > 0)
		value_->write(p, l);
}

std::string Key::asString() const
{
	std::ostringstream s;
	s << "key ";
	s << index_.asString();
	s << " ";
	switch (index_.getPath()) {
	case Index::PATH_NONE:
		break;
	case Index::PATH_NODE:
		s << "id1_=";
		s << id1_;
		s << " ";
		break;
	case Index::PATH_EDGE:
		s << "id1_=";
		s << id1_;
		s << " id2_=";
		s << id2_;
		s << " ";
		break;
	default:
		break;
	}
	switch (index_.getKey()) {
	case Index::KEY_NONE:
		break;
	case Index::KEY_PRESENCE:
		break;
	case Index::KEY_EQUALITY:
	case Index::KEY_SUBSTRING:
		if (value_ != 0) {
			s << value_->asString();
		}
		break;
	default:
		break;
	}
	return s.str();
}

std::string Key::asString_XML(DbTxn *txn, const Container &container) const
{
	std::string s;
	switch (index_.getPath()) {
	case Index::PATH_NONE:
		break;
	case Index::PATH_NODE: {
			Name n1;
			container.lookupName(txn, id1_, n1);
			s += "path='";
			s += n1.asString();
			s += "' ";
		}
		break;
	case Index::PATH_EDGE: {
			Name n1, n2;
			container.lookupName(txn, id1_, n1);
			container.lookupName(txn, id2_, n2);
			s += "path='";
			s += n1.asString();
			s += ".";
			s += n2.asString();
			s += "' ";
		}
		break;
	default:
		break;
	}
	switch (index_.getKey()) {
	case Index::KEY_NONE:
		break;
	case Index::KEY_PRESENCE:
		break;
	case Index::KEY_EQUALITY:
	case Index::KEY_SUBSTRING:
		if (value_ != 0 && value_->getOccupancy() != 0) {
			s += "value='";
			s += string((const char*)value_->getBuffer(), value_->getOccupancy());
			s += "' ";
		}
		break;
	default:
		break;
	}
	return s;
}

void Key::setDbtFromThis(DbtOut &dbt, bool swap) const
{
	dbt.set(0, keyBufferSize(3));
	Buffer b(dbt.get_data(), dbt.get_size(), /*wrapper*/true); // buffer_ doesn't make a copy.
	writeKeyBufferToBuffer(b, getValue(), getValueSize(), swap); // offset,length ignored when not SUBSTRING
}

void Key::setThisFromDbt(const DbtOut &dbt, bool swap)
{
	Buffer b(dbt.get_data(), dbt.get_size(), /*wrapper*/true); // buffer_ doesn't make a copy.
	readKeyFromBuffer(b, swap);
}

// KeyStash

KeyStash::KeyStash()
	: buffer_(0, 1024) // Let's guess that the average total size of the keys is <1024 bytes.
{
}

void KeyStash::reset()
{
	buffer_.resetCursorPosition();
	buffer_.resetOccupancy();
}

void KeyStash::write(const void *p, size_t l)
{
	buffer_.write(p, l);
}

void *KeyStash::first(iterator &i, size_t &n, Index &index)
{
	if (buffer_.getOccupancy() != 0) {
		i = (unsigned char*)buffer_.getBuffer();
		return dereferenceIterator(i, n, index);
	} else {
		i = 0;
		n = 0;
		index.reset();
		return 0;
	}
}

void *KeyStash::next(iterator &i, size_t &n, Index &index)
{
	unsigned char *end = ((unsigned char*)buffer_.getBuffer()) + buffer_.getOccupancy();
	if (i < end) {
		return dereferenceIterator(i, n, index);
	} else {
		i = 0;
		n = 0;
		index.reset();
		return 0;
	}
}

void *KeyStash::dereferenceIterator(iterator &i, size_t &n, Index &index)
{
	// This is all very icky, but it's in the interests of performance, so
	// please forgive me.  There are no alignment guarantees here, so we
	// have to memcpy the data to avoid bus errors on architectures that
	// enforce alignment (Sparc).
	//
	index.reset();
	unsigned long ival;
	memcpy(&ival, (void *)i, sizeof (ival));
	index.set(ival);
	i += sizeof(unsigned long);
	memcpy(&n, (void *)i, sizeof (n));
	i += sizeof(size_t);
	void *p = i;
	i += n;
	return p;
}
