/*
 * Copyright 2002-2008, Haiku Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Tyler Dauwalder
 */

/*!
	\file QueryPredicate.cpp
	BQuery predicate helper classes implementation.
*/

#include "QueryPredicate.h"

#include <ctype.h>

#include <UnicodeChar.h>


namespace BPrivate {
namespace Storage {

// #pragma mark - QueryNode


QueryNode::QueryNode()
{
}


QueryNode::~QueryNode()
{
}


// #pragma mark - LeafNode


LeafNode::LeafNode()
{
}


LeafNode::~LeafNode()
{
}


uint32
LeafNode::Arity() const
{
	return 0;
}


status_t
LeafNode::SetChildAt(QueryNode *child, int32 index)
{
	return B_BAD_VALUE;
}


QueryNode *
LeafNode::ChildAt(int32 index)
{
	return NULL;
}


// #pragma mark - UnaryNode


UnaryNode::UnaryNode()
	:
	fChild(NULL)
{
}


UnaryNode::~UnaryNode()
{
	delete fChild;
}


uint32
UnaryNode::Arity() const
{
	return 1;
}


status_t
UnaryNode::SetChildAt(QueryNode *child, int32 index)
{
	status_t error = B_OK;
	if (index == 0) {
		delete fChild;
		fChild = child;
	} else
		error = B_BAD_VALUE;
	return error;
}


QueryNode *
UnaryNode::ChildAt(int32 index)
{
	QueryNode *result = NULL;
	if (index == 0)
		result = fChild;
	return result;
}


// #pragma mark - BinaryNode


BinaryNode::BinaryNode()
	:
	fChild1(NULL),
	fChild2(NULL)
{
}


BinaryNode::~BinaryNode()
{
	delete fChild1;
	delete fChild2;
}


uint32
BinaryNode::Arity() const
{
	return 2;
}


status_t
BinaryNode::SetChildAt(QueryNode *child, int32 index)
{
	status_t error = B_OK;
	if (index == 0) {
		delete fChild1;
		fChild1 = child;
	} else if (index == 1) {
		delete fChild2;
		fChild2 = child;
	} else
		error = B_BAD_VALUE;
	return error;
}


QueryNode *
BinaryNode::ChildAt(int32 index)
{
	QueryNode *result = NULL;
	if (index == 0)
		result = fChild1;
	else if (index == 1)
		result = fChild2;
	return result;
}


// #pragma mark - AttributeNode


AttributeNode::AttributeNode(const char *attribute)
	:
	fAttribute(attribute)
{
}


status_t
AttributeNode::GetString(BString &predicate)
{
	predicate.SetTo(fAttribute);
	return B_OK;
}


// #pragma mark - StringNode


StringNode::StringNode(const char *value, bool caseInsensitive)
{
	if (value == NULL)
		return;

	if (caseInsensitive) {
		while (uint32 codePoint = BUnicodeChar::FromUTF8(&value)) {
			char utf8Buffer[4];
			char *utf8 = utf8Buffer;
			if (BUnicodeChar::IsAlpha(codePoint)) {
				uint32 lower = BUnicodeChar::ToLower(codePoint);
				uint32 upper = BUnicodeChar::ToUpper(codePoint);
				if (lower == upper) {
					BUnicodeChar::ToUTF8(codePoint, &utf8);
					fValue.Append(utf8Buffer, utf8 - utf8Buffer);
				} else {
					fValue << "[";
					BUnicodeChar::ToUTF8(lower, &utf8);
					fValue.Append(utf8Buffer, utf8 - utf8Buffer);
					utf8 = utf8Buffer;
					BUnicodeChar::ToUTF8(upper, &utf8);
					fValue.Append(utf8Buffer, utf8 - utf8Buffer);
					fValue << "]";
				}
			} else if (codePoint == L' ') {
				fValue << '*';
			} else {
				BUnicodeChar::ToUTF8(codePoint, &utf8);
				fValue.Append(utf8Buffer, utf8 - utf8Buffer);
			}
		}
	} else {
		fValue = value;
		fValue.ReplaceAll(' ', '*');
	}
}


status_t
StringNode::GetString(BString &predicate)
{
	BString escaped(fValue);
	escaped.CharacterEscape("\"\\'", '\\');
	predicate.SetTo("");
	predicate << "\"" << escaped << "\"";
	return B_OK;
}


// #pragma mark - DateNode


DateNode::DateNode(const char *value)
	:
	fValue(value)
{
}


status_t
DateNode::GetString(BString &predicate)
{
	BString escaped(fValue);
	escaped.CharacterEscape("%\"\\'", '\\');
	predicate.SetTo("");
	predicate << "%" << escaped << "%";
	return B_OK;
}


// #pragma mark - ValueNode


template<>
status_t
ValueNode<float>::GetString(BString &predicate)
{
	char buffer[32];
	union {
		int32 asInteger;
		float asFloat;
	} value;
	value.asFloat = fValue;
//	int32 value = *reinterpret_cast<int32*>(&fValue);
	sprintf(buffer, "0x%08" B_PRIx32, value.asInteger);
	predicate.SetTo(buffer);
	return B_OK;
}


template<>
status_t
ValueNode<double>::GetString(BString &predicate)
{
	char buffer[32];
	union {
		int64 asInteger;
		double asFloat;
	} value;
//	int64 value = *reinterpret_cast<int64*>(&fValue);
	value.asFloat = fValue;
	sprintf(buffer, "0x%016" B_PRIx64, value.asInteger);
	predicate.SetTo(buffer);
	return B_OK;
}


// #pragma mark - SpecialOpNode


SpecialOpNode::SpecialOpNode(query_op op)
	:
	fOp(op)
{
}


status_t
SpecialOpNode::GetString(BString &predicate)
{
	return B_BAD_VALUE;
}


// #pragma mark - UnaryOpNode


UnaryOpNode::UnaryOpNode(query_op op)
	:
	fOp(op)
{
}


status_t
UnaryOpNode::GetString(BString &predicate)
{
	status_t error = (fChild ? B_OK : B_BAD_VALUE);
	if (error == B_OK) {
		if (fOp == B_NOT) {
			BString childString;
			error = fChild->GetString(childString);
			predicate.SetTo("(!");
			predicate << childString << ")";
		} else
			error = B_BAD_VALUE;
	}
	return error;
}


// #pragma mark - BinaryOpNode


BinaryOpNode::BinaryOpNode(query_op op)
	:
	fOp(op)
{
}


status_t
BinaryOpNode::GetString(BString &predicate)
{
	status_t error = (fChild1 && fChild2 ? B_OK : B_BAD_VALUE);
	BString childString1;
	BString childString2;
	if (error == B_OK)
		error = fChild1->GetString(childString1);
	if (error == B_OK)
		error = fChild2->GetString(childString2);
	predicate.SetTo("");
	if (error == B_OK) {
		switch (fOp) {
			case B_EQ:
				predicate << "(" << childString1 << "=="
					<< childString2 << ")";
				break;
			case B_GT:
				predicate << "(" << childString1 << ">"
					<< childString2 << ")";
				break;
			case B_GE:
				predicate << "(" << childString1 << ">="
					<< childString2 << ")";
				break;
			case B_LT:
				predicate << "(" << childString1 << "<"
					<< childString2 << ")";
				break;
			case B_LE:
				predicate << "(" << childString1 << "<="
					<< childString2 << ")";
				break;
			case B_NE:
				predicate << "(" << childString1 << "!="
					<< childString2 << ")";
				break;
			case B_CONTAINS:
				if (StringNode *strNode = dynamic_cast<StringNode*>(fChild2)) {
					BString value;
					value << "*" << strNode->Value() << "*";
					error = StringNode(value.String()).GetString(childString2);
				}
				if (error == B_OK) {
					predicate << "(" << childString1 << "=="
						<< childString2 << ")";
				}
				break;
			case B_BEGINS_WITH:
				if (StringNode *strNode = dynamic_cast<StringNode*>(fChild2)) {
					BString value;
					value << strNode->Value() << "*";
					error = StringNode(value.String()).GetString(childString2);
				}
				if (error == B_OK) {
					predicate << "(" << childString1 << "=="
						<< childString2 << ")";
				}
				break;
			case B_ENDS_WITH:
				if (StringNode *strNode = dynamic_cast<StringNode*>(fChild2)) {
					BString value;
					value << "*" << strNode->Value();
					error = StringNode(value.String()).GetString(childString2);
				}
				if (error == B_OK) {
					predicate << "(" << childString1 << "=="
						<< childString2 << ")";
				}
				break;
			case B_AND:
				predicate << "(" << childString1 << "&&"
					<< childString2 << ")";
				break;
			case B_OR:
				predicate << "(" << childString1 << "||"
					<< childString2 << ")";
				break;
			default:
				error = B_BAD_VALUE;
				break;
		}
	}
	return error;
}


// #pragma mark - QueryStack


QueryStack::QueryStack()
{
}


QueryStack::~QueryStack()
{
	for (int32 i = 0; QueryNode *node = (QueryNode*)fNodes.ItemAt(i); i++)
		delete node;
}


status_t
QueryStack::PushNode(QueryNode *node)
{
	status_t error = (node ? B_OK : B_BAD_VALUE);
	if (error == B_OK) {
		if (!fNodes.AddItem(node))
			error = B_NO_MEMORY;
	}
	return error;
}


QueryNode *
QueryStack::PopNode()
{
	return (QueryNode*)fNodes.RemoveItem(fNodes.CountItems() - 1);
}


status_t
QueryStack::ConvertToTree(QueryNode *&rootNode)
{
	status_t error = _GetSubTree(rootNode);
	if (error == B_OK && !fNodes.IsEmpty()) {
		error = B_BAD_VALUE;
		delete rootNode;
		rootNode = NULL;
	}
	return error;
}


status_t
QueryStack::_GetSubTree(QueryNode *&rootNode)
{
	QueryNode *node = PopNode();
	status_t error = (node ? B_OK : B_BAD_VALUE);
	if (error == B_OK) {
		uint32 arity = node->Arity();
		for (int32 i = (int32)arity - 1; error == B_OK && i >= 0; i--) {
			QueryNode *child = NULL;
			error = _GetSubTree(child);
			if (error == B_OK) {
				error = node->SetChildAt(child, i);
				if (error != B_OK)
					delete child;
			}
		}
	}
	// clean up, if something went wrong
	if (error != B_OK && node) {
		delete node;
		node = NULL;
	}
	rootNode = node;
	return error;
}


}	// namespace Storage
}	// namespace BPrivate
