
#include "class.h"


struct class * class_new() {

	struct class * classInstance 	= malloc( sizeof( struct class ) );

	return classInstance;

}

void class_reIndexLineNumbers( struct class * baseClass ) {

	int maxLineNumber 				= 0;

	struct array * methods			= baseClass->methods;

	int methodsCount				= array_length( methods );

	for (int i = 0; i < methodsCount; ++i)
	{
		struct method * methodInstance 	= array_get( methods, i );

		if( methodInstance->lineNumber > maxLineNumber ) {

			maxLineNumber	= methodInstance->lineNumber;

		}

	}

	maxLineNumber += 12;

	for (int i = 0; i < methodsCount; ++i)
	{
		struct method * methodInstance 	= array_get( methods, i );


		if( methodInstance->lineNumber == -1 ) {

			methodInstance->lineNumber 	= maxLineNumber;

			maxLineNumber 				+=	text_findNumberOfLineBreaks( methodInstance->body );

			maxLineNumber 				+= 6;

		}

	}


}


void class_updateThisArgument( struct array * arguments, char * baseClassName ) {

	struct text * newThisArgument = text_new( "" );

	text_append( newThisArgument, "struct " );

	text_append( newThisArgument, baseClassName );

	text_append( newThisArgument, " * this" );

	array_set( arguments, 0, newThisArgument->value );

	//printf("update this argument:	%s  %s\n	", firstArgument,  methodName);

}

int class_getMethodIndexByName( struct class * classInstance, char * methodName ) {

	struct array * methods 	= classInstance->methods;

	int count 				= array_length( methods );

	for (int i = 0; i < count; ++i)
	{
		
		struct method * currentMethod = array_get( methods, i );

		if( strcmp( currentMethod->methodName, methodName ) == 0 ) {

			return i;

		}


	}

	return -1;

}


void addMethodsToBaseClass( struct class * baseClass, struct class * extendedClass ) {

	struct array * methods			= extendedClass->methods;

	int methodsCount				= array_length( methods );

	for (int i = 0; i < methodsCount; ++i)
	{
		struct method * methodInstance 	= array_get( methods, i );

		struct method * methodCopy		= malloc( sizeof( struct method ) );

		methodCopy->methodName			= methodInstance->methodName;

		methodCopy->body				= methodInstance->body;

		methodCopy->returnType			= methodInstance->returnType;

		methodCopy->lineNumber			= -1;

		methodCopy->arguments			= methodInstance->arguments;


		int methodIndex					= class_getMethodIndexByName( baseClass, methodInstance->methodName );

		if( methodIndex == -1 ) {

			class_updateThisArgument( methodCopy->arguments, baseClass->className );

			array_add( baseClass->methods, methodCopy );

		} else {

			class_updateThisArgument( methodCopy->arguments, baseClass->className );

			//array_set( baseClass->methods, methodIndex, methodCopy );

		}

	}

}

void addPropertiesToBaseClass( struct class * baseClass, struct class * extendedClass ) {

	struct array * properties		= extendedClass->properties;

	int propertyCount				= array_length( properties );


	for (int i = 0; i < propertyCount; ++i)
	{

		struct property * propertyInstance 	= array_get( properties, i );

		int propertyIndex					= class_getPropertyIndexByName( baseClass, propertyInstance->propertyName );

		if( propertyIndex == -1 ) {

			array_add( baseClass->properties, propertyInstance );

		} else {

			array_set( baseClass->properties, propertyIndex, propertyInstance );

		}

	}

	//printf("processCurrent extend: %s  \n\n", extendedClass->className );

}


void class_extendClass( struct class * baseClass ) {

	if( baseClass->hasExtended == 1 ) {

		return;

	}

	struct array * extends		= baseClass->extends;

	int extendCount				= array_length( extends );


	//printf("extend: %s %i  \n\n\n\n\n", baseClass->className, extendCount );


	for ( int i = 0; i < extendCount; ++i )
	{



		char * extendName 				= text_removeWhiteSpaces( array_get( extends, i ) );


		struct class * extendedClass	= application_getClassByClassName( allClasses, extendName );


		if( extendedClass == NULL ) {

			printf("\n\n\n\n ERROR:			Class '%s' tries to extend class '%s', Class '%s' does not exist. \n\n\n\n", baseClass->className, extendName, extendName);

			exit( 0 );

		} 

		if( extendedClass->hasExtended == -1 ) {

			class_extendClass( extendedClass );

		}
/*
		addPropertiesToBaseClass( baseClass, extendedClass );

		addMethodsToBaseClass( baseClass, extendedClass );
*/
	}

	class_reIndexLineNumbers( baseClass );

	baseClass->hasExtended = 1;

}

