#ifndef _MAIN_CU_H_
#define _MAIN_CU_H_

#include "sequences.h"
#include "thread_manager.h"
#include "matches_manager.h"
#include "arguments_manager.h"
#include "partial_primary_library.h"
#include "primary_library.h"
#include "distance_matrix.h"
#include "extended_library.h"
#include "neighbour_joining.h"
#include "nj_internal_node.h"

struct TexVariablesAddresses; //from "data_management.cuh" file

using Data::Sequences;
using Data::SubstitutionMatrix;
using Data::MatchesManager;


class ThreadManagerRunnable;

/*******************************************************************************
 * RunnableWithParams struct is needed by Thread Manager.                      *
 * Thread Manager uses it to bind parameters ("params", which are              *
 * characteristic to a selected window -task), to the algorithm.               *
 * Thread Manager call the "run" method of the algorithm with                  *
 * given parameters.                                                           *
 *******************************************************************************/
struct RunnableWithParams
{
public:
    ThreadManagerRunnable* algorithm;
    void* params;
};

/*******************************************************************************
 * ThreadManagerRunnable
 *******************************************************************************/
        
class ThreadManagerRunnable
{
private:
    static ThreadManagerRunnable** runnables;
public:

/*******************************************************************************
 * This method should return the relative path to the file with settings       *
 * that should be load if user choose this algorithm and won't pass (one way   *
 * or another) satisfying number of arguments                                  *
 *******************************************************************************/
    virtual char* defaultSettingsFile() = 0;

/*******************************************************************************
 * This is the method that should be invoked when we want to run an algorithm  *
 *******************************************************************************/
    virtual void run(ThreadManager* tm) = 0;

/*******************************************************************************
 * This is the actual single method and is invoked by thread manager in given  *
 * thread                                                                      *
 *******************************************************************************/
    virtual void actualInvokedMethod(void* params) = 0;

/*******************************************************************************
 * Should return an short name of the algorithm (that user can pass as an      *
 * argument)                                                                   *
 *******************************************************************************/
    virtual const char* getAlgorithmName() = 0;

/*******************************************************************************
 * This method tries to load arguments specific to the given algorithm if it   *
 * fail then it will try to load default for the algorithm settings file and   *
 * try load them again.                                                        *
 *******************************************************************************/
    void loadArguments(ArgumentsManager* am);

/*******************************************************************************
 * Responsibility of loading arguments should be shift on the algorithm class  *
 *******************************************************************************/
    virtual void actuallyArgumentsLoading(ArgumentsManager* am) = 0;

/*******************************************************************************
 * Method invoked by the ThreadManager must be a static two-parameter:         *
 *  @ pointer to a ThreadManager                                               *
 *  @ pointer to some given data                                               *
 * metod. This method satisfies those requirements and also allow to           *
 * structurise the algorithm and let just implement a few methods to optain    *
 * and forget about the whole ThreadManager structure.                         *
 *******************************************************************************/
    static void invoker(ThreadManager* tm, void* data);

/*******************************************************************************
 * Searches for the algorithm by its name given in argument's "--alg", "-a"    *
 * or in configuration file "alg = ..."                                        *
 *******************************************************************************/
    static ThreadManagerRunnable* getAlgorithm(ArgumentsManager* am);

/*******************************************************************************
 * Initialize all the available runnables for user friendly interface.         *
 * CAUTIONS!!                                                                  *
 * IF ANY NEW CLASS WILL IMPLEMENT THE ALGORITHM INTERFACE IT SHOULD BE ADD    *
 * HERE                                                                        *
 *******************************************************************************/
    static void init();
};


/*******************************************************************************
 * PLWindowParams is a set of parameters that are specific to a given          *
 * window. Used by Thread Manager                                              *
 *******************************************************************************/
        
