/*****************************************************************************
 *                                                                           *
 *   PLAST : Parallel Local Alignment Search Tool                            *
 *   Version 2.3, released November 2015                                     *
 *   Copyright (c) 2009-2015 Inria-Cnrs-Ens                                  *
 *                                                                           *
 *   PLAST is free software; you can redistribute it and/or modify it under  *
 *   the Affero GPL ver 3 License, that is compatible with the GNU General   *
 *   Public License                                                          *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 *   Affero GPL ver 3 License for more details.                              *
 *****************************************************************************/

#include <algo/hits/hsp/HspExtensionCmd.hpp>

#include <alignment/core/impl/HspContainer.hpp>

#include <alignment/tools/impl/AlignmentSplitter.hpp>

#include <designpattern/api/Iterator.hpp>

#include <misc/api/PlastStrings.hpp>

#include <string.h>
#include <math.h>

using namespace std;

using namespace dp;

using namespace os;
using namespace os::impl;

using namespace misc;

using namespace database;

using namespace statistics;

using namespace alignment::core;

using namespace alignment::tools;
using namespace alignment::tools::impl;

#include <stdio.h>
#define DEBUG(a)    //printf a
#define VERBOSE(a)  //printf a

#define LOG2 0.69314718055994530941

/********************************************************************************/
namespace algo {
namespace hits {
namespace hsp  {
/********************************************************************************/

/*********************************************************************
** METHOD  :
** PURPOSE :
** INPUT   :
** OUTPUT  :
** RETURN  :
** REMARKS :
*********************************************************************/
HspExtensionCmd::HspExtensionCmd (
    database::ISequenceDatabase*            db1,
    database::ISequenceDatabase*            db2,
    statistics::IQueryInformation*          queryInfo,
    IHspContainer*                          hspContainer,
    IHspContainer*                          hspContainer2,
    alignment::tools::ISemiGapAlign*        dynapro,
    algo::core::IParameters*                params,
    dp::IObserver* 							observer
)
    : _db1(0), _db2(0), _queryInfo(0), _hspContainer (0), _hspContainer2 (0),
      _dynapro(0), _parameters(0)
{
    setDb1                (db1);
    setDb2                (db2);
    setQueryInfo          (queryInfo);
    setHspContainer       (hspContainer);
    setHspContainer2      (hspContainer2);
    setDynapro            (dynapro);
    setParameters         (params);

    /** We register the provided observer. */
    this->addObserver (observer);
}

/*********************************************************************
** METHOD  :
** PURPOSE :
** INPUT   :
** OUTPUT  :
** RETURN  :
** REMARKS :
*********************************************************************/
HspExtensionCmd::~HspExtensionCmd ()
{
    setDb1                (0);
    setDb2                (0);
    setQueryInfo          (0);
    setHspContainer       (0);
    setHspContainer2      (0);
    setDynapro            (0);
    setParameters         (0);
}

/*********************************************************************
** METHOD  :
** PURPOSE :
** INPUT   :
** OUTPUT  :
** RETURN  :
** REMARKS :
*********************************************************************/
void HspExtensionCmd::execute ()
{
    size_t nbHsp=0;

    IHspContainer::HSP* hsp = 0;

    size_t nbTotal       = _hspContainer->getItemsNumber();
    size_t notifyModulus = MAX (1, nbTotal / 100);  // notify each percentage.

    /** We loop over all HSP. */
     while ( (hsp = _hspContainer->retrieve (nbHsp)) != 0)
     {
    	 /** We notify about the execution % of the loop. */
    	 if (nbHsp % notifyModulus == 0)
    	 {
    	     this->notify (new IterationStatusEvent (ITER_ON_GOING, nbHsp,  nbTotal, MSG_HITS_MSG2, nbHsp,  nbTotal));
    	 }

         u_int64_t qryStart = hsp->q_start;
         u_int64_t sbjStart = hsp->s_start;

         /** We look for the query sequence. */
         ISequence* seqQry = _db2->getSequenceRefByIndex (hsp->q_idx);
         if (!seqQry)  { throw MSG_HITS_MSG1; }

         ISequence* seqSbj = _db1->getSequenceRefByIndex (hsp->s_idx);
         if (!seqSbj)  { throw MSG_HITS_MSG1; }

         /** We shift the two HSP ranges into sequence referential (instead of database). */
         qryStart -= seqQry->offsetInDb;
         sbjStart -= seqSbj->offsetInDb;

        u_int32_t leftOffsetInQuery=0, leftOffsetInSubject=0, rightOffsetInQuery=0, rightOffsetInSubject=0;

        int scoreRight=0, scoreLeft=0;

         const char* qryData = seqQry->getData();
         const char* sbjData = seqSbj->getData();

         /** LEFT EXTENSION */
         scoreLeft = _dynapro->compute (
             qryData,
             sbjData,
             qryStart + 1,
             sbjStart + 1,
             & leftOffsetInQuery,
             & leftOffsetInSubject,
             1
         );

         /** RIGHT EXTENSION */
         scoreRight = _dynapro->compute (
             qryData + qryStart,
             sbjData + sbjStart,
             seqQry->getLength() - qryStart - 1,
             seqSbj->getLength() - sbjStart - 1,
             & rightOffsetInQuery,
             & rightOffsetInSubject,
             0
         );

         /** We compute the total score. */
         int score = scoreLeft + scoreRight;

         /** We retrieve statistical information for the current query sequence. */
         IQueryInformation::SequenceInfo& info = _queryInfo->getSeqInfo (*seqQry);

         if (score >= info.cut_offs)
         {
             _hspContainer2->insert (
                 hsp->q_start - leftOffsetInQuery   + 1,
                 hsp->q_start + rightOffsetInQuery,
                 hsp->s_start - leftOffsetInSubject + 1,
                 hsp->s_start + rightOffsetInSubject,
                 hsp->q_idx,
                 hsp->s_idx,
                 score
             );
         }

     } /* end of while ( (hsp = _hspContainer->retrieve... */

     /** We send a notification about the % of execution. Here, we force to be at 100%. */
     this->notify (new IterationStatusEvent (ITER_ON_GOING, nbTotal, nbTotal, MSG_HITS_MSG2, nbTotal, nbTotal));

     DEBUG (("HspExtensionCmd::execute  nbExists=%d\n", nbExists));
}

/********************************************************************************/
}}}; /* end of namespaces. */
/********************************************************************************/
