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

static char* buffer = 0;
static int bufferLength = 0;

static int GetBufferLength(void* vector, int length, int mode, char* op, char* cl);
static int GetVectorEl(void* vector, int i, int mode);
static int CheckParams(void* vector, int length, int mode, int option);

char* fvpr(void* vector, int length, int mode, int option)
{
	char *op, *cl;
	char element[64];
	
	/*If the vector is a matrix, get the length.*/
	if (mode & DISPLAY_MATRIX) length = MatrixLength(*((matrix*)vector));
	
	/*Check the parameters.*/
	if (! CheckParams(vector, length, mode, option)) return 0;
	
	/*Get the characters that will (maybe) be printed to begin and end the 
	vector.*/
	switch(option)
	{
	case 0:
		op = "";
		cl = "";
		break;
	case 1:
		if (length > 1)
		{
			op = "[";
			cl = "]";
		}
		else
		{
			op = "";
			cl = "";
		}
		break;
	case 2:
		op = "{";
		cl = "}";
		break;
	}
	
	/*Get the length of buffer that will be needed. Do not reallocate space unless
	we need to.*/
	int i, newBufferLength = GetBufferLength(vector, length, mode, op, cl);
	if (newBufferLength > bufferLength)
	{
		free(buffer);
		bufferLength = newBufferLength;
		buffer = tcalloc(bufferLength, sizeof(char));
	}
	buffer[0] = '\0';
	if (! length) return buffer;
	
	/*Do the printing.*/
	strcpy(buffer, op);
	for (i = 0; i < length; i++)
	{
		int c = GetVectorEl(vector, i, mode);
		sprintf(element, i ? ", %d" : "%d", c);
		strcat(buffer, element);
	}
	strcat(buffer, cl);
	if ((mode & DISPLAY_MATRIX) && NumberOfRows(*((matrix*)vector)) > 1)
	{
		strcat(buffer, "'");
	}
	return buffer;
}
	
int GetBufferLength(void* vector, int length, int mode, char* op, char* cl)
{
	if (length == 0) return 3;
	/*The buffer length will be, at most, the vector length times the length of
	the element separator plus the number of digits required to display the
	largest number in the vector, all plus the lengths of the beginning and ending
	separators.*/
	/*To get the number of digits, find the maximum and then do a simple logarithm.*/	
	int max = 1, digits = 1, i;
	for (i = 0; i < length; i++)
	{
		int c = GetVectorEl(vector, i, mode);
		if (c > max) max = c;
		if (-c > max) max = -c;
	}
	/*Perform a simple logarithm. The number of digits begins at 1, not zero, 
	because we are leaving space for a minus sign.*/
	
	while(max)
	{
		max /= 10;
		digits++;
	}
	
	return strlen(op) + strlen(cl) + ((strlen(", ") + digits) * length) + 1 + ((mode & DISPLAY_MATRIX) ? 1 : 0);
}

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

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