#include <time.h>
#include "test.h"

#define FF_OPTION						0
#define FF_OPERATION				1	
#define FF_A								2
#define FF_B								3
#define FF_C								4
#define FF_D								5
#define FF_ROW							6
#define FF_COL							7

static void TestAdd(matrix *A, matrix *B, char *Option, int *Filled);
static void TestSubtract(matrix *A, matrix *B, char *Option, int *Filled);
static void TestMultiply(matrix *A, matrix *B, char *Option, int *Filled);
static void TestInsertRows(matrix *A, matrix *B, int Row, char *Option, int *Filled);
static void TestInsertColumns(matrix *A, matrix *B, int Column, char *Option, int *Filled);
static void TestMultiplyVector(matrix *A, matrix *B, int* C, int *D, int Row, int Column, int Size, int *Filled);
static void TestMatrixEqualMatrix(matrix *A, matrix *B, int* Filled);

static int ReadAllocationOption(char* Option, int* Filled);
static void SetPointers(matrix **A, matrix **B, int **C, int **D, int* Filled);
static void ShowArray(int *array, int Count, char* name);
static void SetDefaults(int* Filled, int* Row, int* Col);

int main(int argc, char* argv[])
{
	FILE* input;
		
	char Desc[] = "string option, string operation, matrix A, matrix B, arrayi C, arrayi D, int row, int column";
	char Operation[128], Option[128];
	matrix A, B;
	int *C, *D, *Filled;
	int CCount, DCount, Row, Column;
	MemoryManager mem;
	
	if (! (input = ParseCmdLine(argc,argv))) return 0;
	mem = CreateMemoryManager(5, 2, 0, 0);
	
	while (ParseStructure(input, Desc, &Filled, &mem, Option, Operation, &A, &B, &C, &CCount, &D, &DCount, &Row, &Column))
	{
		SetDefaults(Filled, &Row, &Column);
		if (! Filled[FF_OPERATION])
		{
			printf("Warning: No operation code specified. Skipping problem.\n");
		}
		else if (! strcmp(Operation, "AddMatrix"))
		{
			TestAdd(&A, &B, Option, Filled);
		}
		else if (! strcmp(Operation, "SubtractMatrix"))
		{
			TestSubtract(&A, &B, Option, Filled);
		}
		else if (! strcmp(Operation, "MultiplyMatrix"))
		{
			TestMultiply(&A, &B, Option, Filled);
		}
		else if (! strcmp(Operation, "MultiplyVector"))
		{
			TestMultiplyVector(&A, &B, C, D, Row, Column, CCount, Filled);
		}
		else if (! strcmp(Operation, "MatrixEqualMatrix"))
		{
			TestMatrixEqualMatrix(&A, &B, Filled);
		}
		else
		{
			printf("Warning: Unrecognized operation '%s'.\n", Operation);
		}
		printf("-------------------------------------------------------------------------------\n");
		FreeMemory(&mem);
		mem = CreateMemoryManager(10, 2, 0, 0);
	}
	FreeMemory(&mem);
	return 0;
}

void TestAdd(matrix *A, matrix *B, char *Option, int *Filled)
{
	int Alloc;
	matrix Destination;
	printf("Testing addition A + B...\n");
	SetPointers(&A, &B, 0, 0, Filled);
	ShowMatrix(A, "A");
	ShowMatrix(B, "B");		
	Alloc = ReadAllocationOption(Option, Filled);
	
	if (Alloc < 0)
	{
		AllocateMatrix(&Destination, NumberOfRows(*A), NumberOfColumns(*A));
		AddMatrix(A, B, &Destination);		
	}
	else
	{
		Destination = AddMatrix(A, B, (matrix*) Alloc);
	}
	ShowMatrix(&Destination, "Result");
	DeallocateMatrix(&Destination);
}
	
void TestSubtract(matrix *A, matrix *B, char *Option, int *Filled)
{
	int Alloc;
	matrix Destination;
	printf("Testing subtraction A - B...\n");
	SetPointers(&A, &B, 0, 0, Filled);
	ShowMatrix(A, "A");
	ShowMatrix(B, "B");		
	Alloc = ReadAllocationOption(Option, Filled);
	if (Alloc < 0)
	{
		AllocateMatrix(&Destination, NumberOfRows(*A), NumberOfColumns(*A));
		SubtractMatrix(A, B, &Destination);		
	}
	else
	{
		Destination = SubtractMatrix(A, B, (matrix*) Alloc);
	}
	ShowMatrix(&Destination, "Result");
	DeallocateMatrix(&Destination);
}

