/**
 * This source is meant to help to add method to the function library
 * FEATURE A library per group
 * TODO Enhance the detection of an link file
 * TODO Test the load method
 *
     * --system()--
     * La valeur renvoy�e est
     *    - 127 si l'appel syst�me execve() pour /bin/sh �choue,
     *    - -1 si une autre erreur se produit,
     *    - ou le code de retour de la commande sinon.
 *
 * TODO Invent a session agent that maintain currently used variable (groupId request, userId request, etc)
 * @param filepath
 * @param output
 * @return
 */

#include <stdio.h>
#include <stdlib.h>

#include <dlfcn.h>
#include <dirent.h>

#include <string.h>

#include "MathsLibraryManager.h"

#include "method.h"

#include "configuration.h"

#include "utilities.h"
#include "logger.h"

#include "DatabaseDAO.h"

#define COMPILER_BASE         "gcc -fPIC "
#define INCLUDE_HEADERS       "-Isrc/main -Isrc/main/model -Isrc/main/miscellaneous -Isrc/main/control"

#define PROBLEMS_TEMPLATE_PATH        "build/main/problem-functions/%ld/"

#define PROBLEMS_FUNCTION_LIBRARY     "problem-functions.so"

#define PROBLEMS_CONSTRAINTS_LIBRARY  "problem-constraints.so"



#define LIBRARY_TEMPLATE_PATH         "build/main/library/%ld/"

#define MATH_LIB                      "build/main/library/mathematics-resolution.so"

static
char* transformToObjectFilepath(char *filepath,char *output)
{
	char *search                       = NULL;

	logMessage(DEBUG3,"enter method","transformToObjectFilepath");
	logMessage(DEBUG3,"parameters : filepath",filepath);

	search = strrchr(filepath,'.');
            handleError(
                    search == (char *)NULL,
                    NULL,
                    "transform to object filepath",
                    "could not find '.' character into given filepath");

        strcpy(output,filepath);
        output[strlen(output)-1] = 'o';

	logMessage(DEBUG3,"exiting method",output);
        return output;
}

static
char*  resolveGroupMethods(char *output)
{
    char	   groupFolderName[MAX_INPUT] = {0};
    char           filenameBuffer[MAX_INPUT]  = {0};
    char           buffer[MAX_INPUT]          = {0};
    struct dirent *directory_data             = NULL;
    DIR *	   openedFolder               = NULL;

    sprintf(groupFolderName,PROBLEMS_TEMPLATE_PATH,getSessionId());

    logMessage(DEBUG3,"opening group methods folder",groupFolderName);

    openedFolder     = opendir(groupFolderName);
        handleError(!openedFolder,NULL,"opening group methods folder","fail at open the folder");
    directory_data = readdir(openedFolder);
    
    while(directory_data)
    {
        //reset
        strcpy(filenameBuffer,"");

        sprintf(filenameBuffer,"%s%s",groupFolderName,directory_data->d_name);
        logMessage(DEBUG3,"read directory file",filenameBuffer);

        // check if it is a object file
        if( strstr(filenameBuffer,".o") )
        {
            logMessage(DEBUG3,"object file detected",filenameBuffer);
            
            //add it to our result output
            sprintf(buffer,"%s ",filenameBuffer);
            strcat(output,buffer);

            logMessage(DEBUG3,"update output",output);
        }

        //continue to read directory
        directory_data = readdir(openedFolder);
    }

    //close the read
    closedir(openedFolder);
    logMessage(DEBUG3,"exiting resolving group methods",output);

    //return data
    return output;
}

Method* loadMethod(
	char *methodname
)
{
	Method           *m = NULL;
	algorithm_method  algorithm = NULL;
	void             *handle = NULL;

	logMessage(DEBUG2,"enter method","loadMethod");

	logMessage(DEBUG2,"step 1 : load the math library",MATH_LIB);
	handle = dlopen(MATH_LIB,RTLD_LAZY);

		handleError(
			!handle,
			NULL,
			"load library",
			"Fail at loading mathematics library");

	logMessage(DEBUG2,"step 2 : load algorithm function",methodname);
	algorithm = (algorithm_method) dlsym(handle, methodname);

		handleError(
			!algorithm,
			NULL,
			"load method",
			"Fail at loading method from mathematics library");

	logMessage(DEBUG2,"step 3","building corresponding method object");
	// TODO Replace asap
//	m = getMethod(methodname);
//	m->_method_function = algorithm;

	m = createMethod(
			0,
			methodname,
			algorithm
		);

	logMessage(DEBUG2,"exiting method","loadMethod");

	return m;
}

