#ifndef _ALIGNER_H_
#define _ALIGNER_H_

/**
 *    WHAM - high-throughput sequence aligner
 *    Copyright (C) 2011  WHAM Group, University of Wisconsin
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    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
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*	$Id: aligner.h 164 2012-11-26 08:53:32Z yinan $ */

#include <stdio.h>
#include "hash.h"
#include "sequence.h"
#include "short.h"

#define ALIGNER_ALL_INDEX -1

typedef struct AlignInfo {
  ShortRead * reader1;
  ShortRead * reader2;
  int maxHit;
  int maxMatch;
  int maxQual;
  int maxGap;
  double scanThreshold;
  bool pairStrand[2][2];
  bool sorted;
  bool strata;
  unsigned int minins;
  unsigned int maxins;
  bool mateMatch;
  int outputFormat;
  bool concatenate;
  int maxMate;
  bool showBar;
} AlignInfo;

typedef struct AlignRes {
  int nRead;
  int nValidRead;
  int nValidAlignment;
} AlignRes;

class Aligner {
private:
  int length; /* the length of query sequence */
  int words; /* the number of 64-bit word to store the query sequence */
  int nPartition; /* the number of fragments */
  int nError; /* the number of allowed errors */
  int nSubstitute;/* the number of allowed substitutions */
  int nInsert; /* the number of allowed insertions */
  int nDelete; /* the number of allowed deletions */
  int maxRepeat; /* the maximum number of repeats */
  bool embedHashTable; /* whether use the embed hash tables */
  /*
   *	This figure is used to demonstrate how the query sequence
   *	is split into uniform-sized fragments.
   *	|<-------------------  query sequence  -------------------->|
   *	| head  | fragment f-1 |......|  fragment 1  |  fragment 0  |
   *	|lenRest|                lenPartitions                      |
   *	|       | lenPartition |......| lenPartition | lenPartition |
   *	|                         lenKey                            |
   */
  int lenKey; /*	the length of the query sequence (bits) */
  int lenPartition; /*	the length of each fragment (bits) */
  int lenPartitions; /*	the length of all fragments (bits) */
  int lenRest; /*	the length of the head (bits) */

  uint32 numEntry;

  int nHashTable; /*	the number of hash indexes */
  int nLookup; /*	the number of lookups for each alignment */

  HashTable * hashTables; /*	hash indexes */
  int * lookupIndex; /*	lookup Index array. store the group id of all concatenations */
  int * lookupOffset; /*	lookup Offset array. store the distance before the leftmost indexed fragment */
//	HitSet hits;	/*	the result pool. store all matched results of an alignment without duplications */

  CompactSequence * sequence; /* the reference sequence */

  /*
   *	these variables are only used in alignment phase
   */
  char * indexpath;
  int outputMode; /* 	normal or SAM */
  int nMaxError; /*	the maximum number of errors in alignments that wham reprots */
  int maxQual;

  int64 headMask[WORDS_PER_READ];

public:
  Aligner();
  Aligner(char * path);

  int init(CompactSequence * sequence, int length, int numPartition,
      unsigned int numBucket, int numMismatch, int numInsert, int numDelete,
      int repeat, bool embedHashTable);
  int build(char * path);
  uint32 align(int64 * key, char * quals, strand s, int rid, HitSet * hits,
      bool skipFirst = false, bool noGap = false);
  uint32 alignFirst(int64 * key, char * quals, strand s, int rid, HitSet * hits,
      bool noGap = false);
  int check(int num);
  AlignRes align(AlignInfo * info, char * path);

  void sortList();

  int save(char * path, int indexID = ALIGNER_ALL_INDEX);
  int saveHead(char * path);
  int saveIndex(char * path, int indexID);
  int load(char * path, int indexID = ALIGNER_ALL_INDEX);
  int loadHead(char * path);
  int loadHashtables(char * path);
  int removeHashTables();

  void printInfo();
  int valid(int length, int numPartition, int numMismatch, int numInsert,
      int numDelete);

  int getReadLength() {
    return length;
  }
  int getNumIndex() {
    return nHashTable;
  }
  int getNumError() {
    return nError;
  }
  bool allowGap() {
    return nPartition == nError + 1;
  }

  void setErrorModel(int maxerr, int maxgap, int maxqual) {
    if (maxqual == 0)
      maxqual = 0;

    if (maxerr == 0)
      maxerr = nError;

    nMaxError = maxerr;
    maxQual = maxqual;
    nInsert = nDelete = maxgap;
//		for (int i = 0; i < nHashTable; i++)
//			hashTables[i].setErrorModel(maxerr, maxqual);
  }

  void setScanThreshold(double r) {
    for (int i = 0; i < nHashTable; i++)
      hashTables[i].setScanThreshold(r);
  }

  void printStat();

private:
  int preProcessHashTables();
  int buildHashTables(char * path);

  AlignRes alignSingleEnd(AlignInfo * info, char * path);
  AlignRes alignPairEnd(AlignInfo * info, char * path);

  AlignRes merge(AlignInfo * info, int num, char * path);
  AlignRes mergeSingleEnd(AlignInfo * info, int num, char * path);
  AlignRes mergePairEnd(AlignInfo * info, int num, char * path);

  int computeNumIndex();
  int computeNumLookup();
  int initLookupArray();

  friend class CompactSequence;
};

#endif

