#include "hitset.h"
#include "sequence.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: hitset.cpp 157 2012-07-25 05:58:09Z yinan $ */

int HitPairSet::add(Hit * hit1, Hit * hit2) {
  int i;

  /* if there is one different pair, there are multiple alignments for read */
  if (nHit >= 2)
    unique = false;

  if (nHit >= MAX_NUM_HITS)
  {
//		elog(WARNING, "WARNING: result set is full. Some reads may be discarded.\n");
    return MSG_HITSETFULL;
  }

  if ((maxMatch == MAX_INT && nHit >= maxHit && !sorted) || nHit > maxMatch)
    return MSG_HITSETFULL;

  /* duplication has been removed in each hitset.*/

  int nError = 0;
  if (hit1->error.num > 0)
    nError += hit1->error.num;
  if (hit2->error.num > 0)
    nError += hit2->error.num;
  if (nError < MAX_NUM_COUNT)
  {
    counts[nError]++;
    psum += p[nError];
  }

  i = nHit;
  if (sorted) {
    for (; i > 0; i -= 2) {
      if (hits[i - 2].error.num + hits[i - 1].error.num
          <= hit1->error.num + hit2->error.num
          || (hits[i - 2].error.num + hits[i - 1].error.num
              == hit1->error.num + hit2->error.num
              && hit2[i - 2].qual + hits[i - 1].qual <= hit1->qual + hit2->qual))
        break;
      hits[i] = hits[i - 2];
      hits[i + 1] = hits[i - 1];
    }
  }
  hits[i] = *hit1;
  hits[i + 1] = *hit2;
  hits[i].qual = hits[i + 1].qual = UNASSIGNED_QUAL;

  if (nHit < maxHit || (maxMatch != MAX_INT && nHit <= maxMatch))
    nHit += 2;

//	if ((maxMatch == MAX_INT && nHit >= maxHit && !sorted) || nHit > maxMatch) 
//		return MSG_HITSETFULL;

  return SUCCESS;
}

/* 	
 *	HitPairSet::build()
 *	check if the pairs of matched mate1 and mate2 satisfy 
 *	the constrain on the distance between mate1 and mate2.
 */
int HitPairSet::build(HitSet * set1, HitSet * set2, uint32 minins,
    uint32 maxins, bool pairStrand[][2], bool reportMateMatch) {
  int i, j;
  unsigned int pos1, pos2;
  strand s1, s2;
  int ret;
  bool pairMatch = false;

  reset();

  properMatch = true;

  /* join the two hitset and get the pair-end alignment */
  for (i = 0; i < set1->nHit; i++) {
    for (j = 0; j < set2->nHit; j++) {
      pos1 = set1->hits[i].pos;
      pos2 = set2->hits[j].pos;
      s1 = set1->hits[i].strand;
      s2 = set2->hits[j].strand;

      /* mate 2 is upstream, mate 1 is downstream */
      if (pos1 > pos2 && pos1 - pos2 + length >= minins
          && pos1 - pos2 + length <= maxins && pairStrand[s1][s2]) {
        ret = add(&set1->hits[i], &set2->hits[j]);
      }

      /*mate 1 is upstream, mate 2 is downstream */
      if (pos1 <= pos2 && pos2 - pos1 + length >= minins
          && pos2 - pos1 + length <= maxins && pairStrand[s1][s2]) {
        ret = add(&set1->hits[i], &set2->hits[j]);
      }

      if (ret == MSG_HITSETFULL
        )
        return nHit;
    }
  }

  if (nHit > 0)
    return nHit;

  properMatch = false;

  /* if no proper matched, find matched mates */
  if (set1->nHit > 0 && set2->nHit > 0) {
    for (i = 0; i < set1->nHit; i++) {
      for (j = 0; j < set2->nHit; j++) {
        ret = add(&set1->hits[i], &set2->hits[j]);

        if (ret == MSG_HITSETFULL
          )
          return nHit;
      }
    }
  }

  if (nHit > 0)
    return nHit;

  /* if no matched pair, add partial matched pair */
  if (nHit == 0 && reportMateMatch) {
    Hit hit;

    /* find matching in mate1 */
    for (i = 0; i < set1->nHit; i++) {
      hit = set1->hits[i];
      hit.error.num = -1;
      hit.strand = FORWARD;
      ret = add(&set1->hits[i], &hit);
      if (ret == MSG_HITSETFULL
        )
        return nHit;
    }

    /* find matching in mate2 */
    for (i = 0; i < set2->nHit; i++) {
      hit = set2->hits[i];
      hit.error.num = -1;
      hit.strand = FORWARD;
      ret = add(&hit, &set2->hits[i]);
      if (ret == MSG_HITSETFULL
        )
        return nHit;
    }
  }

  return nHit;
}

