/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "inc_iostream.h"
#include <string.h>
#include <MvList.hpp>
#include <ctype.h>

//
//  CONSTRUCTORS
//

MvList :: MvList (int protect)
{
	setListTypes (SequentialList, NoSortBy, NoSortType, NoCase);
	_first = _last = 0L; 
	_n = 0; 
	_protect = protect; 
	_curpos = -1;
}

MvList :: MvList (ListType lst, ListSortBy sb, ListSortType st, ListCase lc, int protect, MvElement *delem)
{
	setListTypes(lst, sb, st, lc);
	if (delem) 
		insertLastPrivate(delem);
	else {
		_first = _last = 0L;
		_n = 0;
	}
	first();
	_protect = protect;
}

//
// AUXILIARY FUNCTIONS
//

void MvList :: setListTypes(ListType lst, ListSortBy sb, ListSortType st, ListCase c)
{
	_list_type = lst;
	_list_case = c;
	_current = NULL;

	if (_list_type == SortedList)
	{
		switch (sb)
		{
		case SortListByName :
			_sort_by = sb;
			break;
		
		case SortListByCode :
			_sort_by = sb;
			break;
		
		default : break;
		}
		_sort_type = st;
	}
	else
	{
		_sort_by   = NoSortBy;
		_sort_type = NoSortType;
	}	
}


void MvList::removeAll()
{
	MvLink *curr, *outro;

	curr = _first;
	for (int i=0; i<_n; i++)
	{
		outro = curr->_next;
		MvElement *elem = curr->_self;

		delete curr;
		curr = outro;

		if ((!_protect) && elem->count() == 0)
			delete elem;
	}

	_n = 0;
	_first = _last = 0L;
}

void MvList::print(const char* titulo)
{
	int i;
	MvLink* curr;

	if (titulo) printf("%s\n",titulo);
	for (curr = _first, i = 0; curr; curr = curr->_next, i++)
	{
		printf("    %d) ", i);
		curr->_self->print();
	}
	if (i != _n) printf("MvList::print - Error in the list\n");
}


//
// EDIT LIST
//

void MvList::insertLast (MvElement* v)
{
	if( v == NULL )
		return;

	if (_list_type == SortedList) 
	{
		cout << "Error in MvList.insertLast: invalid function for sorted list";
		return;
	}

	insertLastPrivate(v);
}

void MvList::insertLastPrivate (MvElement* v)
{
	MvLink* last_last;

	if( v == NULL )
		return;

	_n++;
	last_last = _last;

	// create new element

	_last = new MvLink(v, last_last, NULL);

	if (_n == 1) _first = _last;
	else last_last->_next = _last;
}

void MvList::insert(int posit,  MvElement* new_elem)
{
	if (_list_type == SortedList) 
	{
		cout << "Error in MvList.insert: invalid function for sorted list\n";
		return;
	}

	insertPrivate(posit, new_elem);
}

void MvList::insertPrivate(int posit, MvElement* new_elem)
{
	if (posit < 0 || posit > _n)
	{
		cout << "Error in MvList.insert: invalid position "
	    	 << posit << "\n";
		return;
	}

	if( new_elem == NULL )
		return;

	// Insert last element

	if (posit == _n)
	{
		insertLastPrivate(new_elem);
		return;
	}

	// It is not the last element...

	else
	{
		int i;
		MvLink *post, *ant = _first;
		MvLink *new_entry;

		if (posit == 0)
		{
			post = _first;

			new_entry = new MvLink(new_elem, NULL, post);

			_first = new_entry;

			post->_previous = new_entry;
		}
		else
		{
			for (i = 1; i < posit; i++)
				ant = ant->_next;
			post = ant->_next;

			new_entry = new MvLink(new_elem, ant, post);

			ant->_next = new_entry;

			post->_previous = new_entry;
		}
	}
	_n++;
}

