#include "Query.h"

//
// DO NOT COMPILE!
// this does not match Query.h
// everything sould be n-ary, etc.
//

//
//
//
ResultList *
FuzzyWordQuery::Evaluate()
{
	// evaluate only once, please
	ResultList *result = cache[word];

	if(!result)
	{
		result = Expand();
		if(result)
		{
			cache[word] = result;
		}
	}

	if(result && result->Count() == 0)
	{
		result = 0;
	}
	return result;
}

//
//	l	r	and
//	----------------------
//	0	0	0
//	0	b	0
//	0	x	0
//	a	0	0
//	a	b	intersect(a,b)
//	a	x	a
//	x	0	0
//	x	b	b
//	x	x	x
//

ResultList *
AndQuery::Evaluate()
{
	ResultList *result = 0;
	ResultList *shorter = 0;

	operands.Start_Get();
	while(l)
	{
	}

	if(left && right)
	{
		ResultList *l = left->Evaluate();
		if(l)
		{
			ResultList *r = right->Evaluate();
			if(r)
			{
				if(l == ignore)
				{
					delete l;
					result = r;
				}
				else if(r == ignore)
				{
					delete r;
					result = l;
				}
				else
				{
					Intersection(*l, *r);
					result = l;
					delete r;
				}
			}
		}
	}
	return result;
}

//
// remove from r results not present in both l and r
// accumulate scores adequately in remaining results
//
void
AndQuery::Intersection(ResultList &l, ResultList &r)
{
	ResultList *result = new ResultList;
	HtVector *elements = l.elements();
	for(size_t i = 0; i < elements->Size(); i++)
	{
		DocMatch *match = (DocMatch *)(*elements)[i];
		DocMatch *confirm = r.find(match->GetId());
		if(confirm)
		{
			match->SetScore(match->GetScore()+confirm->GetScore());
			if(confirm->GetAnchor() < match->GetAnchor())
			{
				match->SetAnchor(confirm->GetAnchor());
			}
			match->AddLocations(confirm->GetLocations());
		}
		else
		{
			l.remove(match->GetId());
		}
	}
	elements.Release();
	delete elements;
}

void
BinaryOperatorQuery::PrintOn(ostream &out) const
{
	out << "(";
	if(left)
	{
		left->PrintOn(out);
	}
	else
	{
		out << "*nothing*";
	}
	out << " " << OperatorString() << " ";
	if(right)
	{
		right->PrintOn(out);
	}
	else
	{
		out << "*nothing*";
	}
	out << ")";
}


//	l	r	or
//	---------------------
//	0	0	0
//	0	b	b
//	0	x	x
//	a	0	a
//	a	b	union(a,b)
//	a	x	a
//	x	0	x
//	x	b	b
//	x	x	x
//

ResultList *
OrQuery::Evaluate()
{
	ResultList result = 0;
	if(left && right)
	{
		result = left->Evaluate();
		ResultList *r = right->Evaluate();
		if(result)
		{
			if(r)
			{
				if(result == ignore)
				{
					delete result;
					result = r;
				}
				else if(r != ignore)
				{
					Union(*result, *r);
					delete r;
				}
			}
		}
		else
		{
			result = r;
		}
	}
	return result;
}

//
// add to l the results in r with doc-id absent from l
// accumulate scores in remaining results
//
void
OrQuery::Union(ResultList &l, ResultList &r)
{
	HtVector *elements = r.elements();
	for(size_t i = 0; i < elements->Size(); i++)
	{
		DocMatch *match = (DocMatch *)(*elements)[i];
		DocMatch *previous = l.find(match->GetId());
		if(previous)
		{
			previous->AddScore(match.GetScore());
			if(match->GetAnchor() < previous->GetAnchor())
			{
				previous->SetAnchor(match->GetAnchor());
			}
			previous->AddLocations(match.GetLocations());
		}
		else
		{
			l.add(new DocMatch(match));
		}
	}
	elements->Release();
	delete elements;
}

//
//	l	r	not
//	-------------------------
//	0	0	0
//	0	b	0
//	0	x	0
//	a	0	a
//	a	b	diff(a,b)
//	a	x	a
//	x	0	x
//	x	b	x
//	x	x	x
//
ResultList *
NotQuery::Evaluate()
{
	ResultList *result = 0;
	if(left && right)
	{
		result = left->Evaluate();
		if(result && result != ignore)
		{
			ResultList *r = right->Evaluate();
			if(r && r != ignore)
			{
				Subtract(*result, *r);
				delete r;
			}
		}
	}
	return result;
}

