//
// (C) Copyright 2011-2012 Sergey A. Babkin.
// This file is a part of Triceps.
// See the file COPYRIGHT for the copyright notice and license information
//
//
// Implementation of a simple FIFO storage.

#include <table/FifoIndex.h>
#include <type/TableType.h>

namespace TRICEPS_NS {

//////////////////////////// FifoIndex /////////////////////////

FifoIndex::FifoIndex(const TableType *tabtype, Table *table, const FifoIndexType *mytype) :
	Index(tabtype, table),
	type_(mytype),
	first_(NULL),
	last_(NULL),
	size_(0)
{ }

FifoIndex::~FifoIndex()
{
	// the Table will take care of the records
}

void FifoIndex::clearData()
{
	first_ = last_ = NULL;
	size_ = 0;
}

const IndexType *FifoIndex::getType() const
{
	return type_;
}

RowHandle *FifoIndex::begin() const
{
	if (type_->isReverse())
		return last_;
	else
		return first_;
}

RowHandle *FifoIndex::next(const RowHandle *cur) const
{
	if (cur == NULL || !cur->isInTable())
		return NULL;

	RhSection *rs = getSection(cur);
	if (type_->isReverse())
		return rs->prev_;
	else
		return rs->next_;
}

RowHandle *FifoIndex::last() const
{
	if (type_->isReverse())
		return first_;
	else
		return last_;
}

const GroupHandle *FifoIndex::nextGroup(const GroupHandle *cur) const
{
	return NULL;
}

const GroupHandle *FifoIndex::beginGroup() const
{
	return NULL;
}

const GroupHandle *FifoIndex::toGroup(const RowHandle *cur) const
{
	return NULL;
}

RowHandle *FifoIndex::find(const RowHandle *what) const
{
	// Find by sequential comparison of whole rows
	const Row *rwhat = what->getRow();
	RowHandle *curh = first_;
	while(curh != NULL) {
		if (type_->getTabtype()->rowType()->equalRows(rwhat, curh->getRow()))
			return curh;

		RhSection *rs = getSection(curh);
		curh = rs->next_;
	}
	return NULL; // not found
}

Index *FifoIndex::findNested(const RowHandle *what, int nestPos) const
{
	return NULL;
}

bool FifoIndex::replacementPolicy(RowHandle *rh, RhSet &replaced)
{
	size_t limit = type_->getLimit();
	if (limit == 0)
		return true; // no limit, nothing replaced

	// Check if there is any row already marked for replacement and present in this index, 
	// then don't push out another one.
	size_t subtract = 0;
	for (RhSet::iterator it = replaced.begin(); it != replaced.end(); ++it) {
		Index *rind = type_->findInstance(table_, *it);
		if (rind == this)
			++subtract; // it belongs here, so a record will be already pushed out
	}

	if (size_ - subtract >= limit && size_ >= subtract) { // this works well only with one-at-a-time inserts
		if (type_->isJumping()) {
			RowHandle *curh = first_;
			while(curh != NULL) {
				replaced.insert(curh); 

				RhSection *rs = getSection(curh);
				curh = rs->next_;
			}
		} else {
			replaced.insert(first_); 
		}
	}
		
	return true;
}

void FifoIndex::insert(RowHandle *rh)
{
	RhSection *rs = getSection(rh);

	if (first_ == NULL) {
		rs->next_ = NULL;
		rs->prev_ = NULL;
		first_ = last_ = rh;
	} else {
		rs->next_ = NULL;
		rs->prev_ = last_;
		RhSection *lastrs = getSection(last_);
		lastrs->next_ = rh;
		last_ = rh;
	}
	++size_;
}

void FifoIndex::remove(RowHandle *rh)
{
	RhSection *rs = getSection(rh);

	if (first_ == rh) {
		if (last_ == rh) {
			first_ = last_ = NULL; // that was the last row
		} else {
			first_ = rs->next_;
			RhSection *nextrs = getSection(first_);
			nextrs->prev_ = NULL;
		}
	} else if (last_ == rh) {
		last_ = rs->prev_;
		RhSection *prevrs = getSection(last_);
		prevrs->next_ = NULL;
	} else {
		RhSection *nextrs = getSection(rs->next_);
		RhSection *prevrs = getSection(rs->prev_);
		prevrs->next_ = rs->next_;
		nextrs->prev_ = rs->prev_;
	}
	rs->prev_ = rs->next_ = NULL;
	--size_;
}

void FifoIndex::aggregateBefore(Tray *dest, const RhSet &rows, const RhSet &already, Tray *copyTray)
{ 
	// nothing to do
}

void FifoIndex::aggregateAfter(Tray *dest, Aggregator::AggOp aggop, const RhSet &rows, const RhSet &future, Tray *copyTray)
{ 
	// nothing to do
}

bool FifoIndex::collapse(Tray *dest, const RhSet &replaced, Tray *copyTray)
{
	return true;
}

}; // TRICEPS_NS