void TestMultiply(matrix *A, matrix *B, char *Option, int *Filled)
{
	int Alloc;
	matrix Destination;
	printf("Testing matrix multiplication A * B...\n");
	SetPointers(&A, &B, 0, 0, Filled);
	ShowMatrix(A, "A");
	ShowMatrix(B, "B");		
	Alloc = ReadAllocationOption(Option, Filled);
	if (Alloc < 0)
	{
		AllocateMatrix(&Destination, NumberOfRows(*A), NumberOfColumns(*B));
		MultiplyMatrix(A, B, &Destination);		
	}
	else
	{
		Destination = MultiplyMatrix(A, B, (matrix*) Alloc);
	}
	ShowMatrix(&Destination, "Result");
	DeallocateMatrix(&Destination);
}

void TestMultiplyVector(matrix *A, matrix *B, int* C, int *D, int Row, int Column, int Size, int *Filled)
{
	SetPointers(&A, &B, &C, &D, Filled);
	char *message;
	/*Determine the multiplication mode based on what is and isn't filled in.*/
	int Mode = 0, ModeSet = 0;
	if (Filled[FF_A] && Filled[FF_B]) 
	{
		printf("Testing vector multiplication A(%d,:) * B(:,%d)...\n", Row, Column);
		ShowMatrix(A, "A");
		ShowMatrix(B, "B");
		printf("Result: %d\n", MultiplyVector(A, B, Row, Column, 0));
	}
	else if (Filled[FF_A])
	{
		if (! Filled[FF_C]) C = D;
		printf("Testing vector multiplication A(%d,:) * V...\n", Row);
		ShowMatrix(A, "A");
		ShowArray(C, NumberOfColumns(*A), "V");
		printf("Result: %d\n", MultiplyVector(A, (matrix*) C, Row, MULTIPLYVECTOR_ARRAY, 0));
	}
	else if (Filled[FF_B])
	{
		if (! Filled[FF_C]) C = D;
		printf("Testing vector multiplication V * B(:,%d)...\n", Column);
		ShowArray(C, NumberOfColumns(*B), "V");
		ShowMatrix(B, "B");
		printf("Result: %d\n", MultiplyVector((matrix*) C, B, MULTIPLYVECTOR_ARRAY, Column, 0));
	}
	else
	{
		
		printf("Testing vector multiplication V1 * V2...\n");
		ShowArray(C, Size, "V1");
		ShowArray(D, Size, "V2");
		printf("Result: %d\n", MultiplyVector((matrix*) C, (matrix*) D, MULTIPLYVECTOR_ARRAY, MULTIPLYVECTOR_ARRAY, Size));
	}
}

void TestMatrixEqualMatrix(matrix *A, matrix *B, int* Filled)
{
	SetPointers(&A, &B, 0, 0, Filled);
	printf("Testing equality test A = B...\n");
	ShowMatrix(A, "A");
	ShowMatrix(B, "B");
	printf("Result: %d\n", MatrixEqualMatrix(A, B));
}


/*Interpret the how-to-allocate option for an operation that can either use
pre-allocated result storage, allocate its own, or allocate its own of a
specified type.*/
int ReadAllocationOption(char* Option, int* Filled)
{
	if (! Filled[FF_OPTION]) return -1;
	if (! strcmp(Option, "preallocate"))
	{
		return -1;
	}
	else if (! strcmp(Option, "allocate"))
	{
		return 0;
	}
	else if (! strcmp(Option, "type1"))
	{
		return 1;
	}
	else if (! strcmp(Option, "type2"))
	{
		return 2;
	}
	else if (! strcmp(Option, "type3"))
	{
		return 3;
	}
	else
	{
		printf("Warning: Unrecognized allocation option '%s'. Preallocating.\n", Option);
		return -1;
	}
}

/*Make sure that pointers to matrices and arrays are null if the arrays and
matrices were not given in the problem data.*/
void SetPointers(matrix **A, matrix **B, int **C, int **D, int* Filled)
{
	if ((! Filled[FF_A]) && A) *A = 0;
	if ((! Filled[FF_B]) && B) *B = 0;
	if ((! Filled[FF_C]) && C) *C = 0;
	if ((! Filled[FF_D]) && D) *D = 0;
}

/*Used by TestMultiplyVector to show array-type vectors.*/
void ShowArray(int *array, int Count, char* name)
{
	int i;
	if (! array)
	{
		printf("ShowArray: Null array pointer");
		return;
	}
	printf("%s\t", name);
	for (i = 0; i < Count; i++)
	{
		printf("%d\t", array[i]);
	}
	printf("\n");
}

void SetDefaults(int* Filled, int* Row, int* Col)
{
	if (! Filled[FF_ROW]) *Row = -1;
	if (! Filled[FF_COL]) *Col = -1;
}