/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_connectivity.hxx"

#include "MacabHeader.hxx"
#include "MacabRecord.hxx"
#include "macabutilities.hxx"

#include <math.h>
#include <com/sun/star/sdbc/DataType.hpp>
#include <connectivity/dbconversion.hxx>

using namespace connectivity::macab;
using namespace com::sun::star::sdbc;
using namespace com::sun::star::util;
using namespace ::dbtools;

// -------------------------------------------------------------------------
MacabHeader::MacabHeader(const sal_Int32 _size, macabfield **_fields)
{
	sal_Int32 i;
	size = _size;
	fields = new macabfield *[size];
	for(i = 0; i < size; i++)
	{
		if(_fields[i] == NULL)
		{
			fields[i] = NULL;
		}
		else
		{
			/* The constructor duplicates the macabfields it gets because they
			 * are either deleted later or used for other purposes.
			 */
			fields[i] = new macabfield;
			fields[i]->type = _fields[i]->type;
			fields[i]->value = _fields[i]->value;
			if (fields[i]->value)
				CFRetain(fields[i]->value);
		}
	}
	
}

// -------------------------------------------------------------------------
MacabHeader::MacabHeader()
{
	size = 0;
	fields = NULL;
}

// -------------------------------------------------------------------------
MacabHeader::~MacabHeader()
{
}

// -------------------------------------------------------------------------
void MacabHeader::operator+= (const MacabHeader *r)
{
	/* Add one MacabHeader to another. Anything not already in the header is
	 * added to the end of it.
	 */
	sal_Int32 rSize = r->getSize();
	if(rSize != 0) // If the new header does actually have fields
	{
		/* If our header is currently empty, just copy all of the fields from
		 * the new header to this one.
		 */
		if(size == 0)
		{
			sal_Int32 i;
			size = rSize;
			fields = new macabfield *[size];
			for(i = 0; i < size; i++)
			{
				fields[i] = r->copy(i);
			}
		}

		/* Otherwise, only add the duplicates. We do this with a two-pass
		 * approach. First, find out how many fields to add, then reallocate
		 * the size of the fields array and add the old ones at the end.
		 * (More precisely, we create a _new_ fields array with the new length
		 * allocated to it, then get all of the fields from the current
		 * fields array to it, then copy the non-duplicates from the new
		 * header to the end.)
		 */
		else
		{
			sal_Int32 i;
			sal_Int32 numToAdd = 0, numAdded = 0;
			macabfield **newFields;
			for( i = 0; i < rSize; i++)
			{
				if(!contains(r->get(i)))
				{
					numToAdd++;
				}
			}
			
			newFields = new macabfield *[size+numToAdd];
			for(i = 0; i < size; i++)
			{
				newFields[i] = copy(i);
			}

			for( i = 0; i < rSize; i++)
			{
				if(!contains(r->get(i)))
				{
					newFields[size+numAdded] = r->copy(i);
					numAdded++;
					if(numAdded == numToAdd)
						break;
				}
			}

			releaseFields();
			delete [] fields;
			size += numAdded;
			fields = newFields;
		}
	}
}

// -------------------------------------------------------------------------
::rtl::OUString MacabHeader::getString(const sal_Int32 i) const
{
	::rtl::OUString nRet;

	if(i < size)
	{
	    if(fields[i] == NULL || fields[i]->value == NULL || CFGetTypeID(fields[i]->value) != CFStringGetTypeID())
        	return ::rtl::OUString();
	    try
	    {
        	nRet = CFStringToOUString( (CFStringRef) fields[i]->value);
	    }
	    catch(...){ }
	}

	return nRet;
}

// -------------------------------------------------------------------------
void MacabHeader::sortRecord()
{
	sortRecord(0,size);
}

