
#include "spacedef.h"
#include "debugdef.h"
#include "intbits.h"
#include "errordef.h"
#include "match.h"
#include "absdef.h"
#include "minmax.h"
#include "genfile.h"
#include "matchstate.h"
#include "leafblocktree.h"

#include "mcontain.pr"
#include "multiseq-adv.pr"

#include "fmumself.pr"
#include "fsuper.pr"
#include "xdropext.pr"
#include "initmstate.pr"
#include "extendgen.pr"
#include "vmatbgfind.pr"

#define ACCEPTMATCH\
        if(i < j)\
        {\
          start1 = i;\
          start2 = j;\
        } else\
        {\
          start1 = j;\
          start2 = i;\
        }\
        if(HASINDEXEDQUERIES(&matchstate->virtualtree->multiseq))\
        {\
          if(start1 >= selfmatchstate->querysepposition ||\
             start2 <= selfmatchstate->querysepposition)\
          {\
            return 0;\
          }\
        }

#define MAXOUTCASE(I)\
        if(virtualtree->alpha.mapsize - UintConst(1) <= UintConst(I))\
        {\
          if(vmatmaxout ## I(virtualtree,\
                             numberofprocessors,\
                             searchlength,\
                             NULL,\
                             outinfo,output) != 0)\
          {\
            return (Sint) -1;\
          }\
          return 0;\
        }

#define MAXCOUNTCASE(I)\
        if(virtualtree->alpha.mapsize - UintConst(1) <= UintConst(I))\
        {\
          if(vmatmaxcount ## I(virtualtree,\
                               numberofprocessors,\
                               searchlength,\
                               counttab) != 0)\
          {\
            return (Sint) -1;\
          }\
          return 0;\
        }

/*
  The following prototypes are for functions whose code is 
  generated by the C-preprocessor. Hence they do not appear anywhere in the
  source code, and we add them here.
*/

#define VMATMAXOUTTYPE Virtualtree *,\
                       Uint,Uint,void *,void *,Outputfunction output

#define VMATMAXCOUNTTYPE Virtualtree *,Uint,Uint,ArrayUint *

typedef Sint (*Selfmatchfunction)(Virtualtree *,
                                  Uint,
                                  Uint,
                                  void *,
                                  void *outinfo,
                                  Outputfunction output);

typedef struct
{
  Matchstate matchstate;
  Uint querysepposition;     // only if number of query files > 0
} Selfmatchstate;

#include "vmatgen.c"

static Sint processexactselfmatch(void *info,Uint l,Uint i,Uint j)
{
  Uint start1, start2;
  Match seed;
  Selfmatchstate *selfmatchstate = (Selfmatchstate *) info;
  Matchstate *matchstate;

  matchstate = &selfmatchstate->matchstate;
  DEBUG3(3,"processexactselfmatch(l=%lu,i=%lu,j=%lu)\n",
            (Showuint) l,
            (Showuint) i,
            (Showuint) j);
  ACCEPTMATCH;
  DEBUG3(2,"seed %lu %lu %lu\n",
            (Showuint) l,
            (Showuint) start1,
            (Showuint) start2);
  seed.length1 = seed.length2 = l;
  seed.position1 = start1;
  seed.position2 = start2;
  seed.seqnum2 = UNDEFSEQNUM2(&matchstate->virtualtree->multiseq);
  seed.flag = 0;
  if(matchstate->matchparam.xdropbelowscore == UNDEFXDROPBELOWSCORE)
  {
    if(MPARMEXACTMATCH(&matchstate->matchparam.maxdist))
    {  
      seed.distance = 0;
      if(matchstate->processfinal(matchstate,&seed) != 0)
      {
        return (Sint) -1;
      }
    } else
    {  
      Match amatch;
  
      amatch.flag = 0;
      amatch.seqnum2 = UNDEFSEQNUM2(&matchstate->virtualtree->multiseq);
      if(matchstate->bestflag == Allmaximalmatches)
      {
        matchstate->seedmstore.nextfreeMatch = 0;
        CALLEXTENSIONFUNCTION(True,&matchstate->seedmstore,True);
        if(applymatchcontainer(&matchstate->allmstore,
                               matchcontainer,
                               &matchstate->seedmstore) != 0)
        {
          return (Sint) -2;
        }
      } else
      {
        CALLEXTENSIONFUNCTION(True,info,False);
      }
    }
  } else
  {
    Match amatch;
  
    seed.distance = 0;
    amatch.flag = SETFLAGXDROP(matchstate->matchparam.xdropbelowscore);
    amatch.seqnum2 = UNDEFSEQNUM2(&matchstate->virtualtree->multiseq);

    DEBUG1(3,"seedlength = %lu\n",
            (Showuint) matchstate->matchparam.seedlength);
    if(xdropseedextend(False,
                       False,
                       False,
                       matchstate->matchparam.xdropbelowscore,
                       &seed,
                       (Xdropscore) seed.length1 * MATCHSCORE,
                       &matchstate->virtualtree->multiseq,
                       &matchstate->virtualtree->multiseq,
                       matchstate->matchparam.seedlength,
                       &amatch))
    {
      if(matchstate->processfinal(info,&amatch) != 0)
      {
        return (Sint) -3;
      }
    }
  }
  return 0;
}