class PLWindowParams
{
public:
    Sequences* seqs;
    int startSeqs1No, startSeqs2No, windowSize;
    int blockShape;
    int windowX, windowY, partId, parts;
    int** scores;
    int maxMultiprocessorCount;
    PartialPrimaryLibrary* pplX;
    PartialPrimaryLibrary* pplY;

    virtual long long getEstimatedComplexity();
    static int compareAlignmentScoreKernelParams(const void* first, const void* second);
};

/*******************************************************************************
 * ELWindowParams is a set of parameters that are specific to a given          *
 * window. Used by Thread Manager                                              *
 *******************************************************************************/

class ELWindowParams
{
public:
    int windowY, partId;
};

/*******************************************************************************
 * MSAThreadParams is a set of parameters that are specific to a given         *
 * task. Used by Thread Manager                                                *
 *******************************************************************************/

class MSAThreadParams
{
public:
    int partId;
    NJInternalNode* node;
};


/*******************************************************************************
 * 1) ALIGNMENT WITH BACKTRACKING                                              *
 * 2) ALIGNMENT K BESTS (WATERMAN-EGGERT ONLY)                                 *
 * 3) MERGING & SORTING RESULTS                                                *
 *******************************************************************************/

class PrimaryLibraryBuilder : public ThreadManagerRunnable
{
protected:
    SubstitutionMatrix* sm;
    
public:
    PrimaryLibraryBuilder();
    ~PrimaryLibraryBuilder();

    virtual char* defaultSettingsFile();
    virtual void run(ThreadManager* tm);
    virtual const char* getAlgorithmName();
    virtual void actualInvokedMethod(void* params);
    virtual void actuallyArgumentsLoading(ArgumentsManager* am);

    //obligatory parameters:
    int gapOpen;
    int gapExt;
    int maxMultiprocessorCount;
    unsigned int windowSize;
    const char* ploutfile;
    unsigned int K;
    bool entireMatrix;
    const char* njoutfile;
    bool computeNJ;
    int wecutoff; //a score value above which alignments are added into PL
    int nwcutoff; //a score value above which alignments are added into PL

    //only for reading:
    Sequences* seqs;
    PrimaryLibrary* library; // either PL or EL
    DistanceMatrix* dm;
    NeighbourJoining* nj;
    
};


class ExtendedLibraryBuilder : public ThreadManagerRunnable
{
public:
    virtual char* defaultSettingsFile();
    virtual void run(ThreadManager* tm);
    virtual const char* getAlgorithmName();
    virtual void actualInvokedMethod(void* params);
    virtual void actuallyArgumentsLoading(ArgumentsManager* am);

    ExtendedLibraryBuilder();

    //only for reading:
    int winHeight; // window size: numOfSequences x winHeight
    int windowsNumber;
    int sequenceNumber;
    const char* eloutfile;
    
    PrimaryLibraryBuilder* plb;
};

class MultipleSequenceAlignment : public ThreadManagerRunnable
{
private:
    void saveToFile();
public:
    virtual void run(ThreadManager* tm);
    virtual const char* getAlgorithmName();
    virtual void actuallyArgumentsLoading(ArgumentsManager* am);
    virtual char* defaultSettingsFile();
    virtual void actualInvokedMethod(void* params);

    MultipleSequenceAlignment();
    ~MultipleSequenceAlignment();

    ExtendedLibraryBuilder* elb;
    PrimaryLibraryBuilder* plb;
    PrimaryLibrary* library;
    DistanceMatrix* dm;
    int sequenceNumber;
    Sequences* seqs;
    NeighbourJoining* nj;
    short* MSA;
    double* resPen; // residue specific penalties
    float scoreMSA;
    int totalLengthMSA;

    // parameters:
    int nCPUs;
    int libType; //0 - primary, 1 - extended
    int msaGapOp;
    int msaGapEx;
    const char*  msaoutclustal;
    const char*  msaoutfasta;
};
        
extern "C" TexVariablesAddresses copySeqsToTex(Sequences* s, int startSeqs1No, int startSeqs2No, int count);

#endif