int class_findClassByClassNameExists( array * classesArray, char * name ) {

	int count = array_length( classesArray );

	for (int i = 0; i < count; ++i)
	{
		struct class * currentClass = array_get( classesArray, i );

		//printf("			%s\n",currentClass->className );

		if ( strcmp( currentClass->className, name ) == 0 ) {

			return 1;

		}

	}

	return 0;

}

void class_extractMethod(	lexer * currentLexer,
							char * source, 
							int i, 
							struct class * classInstance, 
							struct array * classesArray, 
							struct array * functions ) {


	double key								= lexer_getKey( currentLexer, i );

	array * validPreviousTokens				= array_new();

	array_add( validPreviousTokens, "{");

	array_add( validPreviousTokens, "}");

	array_add( validPreviousTokens, ";");


	int previousKey							= lexer_findPreviouseTokenIndex( currentLexer, i, validPreviousTokens ) + 1;

	struct method * methodInstance			= malloc( sizeof( struct method ) );

	methodInstance->isSetter				= false;

	methodInstance->isGetter				= false;

										  	  method_extractMethodNameAndReturnType( previousKey, key, source, methodInstance );

										  	  //  &(int){ 0 } create inline pointer 
	methodInstance->bodyOriginal			= class_extractBodyFromSource( currentLexer, i, source, 0 );

	methodInstance->variables				= method_extractArguments( currentLexer, key, source, i, classInstance, methodInstance, classesArray );

	methodInstance->lineNumber				= text_countLineBreaks( source, (int) key );


	array_add( classInstance->methods, methodInstance );

}

char * class_extractBodyFromSource( lexer * currentLexer, int i, char * source, int bodyCloseIndex ) {

	int bodyOpenIndex 			= lexer_findNextTokenIndex( currentLexer, i, "{" );

	int bodyOpenKey 			= lexer_findNextToken( currentLexer, i, "{" );

	bodyCloseIndex 				= lexer_findBodyCloseIndex( currentLexer, bodyOpenIndex  );

	int bodyCloseKey 			= lexer_getKey( currentLexer, bodyCloseIndex );
	

	char * bodySource 			= text_slice( source, bodyOpenKey + 1, bodyCloseKey - 1 );

	return bodySource;

}