MvElement* MvList :: insertByType (MvElement* newelem)
{
	MvElement* elem;
	int same = 0;

	if( newelem == NULL )
		return NULL;

	if (_list_type != SortedList) 
	{
		cout << "Error in MvList.insertByType: invalid function for sequential list ";
		return newelem;
	}

	if (isEmpty())
	{
		insertFirstPrivate(newelem);
		return newelem;
	}

	if (_list_type == SortedList)
	{
		if(_sort_by == SortListByCode)
		{
			elem = findNearestCode (newelem->_elem_code);
			if(elem && elem->_elem_code == newelem->_elem_code) same = 1;
		}
		else if (_sort_by == SortListByName)
		{
			elem = findNearestName (newelem->_elem_name);
			if(elem && !strcmp(newelem->_elem_name, elem->_elem_name)) 
					same = 1;
		}

		// If singular sort, return nothing if we cannot insert
		// Program can use curr which points to the right place

		if (_sort_type == SortListSingular && same) return 0;
	}

	// Insert before the current item or last

	if (_current)	insertPrivate(_curpos,newelem);
	else		insertLastPrivate(newelem);

	return newelem;

}

int MvList::remove(int posit)
{
	MvElement *elem = extract( posit);
	if( !elem )
		return 0;
	if ((!_protect) && elem->count() == 0)
		delete elem;

	return 1;
}

MvElement* MvList::extract(int posit)
{
	int	i;
	MvLink *tokill, *tmp;

	if (posit == -1) return NULL;

	if (_n == 0){
		cout << "Error in MvList.out: empty list" << "\n";
		return NULL;
	}

	if (posit < 0 || posit >= _n){
		cout << "Error in MvList.out: invalid position "
		     << posit << "\n";
		return NULL;
	}

	if (posit == 0){
		tokill = _first;
		if (_n == 1)
			_first = _last = NULL; // Last element deleted
		else{
			_first = _first->_next;
			_first->_previous = NULL;
		}

		if (_n == 2) _last = _first;
	}

	else if (posit == _n-1){
		tokill = _last;
		_last = _last->_previous;
		_last->_next = NULL;
	}else{
		tokill = _first;
		for (i=0; i<posit; i++)
			tokill = tokill->_next;
		tmp = tokill->_previous;
		tmp->_next = tokill->_next;
		tmp = tokill->_next;
		tmp->_previous = tokill->_previous;
	}

	_n--;

	/* if removing _current reset */
	if(tokill == _current) {
		_current = _current->_next;
		if(!_current) _curpos = -1;
	}
		
	MvElement *elem = tokill->_self;
	delete tokill;
return elem;
}
void MvList :: sort()
{
	// execute sort algorithm

	MvLink *current = _first, *next;
	int i;
	for ( i = 1; i <= _n; i++)
	{
		next = current->_next;
		sort(i, current);
		current = next; 
	}

	// update _curpos and _last

	_curpos = (_first ? 0 : -1);
	current = _last = _first;

	if (!_last) return;
	for (i = 1; i < _n; i++)
		current = current->_next;
	current->_next = NULL;
	_last = current;
	_first->_previous = NULL;
}

// Bubble sort. This function will finish
// with a sorted list of size pos+2

/* void MvList :: sort(int pos, MvLink* toinsert)
{
	if (pos <= 1) return;

	MvLink *current = _first, *previous = NULL;
	for (int i = 1; i < pos; i++)
	{
		if (compare(toinsert, current) < 0)
		{
			if (current == _first)
			{
				_first = toinsert;
				toinsert->_next = current;
			}
			else
			{
				previous->_next = toinsert;
				toinsert->_next = current;
			}
			break;
			
		}
		toinsert->_previous = previous;
		current->_previous = toinsert;
		previous = current;
		current = current->_next;
	}
}
*/


void MvList :: sort(int pos, MvLink* toinsert)
{
	if (pos <= 1) return;

	MvLink *current = _first, *previous = NULL;
	MvLink	*cnext = NULL,
		*tprev = NULL;

	if( toinsert == NULL )
		return;

	for (int i = 1; i < pos; i++)
	{
		if (compare(toinsert, current) < 0){
			if( current == _first )
				_first = toinsert;

			cnext = current->_next;
			tprev = toinsert->_previous;

			toinsert->_previous = current->_previous;
			current->_next = toinsert->_next;
			toinsert->_next = ( cnext == toinsert ) ? ( current ) : ( cnext );
			current->_previous = ( tprev == current ) ? ( toinsert ) : ( tprev );

			if( cnext != NULL && cnext != toinsert )
				cnext->_previous = toinsert;
			if( tprev != NULL && tprev != current )
				tprev->_next = current;
			if( current->_next != NULL )
				current->_next->_previous = current;
			if( toinsert->_previous != NULL )
				toinsert->_previous->_next = toinsert;
			break;
		}
		previous = current;
		current = current->_next;
			
	}
}