// -------------------------------------------------------------------------
macabfield **MacabHeader::sortRecord(const sal_Int32 _start, const sal_Int32 _length)
{
	/* Sort using mergesort. Because it uses mergesort, it is recursive and
	 * not in place (so it creates a new array at every step of the
	 * recursion), so if you prefer to use a different sort, please feel
	 * free to implement it.
	 */
	macabfield** sorted = new macabfield *[_length];
	if(_length <= 2)
	{
		if(_length == 2)
		{
			if(compareFields(fields[_start], fields[_start+1]) > 0)
			{
				sorted[0] = get(_start+1);
				sorted[1] = get(_start);
			}
			else
			{
				sorted[0] = get(_start);
				sorted[1] = get(_start+1);
			}
		}
		else if(_length == 1)
		{
			sorted[0] = get(_start);
		}
	}
	else
	{
		sal_Int32 halfLength = floor(_length/2);
		sal_Int32 fp = 0, lp = 0;
		sal_Int32 i;
		macabfield **firstHalf = new macabfield *[halfLength];
		macabfield **lastHalf = new macabfield *[_length - halfLength];

		firstHalf = sortRecord(_start, halfLength);
		lastHalf = sortRecord(_start+halfLength, _length-halfLength);
		for(i = 0; i < _length; i++)
		{
			if(compareFields(firstHalf[fp],lastHalf[lp]) < 0)
			{
				sorted[i] = firstHalf[fp++];
				if(fp == halfLength)
				{
					for( i++; i < _length; i++)
					{
						sorted[i] = lastHalf[lp++];
					}
					break;
				}
			}
			else
			{
				sorted[i] = lastHalf[lp++];
				if(lp == _length - halfLength)
				{
					for( i++; i < _length; i++)
					{
						sorted[i] = firstHalf[fp++];
					}
					break;
				}
			}
		}
		if(_length == size)
		{
			fields = sorted;
		}
	}
	return sorted;
}

sal_Int32 MacabHeader::compareFields(const macabfield *_field1, const macabfield *_field2) 
{
	/* Comparing two fields in a MacabHeader is different than comparing two
	 * fields in a MacabRecord. It starts in the same way (if one of the two
	 * fields is NULL, it belongs after the other, so it is considered
	 * "greater"). But, then, all headers are CFStrings, no matter what
	 * type they claim to be (since they actually hold the expected type for
	 * the records with that header). That being said, all we have to do is
	 * the built-in CFStringCompare.
	 */
	if(_field1 == _field2)
		return 0;
	if(_field1 == NULL)
		return 1;
	if(_field2 == NULL)
		return -1;

	CFComparisonResult result = CFStringCompare(
		(CFStringRef) _field1->value,
		(CFStringRef) _field2->value,
		0); // 0 = no options (like ignore case)

	return (sal_Int32) result;
}

// -------------------------------------------------------------------------
sal_Int32 MacabHeader::getColumnNumber(const ::rtl::OUString s) const
{
	sal_Int32 i;
	for(i = 0; i < size; i++)
	{
		if(getString(i) == s)
			break;
	}

	if(i == size)
		i = -1;

	return i;
}

// -------------------------------------------------------------------------
MacabHeader *MacabHeader::begin()
{
	return this;
}

// -------------------------------------------------------------------------
MacabHeader::iterator::iterator ()
{
}

// -------------------------------------------------------------------------
MacabHeader::iterator::~iterator ()
{
}

void MacabHeader::iterator::operator= (MacabHeader *_record)
{
	id = 0;
	record = _record;
}

// -------------------------------------------------------------------------
void MacabHeader::iterator::operator++ ()
{
	id++;
}

// -------------------------------------------------------------------------
sal_Bool MacabHeader::iterator::operator!= (const sal_Int32 i) const
{
	return(id != i);
}

// -------------------------------------------------------------------------
sal_Bool MacabHeader::iterator::operator== (const sal_Int32 i) const
{
	return(id == i);
}

// -------------------------------------------------------------------------
macabfield *MacabHeader::iterator::operator* () const
{
	return record->get(id);
}

// -------------------------------------------------------------------------
sal_Int32 MacabHeader::end() const
{
	return size;
}