//
// remove from l the results with doc-id present in r
//
void
NotQuery::Subtract(ResultList &l, ResultList &r)
{
	ResultList *result = new ResultList;
	HtVector *elements = r.elements();
	for(size_t i = 0; i < elements->Size(); i++)
	{
		DocMatch *match = (DocMatch *)(*elements)[i];
		DocMatch *previous = l.find(match->id);
		if(previous)
		{
			l.remove(match->id);
		}
	}
	elements.Release();
	delete elements;
}

String
NearQuery::OperatorString() const
{
	String s;
	s << "near[" << distance << "]";
}

//
//	l	r	nextTo
//	-----------------------
//	0	0	0
//	0	b	0
//	0	x	0
//	a	0	0
//	a	b	near(a, b)
//	a	x	a
//	x	0	0
//	x	b	b
//	x	x	x
//
ResultList *
ProximityQuery::Evaluate()
{
	ResultList *result = 0;
	if(left && right)
	{
		ResultList *l = left->Evaluate();
		if(l)
		{
			ResultList *r = right->Evaluate();
			if(r)
			{
				Near(*l, *r);
				result = l;
				delete r;
			}
			delete l;
		}
	}
	return result;
}

//
// for each url in l
// if there is not a matching url in r
// or the r'match-position != l'match-position + 1
// then remove that result from l
void
NextQuery::Near(ResultList &l, ResultList &r)
{
	ResultList *result = new ResultList;
	HtVector *elements = l.elements();
	for(size_t i = 0; i < elements->Size(); i++)
	{
		DocMatch *match = (DocMatch *)(*elements)[i];
		DocMatch *confirm = r.find(match->id);
		HtVector r;
		if(confirm)
		{
			MergeLocations(	match->Locations(),
					confirm->Locations(),
					r);
		}
		if(r.Size() == 0)
		{
			l.remove(match->GetId());
		}
		else
		{
			match->SetLocations(r);
			match->SetScore(r.Size());
		}
		elements.Release();
		delete elements;
	}
}

//
//: merge match positions in a 'next' operation
// each position of left operand match is tested against right operand positions
// if two contiguous positions are found, they are merged into a single one
// beginning at the begin of the left operand
// and ending and the end of the right operand
// 
size_t
NextQuery::MergeLocations(HtVector &p, HtVector &q, HtVector &r)
{
	size_t n = 0;
	for(size_t j = 0; j < p.Size(); j++)
	{
		Location &left = *p[j];
		for(size_t k = 0; k < q.Size(); k++)
		{
			Location &right = q[k];
			if(left.to + 1 == right.from)
			{
				r[n++] = new Location(left.from, right.to);
				break;
			}
		}
	}
}

void
NearQuery::Near(ResultList &l, ResultList &r)
{
	ResultList *result = new ResultList;
	HtVector *elements = l.elements();
	for(size_t i = 0; i < elements->Size(); i++)
	{
		DocMatch *match = (DocMatch *)(*elements)[i];
		DocMatch *confirm = r.find(match->id);
		HtVector r;
		size_t n = 0;
		if(confirm)
		{
			MergeLocations( match->Locations(),
					confirm->Locations(),
					r);
		}
		if(r.Size() == 0)
		{
			l.remove(match->GetId());
		}
		else
		{
			match->SetLocations(r);
			match->SetScore(r.Size()/2);
		}
		elements.Release();
		delete elements;
	}
}

//
//: merge match positions in a 'near' operation
// all combinations are tested; the pairs of positions near enough are kept
// 
void
NearQuery::MergeLocations(HtVector &p, HtVector &q, HtVector &r)
{
	size_t n = 0;
	for(size_t j = 0; j < p.Size(); j++)
	{
		Location &left = *p[j];
		for(size_t k = 0; k < q.Size(); k++)
		{
			Location &right = *q[k];
			size_t dist = right.from - left.to;
			if(dist < 1)
			{
				dist = left.from - right.to;
				if(dist < 1)
				{
					dist = 0;
				}
			}
			if(dist <= distance)
			{
				r[n++] = new Location(left);
				r[n++] = new Location(right);
			}
		}
	}
}