//
// INQUIRIES
//

MvElement* MvList::get(int posit)
{
	MvElement* elem;
	int i = 0;

	if (_n == 0 || (posit < 0 || posit >= _n))
		return NULL;

	for(elem = first(), i=0; elem; elem = next(), i++) 
		if(posit == i) return elem;

	return 0;
}

MvElement* MvList::first ()
{
	_current = _first;
	_curpos = 0;
	return ( _current? _current->_self : NULL);
}

MvElement* MvList::next ()
{
	if (_current == NULL) return(NULL);

	_current =  _current->_next;
	_curpos++;
	return (_current ? _current->_self : NULL);
}

MvElement* MvList::previous ()
{
	if (_current == NULL) return(NULL);

	_current =  _current->_previous;
	_curpos--;
	return (_current ? _current->_self : NULL);
}

int MvList :: positionOf (MvElement* elem)
{
	MvElement* curr;
	int i;

	for (curr = first(), i = 0; curr; curr = next(), i++)
		if (elem == curr) break;

	return ((elem == curr) ? i : -1 );
}

MvElement* MvList :: findByCode (int code)
{
	MvElement* elem;

	if(!_first) return 0;

	for (elem = first(); elem; elem = next())
	{
		if(elem->_elem_code == code) return elem;
		if (_sort_by == SortListByCode && elem->_elem_code > code) return 0;
	}
	return 0;
}

MvElement * MvList :: findByName (const char *name)
{
	if(!name) return 0;
	if (!_first) return 0;

	MvElement *elem=0;
	char *n = new char[strlen(name)+1];
	strcpy(n, name);
	convertCase(n);
	for (elem = first(); elem; elem = next())
	{
		if (!elem->_elem_name) continue;
		int cmp = strcmp(n, elem->_elem_name);

		if(!cmp)
			break;
		if (_sort_by == SortListByName && cmp<0) {
			elem = 0;
			break;
		}
	}
	delete [] n;
	return elem;
}

MvElement * MvList :: findNearestCode (int code)
{
	MvElement *elem;

	if (!_first) return 0;
	if (_sort_by != SortListByCode) return 0;

	for (elem = first(); elem; elem = next())
	{
		if(elem->_elem_code > code) return elem;
	}
	return 0;
}

MvElement* MvList :: findNearestName (const char *name)
{
	if(!name) return 0;
	if (!_first) return 0;
	if (_sort_by != SortListByName) return 0;

	MvElement* elem=0;
	char *n = new char[strlen(name)+1];
	strcpy(n, name);
	convertCase(n);

	for (elem = first(); elem; elem = next())
	{
		if (strcmp (n, elem->_elem_name) < 0) break;
	}
	delete [] n;
	return elem;
}

int MvList :: compare(MvLink* link1, MvLink* link2)
{
	
	if( link1 == NULL ){
		if( link2 == NULL )
			return 0;
		else
			return -1;
	}
	else{
		if( link2 == NULL )
			return 1;
	}
	if (_list_type != SortedList || _sort_by == SortListByCode)
	{
		int code1 = link1->_self->getElemCode();
		int code2 = link2->_self->getElemCode();
		return (code1 == code2 ? 0 : (code1 < code2 ? -1 : 1));
	}
	else
		return strcmp(link1->_self->getElemName(), link2->_self->getElemName());
	
}

// REPLACE, REMOVE FROM LIST

// Replace current link
void  MvList :: replaceCurr(MvElement *newelem)
{
	if (remove(_curpos))
		insertPrivate(_curpos,newelem);
}

// Remove	- but don't free - current link
// 			- return ptr to link for delete

void  MvList :: removeCurr()
{
	remove(_curpos);
}


void MvList :: removeByCode (int code)
{
	MvElement* elem = findByCode(code);
	if (elem) removeCurr();
}

void MvList :: removeByName (const char *name)
{
	MvElement* elem = findByName(name);
	if (elem) removeCurr();
}


void MvList :: convertCase (char *data)
{
	int i;

	if( data == NULL )
		return;

	switch (_list_case)
	{
		case NoCase : break;

		case LowerCase :
			for (i=0; i<strlen(data); i++) 
				data[i] = tolower(data[i]);
			break;

		case UpperCase :
			for (i=0; i<strlen(data); i++) 
				data[i] = toupper(data[i]);
			break;
		
		default : break;
	}
}

