#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../pnheaders/general.h"
#include "../pnheaders/matrix.h"
#include "spnbox.h"

/*The answer that avpr returns is a pointer to this buffer. It is reset with each
call to avpr.*/
static char* buffer = 0;
static int bufferLength = 0;

static int CheckParams(int* vector, int length, int mode, char** chr);
static int GetVectorEl(void* vector, int i, int mode);
static int GetBufferLength(void* vector, int length, int mode, char* chr, char* bl, char* el);
static void FinalFree();

char* avpr(void *vector, int length, int mode, char* chr, int option)
{
	char element[64];
	
	if (mode & DISPLAY_MATRIX) length = MatrixLength(*((matrix*) vector));
	
	/*Fill in default parameters.*/
	if (! CheckParams(vector, length, mode, &chr)) return 0;
	
	
	/*If the vector is a matrix, get the length from it.*/
	
	/*Setup bl and el - some kind of beginning and terminating strings.*/
	char *bl = option ? "_{" : "";
	char *el = option ? "}" : "";
	
	/*Setup the destination buffer. Begin by determining the maximum length we
	will need. It is, at most, the number of nonzero entries in the vector times
	the sum of the lengths of the various string parts times the base-10 log of
	the length of the vector.*/	
	int i, newBufferLength = GetBufferLength(vector, length, mode, chr, bl, el);	
	
	//Allocate new space only if necessary.
	if (newBufferLength > bufferLength)
	{
		free(buffer);
		bufferLength = newBufferLength;
		buffer = tcalloc(bufferLength, sizeof(char));
	}		
	//The buffer defaults to an empty string.
	buffer[0] = '\0';
	/*If there are no nonzero entries, newBufferLength will be 1 and we should
	return an empty string.*/
	if (newBufferLength == 1)
	{
		return buffer;
	}
	
	int firstProcessed = 0;	
	for (i = 0; i < length; i++)
	{
		int c = GetVectorEl(vector, i, mode);
		if (! c) continue;
		if (! firstProcessed)
		{
			if (c == 1) 
			{
				sprintf(element, "%s%s%d%s", chr, bl, i + ((mode & DISPLAY_OFFSET) ? 1 : 0), el);
			}
			else if (c == -1)
			{
				sprintf(element, "-%s%s%d%s", chr, bl, i + ((mode & DISPLAY_OFFSET) ? 1 : 0), el);
			}
			else
			{
				sprintf(element, "%d%s%s%d%s", c, chr, bl, i + ((mode & DISPLAY_OFFSET) ? 1 : 0), el);
			}
			firstProcessed = 1;
		}
		else
		{
			if (c == 1)
			{
				sprintf(element, "+%s%s%d%s", chr, bl, i + ((mode & DISPLAY_OFFSET) ? 1 : 0), el);
			}
			else if (c > 0)
			{
				sprintf(element, "+%d%s%s%d%s", c, chr, bl, i + ((mode & DISPLAY_OFFSET) ? 1 : 0), el);
			}
			else if (c == -1)
			{
				sprintf(element, "-%s%s%d%s", chr, bl, i + ((mode & DISPLAY_OFFSET) ? 1 : 0), el);
			}
			else
			{
				sprintf(element, "%d%s%s%d%s", c, chr, bl, i + ((mode & DISPLAY_OFFSET) ? 1 : 0), el);
			}
		}
		strcat(buffer, element);
	}
	return buffer;
}

int GetBufferLength(void* vector, int length, int mode, char* chr, char* bl, char* el)
{
	/*The largest the new buffer will be will be related to the length required
	for each nonzero element. Each element's length will max out at: the number of 
	digits required to display the largest element in the vector plus the number 
	of digits required to display the largest nonzero index in the vector plus the 
	number of characters required to display the strings chr, bl, and el.*/
	int indexMax = 0, elementMax = 0;
	int indexDigits = 0, elementDigits = 1, i;
	int result = 0;
	for (i = 0; i < length; i++)
	{
		int c = GetVectorEl(vector, i, mode);
		if (c)
		{
			indexMax = i;
			result++;
			if (c > elementMax) elementMax = c;
			if (c < -elementMax) elementMax = -c;
		}
	}
	/*If there were no nonzero vectors, the buffer length just needs to be 1.*/
	if (! result) return 1;
	/*To find the number of digits required to display the maximum, do an
	approximate base-10 logarithm.*/
	elementMax++;
	while (elementMax)
	{
		elementMax /= 10;
		elementDigits++;
	}
	indexMax++;
	while (indexMax)
	{
		indexMax /= 10;
		indexDigits++;
	}
	/*Now, assume each nonzero element displays chr, bl, el, an index, and the
	element value:*/
	result *= (strlen(chr) + strlen(bl) + strlen(el) + indexDigits + elementDigits);
	/*Add 1 for the null terminator and return.*/
	return ++result;	
}

int GetVectorEl(void* vector, int i, int mode)
{
	if (mode & DISPLAY_MATRIX)
	{
		return GetMatrixEl((matrix*) vector, i % NumberOfRows(* ((matrix*) vector)), i / NumberOfRows(* ((matrix*) vector)));
	}
	else
	{
		return ((int*)vector)[i];
	}
}

int CheckParams(int* vector, int length, int mode, char** chr)
{
	/*vector and destination are both required.*/
	if (! vector)
	{
		merror(0, "AVPR: A required pointer was null");
		return 0;
	}
	/*length cannot be non-positive.*/
	if (length < 1)
	{
		merror(0, "AVPR: Vector length is non-positive");
		return 0;
	}
	/*If the chr is 0, fill it with the default, m.*/
	if ((! *chr) || (! **chr ))
	{
		*chr = "m";
	}
	return 1;
}