void class_extractProperty( lexer * currentLexer, int key, int i, char * source, struct class * classInstance ) {

	char * previousToken				= lexer_getToken( currentLexer, i - 1 );

	int hasValue						= -1;

	char * propertyValue				= "";

	char * propertySource 				= "";

	struct property * propertyInstance 	= malloc( sizeof( struct property ) );

	propertyInstance->propertyName		= NULL;

	propertyInstance->type				= "default";

	// todo this can be done more effectively
	//printf("previousToken:	%s\n", previousToken);


	int shift = 0;


	if ( strcmp( previousToken, "=" ) == 0 || strcmp( previousToken, "\"" ) == 0 ) {



		int IsTokenKey 					= lexer_findPreviousTokenIndex( currentLexer, i, "=" );

		int IsKey						= lexer_getKey( currentLexer, IsTokenKey );

		int keyBeforeIS					= lexer_getKey( currentLexer, IsTokenKey - 1 );

		propertyValue  					= text_slice( source, IsKey + 1, key - 1 );

		propertySource 					= text_removeWhiteSpaces( text_slice( source, keyBeforeIS + 1, IsKey - 1 ) );

		hasValue = 1;

	} else if ( strcmp( previousToken, ")" ) == 0 ) {


		shift	= 3;

		int IsTokenKey 					= lexer_findPreviousTokenIndex( currentLexer, i, "=" );

		int previousSemicolon			= lexer_findPreviousTokenIndex( currentLexer, i, ";" );


		if( previousSemicolon > IsTokenKey ) {

			printf("\n\n\n			This is an function. \n\n\n\n\n\n\n");

			int afterPreviousSemicolon				= lexer_getKey( currentLexer, previousSemicolon );

			int openArgumentKey 					= lexer_findNextToken( currentLexer, previousSemicolon, "(" );

			int closeArgumentKey 					= lexer_findNextToken( currentLexer, previousSemicolon, ")" );


			char * propertyName						= text_removeWhiteSpaces( text_slice( source, openArgumentKey + 1 , closeArgumentKey - 1 ) ) ;

			struct array * propertyNameParts		= text_split( propertyName, " \t" );


			propertyInstance->propertyName 			= property_extractPropertyName( propertyNameParts );


			printf("			function name			'%s'  %i  %i %i \n\n",propertyInstance->propertyName, openArgumentKey, closeArgumentKey, afterPreviousSemicolon);

			propertySource  						= text_removeWhiteSpaces( text_slice( source, (int)afterPreviousSemicolon + 1, key-1 ) );


			hasValue = 0;


			//propertyInstance->propertyName 	= "customName";

			propertyInstance->type				= "function";

			printf("A\n\n\n");
	

		} else {


			int IsKey						= lexer_getKey( currentLexer, IsTokenKey );

			int keyBeforeIS					= lexer_getKey( currentLexer, IsTokenKey - 1 );


			propertyValue  					= text_slice( source, IsKey + 1, key - 1 );

			propertySource  				= text_removeWhiteSpaces( text_slice( source, keyBeforeIS + 1, IsKey - 1 ) );

			hasValue = 1;




		}

	} else {

		int previousKey					= lexer_getKey( currentLexer, i - 1 );

		propertySource 					= text_slice( source, previousKey + 1, key - 1 );


		shift	= 0;



	}


	char * beforeShiftToken				= lexer_getToken( currentLexer, i - 1 - shift );


	if( strcmp( beforeShiftToken, ">" ) == 0 ) {

		int templateOpenKey						= lexer_getKey( currentLexer, i - 2 - shift );

		int templateCloseKey					= lexer_getKey( currentLexer, i - 1 - shift  );

		int afterCloseKey						= lexer_getKey( currentLexer, i  - shift  );

		char * templateArgument					= text_slice( source, templateOpenKey + 1, templateCloseKey - 1 );

		int beforeClassName						= lexer_getKey( currentLexer, i - 3 - shift  );

		char * className						= text_removeWhiteSpaces( text_slice( source, beforeClassName + 1 , templateOpenKey - 1 ) ) ;

		char * afterTemplate 					= text_slice( source, templateCloseKey + 1, afterCloseKey - 1 );


		char * classNameWithoutStruct			= className;

		struct array * nameParts				= text_split( className, " \t" );

		if( array_length( nameParts ) > 1 ) {

			char * firstPart = array_get( nameParts, 0 );

			if( strcmp( firstPart, "struct" ) == NULL ) {


				classNameWithoutStruct  = array_get( nameParts, 1 );

			}

		}



	struct class * classInstance 	= application_getClassByClassName( allClasses, classNameWithoutStruct );

	if( classInstance == NULL ) {

		printf("Error:	class with template not found:		%s \n\n\n\n\n", classNameWithoutStruct );

		

	} 

	if( !classInstance->usesTemplate ) {

		printf("Error:	This class does not have an template:	%s \n\n", classInstance->className );

	

	}

 	template_addArgumentValue( classInstance, templateArgument );







			struct text * templateClassName			= text_new( "" );

			templateArgument						= text_replaceAll( templateArgument, "*",  "_pointer" );

			templateArgument						= text_removeAllWhiteSpaces( templateArgument );


			text_append( templateClassName, className );

			text_append( templateClassName, "_" );

			text_append( templateClassName, templateArgument );





			struct text * newPropertySource			= text_new( "" );


			text_append( newPropertySource, templateClassName->value );

			text_append( newPropertySource, afterTemplate );


			propertySource	= newPropertySource->value;


			if( hasValue == 1 ) {




				char * needle		= "\\b"; // "\\bTT\\b"

				needle = text_concatenate( needle, className );

				needle = text_concatenate( needle, "\\b" );

				propertyValue = text_regexReplaceAll( propertyValue, needle, className, templateClassName->value );

			}


			//printf("	templateArgument:	%s	%s\n\n", className, templateArgument);


		}
	

	char * trimmedPropertySource			= text_removeWhiteSpaces( propertySource );

	//printf("		trimmedPropertySource:	%s\n", propertySource);

	struct array * propertyParts			= text_split( trimmedPropertySource, " \t" );

	int propertyPartsLength 				= array_length( propertyParts );



	propertyInstance->isPointer			= variable_isPointer( propertyParts );

	propertyInstance->hasValue 			= hasValue;

	propertyInstance->decleration 		= property_extractPropertyDeclaration( propertyParts, propertyInstance );

	if( hasValue == 1 ) {

		//printf("	propertyValue:	%s\n", propertyValue);

		propertyInstance->value  		= property_composePropertyValue( propertyValue, propertyInstance );

	}

	//printf("propertyName: %s\n\n\n", propertyInstance->propertyName);

	if( propertyInstance->propertyName == NULL ) {

		propertyInstance->propertyName 		= property_extractPropertyName( propertyParts );

	}
	

	propertyInstance->declerationParts 	= propertyParts;


	array_add( classInstance->properties, (void*) propertyInstance );

	

	//printf("decleration: %s\n\n\n", propertyInstance->decleration);

}

