#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <math.h>
#include <float.h>

#include "method.h"
#include "utilities.h"

#include "logger.h"

#define GRADIENT_PRECISION 0.001

#define TRUE 1

#define LOG_LEVEL INFO

void resolveByGradientFixedStep(
    Problema*        pb,
    Method*          m,
    double           init_vector[],
    MethodParameters args)
{
    // iteration
    double current_iteration[pb->_dimension];
    double old_iteration    [pb->_dimension];

    //
    double  norme;
    double  diff[pb->_dimension];
    double *derivee_buffer = NULL;

    activateLogger();
    setLoggerLevel(LOG_LEVEL);

    double  step           = *((double*)getParameterProperty(args,STEP_LOADED));
    double  epsilon        = *((double *)getParameterProperty(args,EPSILON_LOADED));
    int     patience       = *((int *)getParameterProperty(args,PATIENCE_LOADED));
    double  old_error = -1, current_error;
    int counter = 0,i;
    boolean toContinue = 1;

    derivee_buffer = NULL;

    logMessage(DEBUG2,"function name","resolveByGradientFixedStepTested");

    logMessage(DEBUG2,"parameters : problem name",pb->name);
    logMessage(DEBUG2,"parameters : method name",m->_name);
    logMessage(DEBUG2,"parameters : vector",printVector(init_vector,pb->_dimension));
    logMessage(DEBUG2,"parameters : precision",doubleToString(epsilon));
    logMessage(DEBUG2,"parameters : patience",integerToString(patience));
    logMessage(DEBUG3,"parameters : step",doubleToString(step));

    // Initialize old_iteration <- init_vector
    memcpy(
            old_iteration,
            init_vector,
            sizeof(double) * pb->_dimension);

    logMessage(DEBUG2,"extract value : step",doubleToString(step));
    if(pb->_solution)
    {
        logMessage(DEBUG2,"solution","bundled with the problem");
        logMessage(DEBUG2,"solution value",printVector(pb->_solution,pb->_dimension));
    }
    if( pb->_derivees[0] )
        logMessage(DEBUG2,"derivee acquisition","we got the first derivee");
    else
        logMessage(DEBUG2,"derivee acquisition","dynamically calculated");

    do
    {

    	if( ! ( pb->_derivees[0] ) )
    		free(derivee_buffer);

        // Si on a la dérivée première autant l'utiliser...
        if( pb->_derivees[0] )
        {
            derivee_buffer = pb->_derivees[0](old_iteration);
        }
        // sinon
        else
        {
            derivee_buffer = approximateGradient(
                                pb->_function,
                                old_iteration,
                                pb->_dimension,
                                0.001
                            );
        }

        checkDecimalLimit(*derivee_buffer,m);

        for(i=0; i<pb->_dimension; i++)
        {
            current_iteration[i] =
                    old_iteration[i]
                    - (
                        (step)
                        * derivee_buffer[i]
                    );

            checkDecimalLimit(current_iteration[i],m);
        }

        logMessage(DEBUG2,"derivee value",printVector(derivee_buffer,pb->_dimension));
        logMessage(DEBUG2,"current iteration",printVector(current_iteration,pb->_dimension));
        logMessage(DEBUG2,"old iteration",printVector(old_iteration,pb->_dimension));
        logMessage(DEBUG1,"iteration",printVector(current_iteration,pb->_dimension));

        for(i=0; i<pb->_dimension; i++)
            diff[i] = current_iteration[i] - old_iteration[i];

        norme = normeEuclidienne(diff,pb->_dimension);

        logMessage(DEBUG2,"difference's norme value",doubleToString(norme));

        //Calcul de l'erreur, si on la possède
        if(pb->_solution){
            current_error = error(pb->_dimension,current_iteration,pb->_solution);

            logMessage(DEBUG2,"error",doubleToString(current_error));
            if(old_error != -1)
                logMessage(DEBUG2,"error difference between iteration",doubleToString(fabs(current_error - old_error)));

            // Actualisation statistique sur l'erreur
            old_error = current_error;
        }

        // Pr�paration prochaine it�ration
        memcpy(old_iteration,current_iteration,pb->_dimension * sizeof(double));


        toContinue = toContinue && ( norme > epsilon );
        toContinue = toContinue && ( ++counter < patience );

        logMessage(DEBUG2,"counter",integerToString(counter));

        logMessage(DEBUG2,"stop condition 1 : norme > m->_epsilon_in",printBoolean(norme > epsilon));
        logMessage(DEBUG2,"stop condition 2 : counter < m->_max_iteration",printBoolean((counter+1) < patience));
        logMessage(DEBUG2,"should we continue",printBoolean(toContinue));
    }
    while( toContinue );

    saveAlgorithmData(
            m,
            pb,
            args,
            counter,
            current_iteration,
            pb->compute(pb,current_iteration,pb->_dimension)[0],
            current_error);
}
