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

static const char revid[] = "$Id: Syntax.cpp,v 1.29 2004/01/15 12:52:28 merrells Exp $";

#include "dbxml_config.h"
#include "dbxml/XmlPortability.hpp"
#include "Syntax.hpp"
#include "IndexSpecification.hpp"

#include <cassert>

using namespace DbXml;

// NoneSyntax

Syntax::Type NoneSyntax::getType() const
{
	return Syntax::NONE;
}

const char *NoneSyntax::getName() const
{
	return "none";
}

bool NoneSyntax::test(const char *v, size_t len) const
{
	UNUSED(v);
	UNUSED(len);

	return true;
}

int NoneSyntax::compare(const char *p1, size_t l1, const char *p2, size_t l2) const
{
	size_t l = l1 > l2 ? l2 : l1;
	for (; l--; ++p1, ++p2)
		if (*p1 != *p2)
			return ((long)*p1 - (long)*p2);
	return ((long)l1 - (long)l2);
}

int NoneSyntax::bt_compare(Db *db, const Dbt *dbt1, const Dbt *dbt2) const
{
	UNUSED(db);

	return compare((const char*)dbt1->get_data(), dbt1->get_size(), (const char*)dbt2->get_data(), dbt2->get_size());
}

Database::bt_compare_fn NoneSyntax::get_bt_compare() const
{
	return static_bt_compare;
}

int NoneSyntax::static_bt_compare(Db *db, const Dbt *dbt1, const Dbt *dbt2)
{
	static NoneSyntax syntax;
	return syntax.bt_compare(db, dbt1, dbt2);
}

int NoneSyntax::structureKeyLength(const Index &index, size_t keyLength) const
{
	UNUSED(keyLength);
	int r = 0;
	if (index.getKey() == Index::KEY_PRESENCE) {
		switch (index.getPath()) {
		case Index::PATH_NODE:
			r = 5;
			break;
		case Index::PATH_EDGE:
			r = 9;
			break;
		default:
			assert(0);
			break;
		}
	} else {
		assert(0);
	}
	return r;
}

const char *NoneSyntax::minimumValue() const
{
	return 0;
}

const char *NoneSyntax::maximumValue() const
{
	return 0;
}

size_t NoneSyntax::countKeys(const Index &index, const char *valueBuffer, size_t valueLength) const
{
	UNUSED(valueBuffer); 
	UNUSED(valueLength);
	assert(index.getKey()==Index::KEY_PRESENCE);
	return 1;
}

KeyGenerator::Ptr NoneSyntax::getKeyGenerator(const Index &index, const char *valueBuffer, size_t valueLength) const
{
	assert(index.getKey()==Index::KEY_PRESENCE);
	return KeyGenerator::Ptr(new SingleKeyGenerator(valueBuffer, valueLength));
}

// StringSyntax

Syntax::Type StringSyntax::getType() const
{
	return Syntax::STRING;
}

const char *StringSyntax::getName() const
{
	return "string";
}

bool StringSyntax::test(const char *v, size_t len) const
{
	UNUSED(v);
	UNUSED(len);

	return true;
}

int StringSyntax::compare(const char *p1, size_t l1, const char *p2, size_t l2) const
{
	size_t l = l1 > l2 ? l2 : l1;
	for (; l--; ++p1, ++p2)
		if (*p1 != *p2)
			return ((long)*p1 - (long)*p2);
	return ((long)l1 - (long)l2);
}

int StringSyntax::bt_compare(Db *db, const Dbt *dbt1, const Dbt *dbt2) const
{
	UNUSED(db);

	return compare((const char*)dbt1->get_data(), dbt1->get_size(), (const char*)dbt2->get_data(), dbt2->get_size());
}

Database::bt_compare_fn StringSyntax::get_bt_compare() const
{
	return static_bt_compare;
}

int StringSyntax::static_bt_compare(Db *db, const Dbt *dbt1, const Dbt *dbt2)
{
	static StringSyntax syntax;
	return syntax.bt_compare(db, dbt1, dbt2);
}