/**
 * Save a problem's function into the library appropriate for this
 * 
 */
boolean putFunctionIntoProblemLibrary(
		char *filepath // HELP Filepath to source file
)
{
	char command_buffer[MAX_INPUT] = {0};
	char file_buffer[MAX_INPUT]    = {0};
        char function_lib[MAX_INPUT]   = {0};
        char methods     [3*MAX_INPUT] = {0};
        char methods_folder[MAX_INPUT] = {0};
	int  retour_control;

        logMessage(DEBUG2,"putProblemIntoLibrary : given parameters",filepath);

		handleError(
			!fileExists(filepath),
			FALSE,
			"file not found",
			"source file wasn't found");

        logMessage(DEBUG2,"file existency","It exists");

            handleError(
                   !strstr(filepath,".o")
                    && !strstr(filepath,".c"),
                    FALSE,
                    "wrong parameter",
                    "given file is neither a source file not an object file");

        // check if it is already compile
        if( !strstr(filepath,".o") )
        {
            logMessage(DEBUG2,"file state","given file is not compiled");
            
            // On r�cup�re le nom du fichier
            transformToObjectFilepath(filepath,file_buffer);
            logMessage(DEBUG2,"object file path",file_buffer);

                // prepare the command - compiles files
                sprintf(command_buffer,"%s %s -c %s -o %s",COMPILER_BASE,INCLUDE_HEADERS,filepath,file_buffer);
                logMessage(DEBUG1,"system call : compile source code to link file",command_buffer);

            //compile source file
            retour_control = system(command_buffer);

                    handleError(
                            retour_control == 127,
                            FALSE,
                            "system call",
                            "couldn't add the specified compiled source file to the library");

                    handleError(
                            retour_control == -1,
                            FALSE,
                            "system call",
                            "unexpected error during call to add compiled file to the library");

            logMessage(DEBUG1,"compiler return code",integerToString(retour_control));

            //resolve group methods folder
            sprintf(methods_folder,PROBLEMS_TEMPLATE_PATH,getSessionId());

                //ensure folder exists
                sprintf(command_buffer,"mkdir -p %s",methods_folder);
                logMessage(DEBUG1,"system call : ensure group methods folder exists",command_buffer);

            //ensure folder exists
            retour_control = system(command_buffer);

                // move object file to the correct folder
                sprintf(command_buffer,"mv %s %s",file_buffer,methods_folder);
                logMessage(DEBUG1,"system call : move object file to the library folder",command_buffer);

            //move it to object file folder
            retour_control = system(command_buffer);
        }
        else
        {
            //resolve group methods folder
            sprintf(methods_folder,PROBLEMS_TEMPLATE_PATH,getSessionId());

                //ensure folder exists
                sprintf(command_buffer,"mkdir -p %s",methods_folder);
                logMessage(DEBUG1,"system call : ensure group methods folder exists",command_buffer);

			//ensure folder exists
			retour_control = system(command_buffer);

            // move object file to the correct folder
            sprintf(command_buffer,"cp %s %s",filepath,methods_folder);
            logMessage(DEBUG1,"system call : move object file to the library folder",command_buffer);

			//move it to object file folder
			retour_control = system(command_buffer);
        }

        
            //Fetch group method library
            sprintf(function_lib,LIBRARY_TEMPLATE_PATH,getSessionId());

            //ensure folder exists
            sprintf(command_buffer,"mkdir -p %s",function_lib);
            logMessage(DEBUG1,"system call : ensure group library folder exists",command_buffer);

        retour_control = system(command_buffer);

            // resolve library path
            strcat(function_lib,PROBLEMS_FUNCTION_LIBRARY);

            //fetch all methods object files into a string
            handleError(
                    !resolveGroupMethods(methods),
                    FALSE,
                    "fetch group methods",
                    "fail during the fetch");

            // prepare the command - put into the library
            sprintf(command_buffer,"%s -shared %s -o %s",COMPILER_BASE,methods,function_lib);
            logMessage(DEBUG1,"system call : put function into library",command_buffer);

	//put link file into lib
	retour_control = system(command_buffer);

	return TRUE;
}