int class_getPropertyIndexByName( struct class * classInstance, char * propertyName ) {

	struct array * properties		= classInstance->properties;

	int propertyCount				= array_length( properties );

	for (int i = 0; i < propertyCount; ++i)
	{

		struct property * propertyInstance = array_get( properties, i );

		if( strcmp( propertyInstance->propertyName, propertyName ) == 0 ) {

			return i;

		}

	}

	return -1;

}



struct property * class_getPropertyByName( struct class * classInstance, char * propertyName ) {

	struct array * properties		= classInstance->properties;

	int propertyCount				= array_length( properties );

	for (int i = 0; i < propertyCount; ++i)
	{

		struct property * propertyInstance = array_get( properties, i );

		if( strcmp( propertyInstance->propertyName, propertyName ) == 0 ) {

			return propertyInstance;

		}

	}

	return NULL;

}

void class_composeHeader( struct class * classInstance,  struct text * classHeader, char * filename ) {

	char * className	=  classInstance->className;

	if( strcmp( className, "char" ) != 0 && strcmp( className, "int" ) != 0 && strcmp( className, "void" ) != 0 ) {

		class_composeStruct( classInstance, classHeader );

	}

}

void class_composeTypedefStruct( struct class * classInstance, struct text * classHeader ) {

	char * className			= classInstance->className;

	struct array * properties 	= classInstance->properties;

	int propertyCount			= array_length( properties );

	text_append( classHeader, "\n\n" );

	text_append( classHeader, "typedef struct " );

	text_append( classHeader, className );

	text_append( classHeader, " " );

	text_append( classHeader, className );

	text_append( classHeader, ";\n\n" );

	text_append( classHeader, "\n\n" );

}

void class_composeStruct( struct class * classInstance, struct text * classHeader ) {

	char * className			= classInstance->className;

	struct array * properties 	= classInstance->properties;

	int propertyCount			= array_length( properties );


	text_append( classHeader, "typedef struct " );

	text_append( classHeader, className );

	text_append( classHeader, "{\n\n" );

	if( classInstance->hasReflection ) {

		if( strcmp( className, "window" ) != 0 && strcmp( className, "Hints" ) != 0 ) {

			text_append( classHeader, "	unsigned short __classIndex;\n\n" );

		}

	}

	for ( int i = 0; i < propertyCount; ++i )
	{

		struct property * currentProperty = array_get( properties, i );

		char * decleration = currentProperty->decleration;

		text_append( classHeader, "	" );

		text_append( classHeader, decleration );

		text_append( classHeader, ";\n\n" );

	}



	text_append( classHeader, "\n} " );

	text_append( classHeader,  className );

	text_append( classHeader, ";\n\n" );

}