int StringSyntax::structureKeyLength(const Index &index, size_t keyLength) const
{
	int r = 0;
	switch (index.getKey()) {
	case Index::KEY_PRESENCE:
	case Index::KEY_EQUALITY:
		switch (index.getPath()) {
		case Index::PATH_NODE:
			r = 5;
			break;
		case Index::PATH_EDGE:
			r = 9;
			break;
		default:
			assert(0);
			break;
		}
		break;
	case Index::KEY_SUBSTRING:
		switch (index.getPath()) {
		case Index::PATH_NODE:
		case Index::PATH_EDGE:
			r = keyLength;
			break;
		default:
			assert(0);
			break;
		}
		break;
	default:
		assert(0);
		break;
	}
	return r;
}

const char *StringSyntax::minimumValue() const
{
	return "";
}

const char *StringSyntax::maximumValue() const
{
	return "\xff";
}

size_t StringSyntax::countKeys(const Index &index, const char *valueBuffer, size_t valueLength) const
{
	size_t r= 0;
	switch (index.getKey()) {
	case Index::KEY_PRESENCE:
	case Index::KEY_EQUALITY:
		r= 1;
		break;
	case Index::KEY_SUBSTRING:
		{
		SubstringKeyGenerator kg(valueBuffer, valueLength);
		const char *p= 0;
		size_t l= 0;
		while(kg.next(p, l)) r++;
		}
		break;
	default:
		assert(0);
		break;
	}
	return r;
}

KeyGenerator::Ptr StringSyntax::getKeyGenerator(const Index &index, const char *valueBuffer, size_t valueLength) const
{
	KeyGenerator::Ptr r;
	switch (index.getKey()) {
	case Index::KEY_PRESENCE:
	case Index::KEY_EQUALITY:
		r.reset(new SingleKeyGenerator(valueBuffer, valueLength));
		break;
	case Index::KEY_SUBSTRING:
		r.reset(new SubstringKeyGenerator(valueBuffer, valueLength));
		break;
	default:
		assert(0);
		break;
	}
	return r;
}

// NumberSyntax

Syntax::Type NumberSyntax::getType() const
{
	return Syntax::NUMBER;
}

const char *NumberSyntax::getName() const
{
	return "number";
}

bool NumberSyntax::test(const char *v, size_t len) const
{
	// ws*{NaN|[-]Infinity|[-]digit+[.digit+]}
	const char *p = v;
	int state = 0;
	bool r = true;
	while (r && p < (v + len) && *p != '\0') {
		switch (state) {
		case 0:
			if (isspace(*p))
				state = 0;
			else if (*p == '-')
				state = 1;
			else if (isdigit(*p))
				state = 2;
			else if(*p=='I')
				state = 4;
			else if(*p=='N')
				state = 5;
			else
				r = false;
			break;
		case 1:
			if (isdigit(*p))
				state = 2;
			else if(*p=='I')
				state = 4;
			else if(*p=='N')
				state = 5;
			else
				r = false;
			break;
		case 2:
			if (isdigit(*p))
				state = 2;
			else if (*p == '.')
				state = 3;
			else
				r = false;
			break;
		case 3:
			if (isdigit(*p))
				state = 2;
			else
				r = false;
			break;
		case 4:
			r = strncmp(p, "nfinity", 7)==0;
			p = v + len - 1;
			break;
		case 5:
			r = strncmp(p, "aN", 2)==0;
			p = v + len - 1;
			break;
		}
		++p;
	}
	return r && state > 1;
}

bool hasDecimal(const char *p, size_t l)
{
	while(l>0) {
		if (*p!='.' && *p!='0') return true;
		p++;
		l--;
	}
	return false;
}