Problema* loadProblem(
	long problemId)
{
	char              pathToProblemLib[MAX_STRING_INPUT] = {0};
	Problema         *problem                            = NULL;
	problem_function  function                           = NULL;
	void             *handle                             = NULL;

	//FIXME Test purpose
	char              problemFunctionName[MAX_STRING_INPUT] = "rosenbrock";

	logMessage(DEBUG2,"enter method","loadProblem");

	sprintf(pathToProblemLib,LIBRARY_TEMPLATE_PATH,getSessionId());
	logMessage(DEBUG2,"resolving the function library - 1",pathToProblemLib);
	strcat(pathToProblemLib,PROBLEMS_FUNCTION_LIBRARY);
	logMessage(DEBUG2,"resolving the function library - 2",pathToProblemLib);

		handleError(
			!fileExists(pathToProblemLib),
			FALSE,
			"library not found",
			"library file wasn't found");

	logMessage(DEBUG2,"step 1 : load the function library",pathToProblemLib);
	handle = dlopen(pathToProblemLib,RTLD_LAZY);

		handleError(
			!handle,
			NULL,
			"load library",
			"Fail at loading mathematics library");

	logMessage(DEBUG2,"step 2 : load algorithm function",problemFunctionName);
	function = (problem_function) dlsym(handle, problemFunctionName);

		handleError(
			!function,
			NULL,
			"load method",
			"Fail at loading method from function library");

	logMessage(DEBUG2,"step 3 : build corresponding problem","");
	//TODO Replace asap
//	problem = DAOreadProblema(problemId);
//	problem->_function = function;

	problem = createProblema(
			problemId,
			"dummy problem",
			"function representation",
			2,
			function);

	return problem;
}

problem_function loadProblemFunction(
	char *functionname)
{
	char              pathToProblemLib[MAX_STRING_INPUT] = {0};
	problem_function  function                           = NULL;
	void             *handle                             = NULL;

	sprintf(pathToProblemLib,LIBRARY_TEMPLATE_PATH,getSessionId());
	logMessage(DEBUG2,"resolving the function library - 1",pathToProblemLib);
	strcat(pathToProblemLib,PROBLEMS_FUNCTION_LIBRARY);
	logMessage(DEBUG2,"resolving the function library - 2",pathToProblemLib);

		handleError(
			!fileExists(pathToProblemLib),
			FALSE,
			"library not found",
			"library file wasn't found");

	logMessage(DEBUG2,"step 1 : load the function library",pathToProblemLib);
	handle = dlopen(pathToProblemLib,RTLD_LAZY);

		handleError(
			!handle,
			NULL,
			"load library",
			"Fail at loading mathematics library");

	logMessage(DEBUG2,"step 2 : load algorithm function",functionname);
	function = (problem_function) dlsym(handle, functionname);

		handleError(
			!function,
			NULL,
			"load method",
			"Fail at loading method from function library");

	return function;
}

problem_constraint loadConstraintFunction(
	char *constraintname)
{
	char                pathToProblemLib[MAX_STRING_INPUT] = {0};
	problem_constraint  function                           = NULL;
	void               *handle                             = NULL;

	sprintf(pathToProblemLib,LIBRARY_TEMPLATE_PATH,getSessionId());
	logMessage(DEBUG2,"resolving the function library - 1",pathToProblemLib);
	strcat(pathToProblemLib,PROBLEMS_FUNCTION_LIBRARY);
	logMessage(DEBUG2,"resolving the function library - 2",pathToProblemLib);

		handleError(
			!fileExists(pathToProblemLib),
			FALSE,
			"library not found",
			"library file wasn't found");

	logMessage(DEBUG2,"step 1 : load the function library",pathToProblemLib);
	handle = dlopen(pathToProblemLib,RTLD_LAZY);

		handleError(
			!handle,
			NULL,
			"load library",
			"Fail at loading mathematics library");

	logMessage(DEBUG2,"step 2 : load algorithm function",constraintname);
	function = (problem_constraint) dlsym(handle, constraintname);

		handleError(
			!function,
			NULL,
			"load method",
			"Fail at loading method from function library");

	return function;
}