static Sint assignquerysepposition(Sint mode,
                                   Uint *querysepposition,
                                   Multiseq *multiseq)
{
  if(mode == (Sint) 1) // maximalunique computation
  {
    if(!HASINDEXEDQUERIES(multiseq))
    {
      ERROR0("search for maximal unique matches "
             "requires at least one query file");
      return (Sint) -1;
    }
    *querysepposition = getqueryseppos(multiseq);
  } else
  {
    if(HASINDEXEDQUERIES(multiseq))
    {
      if(mode == (Sint) 0)
      {
        ERROR0("supermaximal repeat search does not allow query files in index");
        return (Sint) -2;
      }
      *querysepposition = getqueryseppos(multiseq);
    }
  }
  return 0;
}

Sint findselfmatches(Sint mode,
                     Virtualtree *virtualtree,
                     Matchparam *matchparam,
                     Bestflag bestflag,
                     Uint shownoevalue,
                     Uint showselfpalindromic,
                     SelectBundle *selectbundle,
                     void *procmultiseq,
                     Currentdirection currentdirection,
                     Processfinalfunction processfinal,
                     Evalues *evalues,
                     BOOL domatchbuffering)
{
  Selfmatchstate selfmatchstate;

  Selfmatchfunction selfmatchfunctions[] =
  {
    findsupermax,             // at index 0
    findmaximaluniquematches, // at index 1
    vmatmaxoutwithgaps,       // at index 2
    vmatmaxoutgeneric         // at index 3
  };

  if(initMatchstate(&selfmatchstate.matchstate,
                    virtualtree,
                    NULL,
                    matchparam,
                    bestflag,
                    shownoevalue,
                    showselfpalindromic,
                    selectbundle,
                    0, // onlinequerynumoffset
                    procmultiseq,
                    currentdirection,
                    False,
                    processfinal,
                    evalues,
                    domatchbuffering) != 0)
  {
    return (Sint) -1;
  }
  if(virtualtree->multiseq.totallength < UintConst(2))
  {
    ERROR0("repeat search requires a sequence of length >= 2");
    return (Sint) -2;
  }
  if(assignquerysepposition(mode,
                            &selfmatchstate.querysepposition,
                            &selfmatchstate.matchstate.virtualtree->multiseq) 
                            != 0)
  {
    return (Sint) -3;
  }
  if(selfmatchfunctions[mode](virtualtree,
                              matchparam->numberofprocessors,
                              matchparam->seedlength,
                              (void *) &selfmatchstate.matchstate.
                                                       matchparam.repeatgapspec,
                              (void *) &selfmatchstate,
                              processexactselfmatch) != 0)
  {
    return (Sint) -4;
  }
  if(bestflag == Allmaximalmatches && 
     !MPARMEXACTMATCH(&matchparam->maxdist))
  {
    if(applymatchcontainer(&selfmatchstate.matchstate,
                           processfinal,
                           &selfmatchstate.matchstate.allmstore) != 0)
    {
      return (Sint) -5;
    }
    FREEARRAY(&selfmatchstate.matchstate.allmstore,Match);
    FREEARRAY(&selfmatchstate.matchstate.seedmstore,Match);
  }
  return 0;
}