int NumberSyntax::compare(const char *p1, size_t l1, const char *p2, size_t l2) const
{
	// ws*{NaN|[-]Infinity|[-]digit+[.digit+]}
	while (l1 > 0 && isspace(*p1)) { ++p1; --l1; } // Whitespace
	while (l2 > 0 && isspace(*p2)) { ++p2; --l2; } // Whitespace
	bool n1 = p1[0]=='N' && p1[1]=='a' && p1[2]=='N'; // NaN
	bool n2 = p2[0]=='N' && p2[1]=='a' && p2[2]=='N'; // NaN
	if (n1 && n2) return 0;
	if (n1 ^ n2) return (n1 ? -1 : +1);
	bool m1 = *p1 == '-'; // Minus
	bool m2 = *p2 == '-'; // Minus
	if (m1 ^ m2) return (m1 ? -1 : +1);
	if (m1) { ++p1;	--l1; ++p2; --l2; }
	bool i1 = *p1 == 'I'; // Infinity
	bool i2 = *p2 == 'I'; // Infinity
	if (i1 && i2) return 0;
	if (i1) return (m1 ? -1 : +1);
	if (i2) return (m1 ? +1 : -1);
	while (l1 > 0 && *p1=='0') { ++p1; --l1; } // Leading zeros
	while (l2 > 0 && *p2=='0') { ++p2; --l2; } // Leading zeros
	const char *d1= p1;
	const char *d2= p2;
	while (d1<p1+l1 && *d1!='.') { ++d1; }
	while (d2<p2+l2 && *d2!='.') { ++d2; }
	int c= (d1-p1)-(d2-p2);
	if (c==0) {
		// Compare integer part
		while (c==0 && p1<d1) {
			c= *p1++ - *p2++;
			l1--; l2--;
		}
		if (c==0) {
			bool hd1= hasDecimal(p1, l1);
			bool hd2= hasDecimal(p2, l2);
			if (!hd1 && !hd2) return 0;
			if (hd1 ^ hd2) return (hd1 ? (m1 ? -1 : +1) : (m1 ? +1 : -1) );
			p1++; l1--;
			p2++; l2--;
			// Compare decimal part
			while (c==0 && l1>0 && l2>0) {
				c= *p1++ - *p2++;
				l1--; l2--;
			}
			if(c==0) c= l2-l1;
		}
	}
	return (m1 ? -c : +c);
}

int NumberSyntax::bt_compare(Db *db, const Dbt *dbt1, const Dbt *dbt2) const
{
	UNUSED(db);

	// The first byte of the key is the prefix.
	// From this we can tell where the value starts.
	char *p1 = (char*)dbt1->get_data();
	char *p2 = (char*)dbt2->get_data();
	if (*p1 == *p2) {
		// Compare the structure component of the keys.
		Index index;
		index.setFromPrefix(*p1);
		size_t l1 = dbt1->get_size();
		size_t l2 = dbt2->get_size();
		size_t skl = structureKeyLength(index, l1);
		int r = ::memcmp(dbt1->get_data(), dbt2->get_data(), skl);
		if (r == 0) {
			if (l1 > skl && l2 > skl) {
				// Compare the value component of the keys,
				p1 += skl;
				p2 += skl;
				l1 -= skl;
				l2 -= skl;
				return compare(p1, l1, p2, l2);
			} else {
				return l1 -l2;
			}
		} else {
			return r;
		}
	} else {
		return *p1 - *p2;
	}
}

Database::bt_compare_fn NumberSyntax::get_bt_compare() const
{
	return static_bt_compare;
}

int NumberSyntax::static_bt_compare(Db *db, const Dbt *dbt1, const Dbt *dbt2)
{
	static NumberSyntax syntax;
	return syntax.bt_compare(db, dbt1, dbt2);
}

int NumberSyntax::structureKeyLength(const Index &index, size_t keyLength) const
{
	UNUSED(keyLength);
	int r = 0;
	switch (index.getKey()) {
	case Index::KEY_PRESENCE:
	case Index::KEY_EQUALITY:
		switch (index.getPath()) {
		case Index::PATH_NODE:
			r = 5;
			break;
		case Index::PATH_EDGE:
			r = 9;
			break;
		default:
			assert(0);
			break;
		}
		break;
	default:
		break;
	}
	return r;
}

const char *NumberSyntax::minimumValue() const
{
	return "-Infinity";
}

const char *NumberSyntax::maximumValue() const
{
	return "Infinity";
}

size_t NumberSyntax::countKeys(const Index &index, const char *valueBuffer, size_t valueLength) const
{
	UNUSED(index); 
	UNUSED(valueBuffer); 
	UNUSED(valueLength);
	return 1;
}

KeyGenerator::Ptr NumberSyntax::getKeyGenerator(const Index &index, const char *valueBuffer, size_t valueLength) const
{
	return KeyGenerator::Ptr(new SingleKeyGenerator(valueBuffer, valueLength));
}


