/*
 * HHblits.h
 *
 *  Created on: Apr 1, 2014
 *      Author: meiermark
 */

#ifndef HHBLITS_H_
#define HHBLITS_H_

#include <fstream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <vector>
#include <cmath>
#include <climits>
#include <float.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <cassert>
#include <map>

#ifdef OPENMP
#include <omp.h>
#endif

#include <sys/time.h>

extern "C" {
#include <ffindex.h>
}

#include "cs.h"
#include "context_library.h"
#include "library_pseudocounts-inl.h"
#include "crf_pseudocounts-inl.h"

#include "hhdecl.h"
#include "list.h"
#include "hash.h"
#include "util.h"
#include "hhutil.h"

#include "hhhmm.h"
#include "hhhit.h"
#include "hhalignment.h"
#include "hhhalfalignment.h"
#include "hhfullalignment.h"
#include "hhhitlist.h"

#include "hhmatrices.h"
#include "hhfunc.h"

#include "hhdatabase.h"

#include "hhprefilter.h"

#include "log.h"
#include "simd.h"
#include "hhhmmsimd.h"
#include "hhviterbimatrix.h"
#include "hhviterbirunner.h"

#include "hhposteriormatrix.h"
#include "hhposteriordecoderrunner.h"

class HHblits {
public:
  HHblits(Parameters& parameters, std::vector<HHblitsDatabase*>& databases);
  virtual ~HHblits();

  void Reset();

  static void ProcessAllArguments(Parameters& par);

  //print methods for hhalign and hhblits
  void printHitList();
  void printHHRFile();

  //writer for non-mpi version
  void writeHHRFile(char* hhrFile);
  void writeAlisFile(char* basename);
  void writeScoresFile(char* scoresFile);
  void writeM8(char* m8File);
  void writePairwiseAlisFile(char* pairwieseAlisFile, char outformat);
  void writeAlitabFile(char* alitabFile);
  void writePsiFile(char* psiFile);
  void writeHMMFile(char* HMMFile);
  void writeA3MFile(char* A3MFile);
  void writeMatricesFile(char* matricesOutputFileName);

  //output writer for mpi version
  static void writeHHRFile(HHblits& hhblits, std::stringstream& out);
  static void writeScoresFile(HHblits& hhblits, std::stringstream& out);
  static void writeM8(HHblits& hhblits, std::stringstream& out);
  static void writePairwiseAlisFile(HHblits& hhblits, std::stringstream& out);
  static void writeAlitabFile(HHblits& hhblits, std::stringstream& out);
  static void writePsiFile(HHblits& hhblits, std::stringstream& out);
  static void writeHMMFile(HHblits& hhblits, std::stringstream& out);
  static void writeA3MFile(HHblits& hhblits, std::stringstream& out);
  static void writeMatricesFile(HHblits& hhblits, std::stringstream& out);

  static void prepareDatabases(Parameters& par, std::vector<HHblitsDatabase*>& databases);

  virtual void run(FILE* query_fh, char* query_path);
  void run(ffindex_entry_t* entry, char* data,
      ffindex_index_t* sequence_index, char* seq,
      ffindex_index_t* header_index, char* header);

protected:
	// substitution matrix flavours
	float __attribute__((aligned(32))) P[20][20];
	float __attribute__((aligned(32))) R[20][20];
	float __attribute__((aligned(32))) Sim[20][20];
	float __attribute__((aligned(32))) S[20][20];
	float __attribute__((aligned(32))) pb[21];

	// secondary structure matrices
	float S73[NDSSP][NSSPRED][MAXCF];
	float S37[NSSPRED][MAXCF][NDSSP];
	float S33[NSSPRED][MAXCF][NSSPRED][MAXCF];

	Parameters& par;

	cs::ContextLibrary<cs::AA>* context_lib;
	cs::Crf<cs::AA>* crf;
	cs::Pseudocounts<cs::AA>* pc_hhm_context_engine;
	cs::Admix* pc_hhm_context_mode;
	cs::Pseudocounts<cs::AA>* pc_prefilter_context_engine;
	cs::Admix* pc_prefilter_context_mode;

	//database filenames
	std::vector<HHblitsDatabase*> dbs;

	// Create query HMM with maximum of par.maxres match states
	HMM* q;
	// Create query HMM with maximum of par.maxres match states (needed for prefiltering)
	HMM* q_tmp;

	// output A3M generated by merging A3M alignments for significant hits to the query alignment
	Alignment* Qali;
	// output A3M alignment with no sequence filtered out (only active with -all option)
	Alignment* Qali_allseqs;

	ViterbiMatrix** viterbiMatrices;
	PosteriorMatrix** posteriorMatrices;

	HitList hitlist; // list of hits with one Hit object for each pairwise comparison done
	std::map<int, Alignment*> alis;

	void perform_realign(HMMSimd& q_vec, const char input_format, std::vector<HHEntry*>& hits_to_realign);
	void mergeHitsToQuery(Hash<Hit>* previous_hits, int& seqs_found, int& cluster_found);
	void add_hits_to_hitlist(std::vector<Hit>& hits, HitList& hitlist);


private:
	static void help(Parameters& par, char all = 0);
	static void ProcessArguments(Parameters& par);
	void RescoreWithViterbiKeepAlignment(HMMSimd& q_vec, Hash<Hit>* previous_hits);
};

#endif /* HHBLITS_H_ */