void class_composeMethods( struct class * classInstance,  struct text * classHeader, struct array * sourceArray ) {

	char * className	= classInstance->className;

	struct array * methods = classInstance->methods;

	int length	= array_length( methods );

	for (int i = 0; i < length; ++i)
	{

		struct method * currentMethod = array_get( classInstance->methods, i );

		method_compose( currentMethod, className, sourceArray, classHeader );

	}

}

int class_classNameIsPrimitive( char * baseClassName ) {

	// dont overload numbers because these are used for pointers
	char * primitiveTypes[3]			= { "int",  "long", "size_t" };

	int primitiveTypesCount				= 3;

	for (int j = 0; j < primitiveTypesCount; ++j)
	{

		char * primitiveType = primitiveTypes[j];

		if( strcmp( baseClassName, primitiveType ) == 0 ) {

			return 1;

		}

	}

	return -1;
}


struct property * class_getClassPropertyByPropertyName( struct class * instance, char * propertyName ) {

	struct array * properties = instance->properties;

	int propertyLength	= array_length( properties );

	for (int i = 0; i < propertyLength; ++i)
	{

		struct property * currentProperty = array_get( properties, i );

		char * currentPropertyName = currentProperty->propertyName;

		if( strcmp( currentPropertyName, propertyName ) == 0 ) {

			return currentProperty;

		}

	}
	
	return NULL;

}


char * class_getDatatypeFromClassProperty( char * declaration, struct array * variables ) {

	array * parts								= text_split( declaration, "->" );

	char * propertyName						= array_get( parts, 1 );

	char * variableName						= array_get( parts, 0 );


	struct variable * variableInstance 	= variable_getByName( variables,  variableName );

	if( variableInstance == NULL ) {

		return "";

	}

	struct class * currentClass	= application_getClassByClassName( allClasses, variableInstance->datatype );

	if( currentClass == NULL ) {

		return "";

	}

	struct property *  propertyInstance 				= class_getClassPropertyByPropertyName( currentClass, propertyName );

	if( propertyInstance == NULL ) {

		return "";

	}

	return propertyInstance->datatype;


}



char * class_openingDeclarationFromBaseClassName( char * baseClassName, char * currentOperatorSymbol, int leftSideIsPointer ) {

	struct class * baseClass 	= application_getClassByClassName( allClasses, baseClassName );

	if( baseClass != NULL ) {

		char * openingDeclaration;


		char * operatorName 				= method_getOperatorNameByOperatorSymbol( currentOperatorSymbol );

		struct text * fullOperatorName					= text_new( "" );

		text_append( fullOperatorName, "operator_" );

		text_append( fullOperatorName, operatorName );

		//printf("operator name:	%s 	%s   %s\n\n", baseClassName, fullOperatorName->value, operatorName);

		struct method * operatorMethod 		= method_getMethodByMethodName( baseClass, fullOperatorName->value );

		if( operatorMethod	!= NULL ) {

			return method_composeOperatorOverloadOpeningDeclaration( baseClassName, operatorName, leftSideIsPointer );

		} else {

		//	printf("		method not found!\n\n");

		}

	} else {



	}

	return NULL;

}

char * class_getBaseClassNameBySymbol( char * leftSideSymbol, struct array * variables, int * leftSideIsPointer ) {

	if( strcmp( leftSideSymbol, "" ) == 0 ) {

		return NULL;

	}

	if( strcmp( leftSideSymbol, "char") == 0 ) {

		return "char";

	}

	if( strstr( leftSideSymbol, "->" ) != 0 ) {

		return class_getDatatypeFromClassProperty( leftSideSymbol, variables );
	
	}
	

	struct variable * variableInstance 		= variable_getByName( variables, leftSideSymbol );

	if( strcmp( variableInstance->datatype, "this_variable_is_missing" ) != 0 ) {

		*leftSideIsPointer	= variableInstance->isPointer;

		return variableInstance->datatype;

	} 


	struct method * methodInstance			= method_extractMethodFromSymbol( leftSideSymbol );

	if( methodInstance != NULL ) {

		return tools_removePointerSymbolFromDatatype( methodInstance->returnType, leftSideIsPointer );

	}


	char * returntype 				= function_getReturnTypeBySymbol( leftSideSymbol, leftSideIsPointer );

	if( returntype != NULL ) {

		return returntype;

	}


	return NULL;

}
