#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <nicklib.h>
#include <globals.h>
#include <mcmcpars.h>
#include <getpars.h>

#include "admutils.h"
#include "mcio.h"  

#define WVERSION   "2450" 
#define MAXFL  50   
#define MAXSTR  512

// phasedmode added
// bugfix for pedcols
// mcio (bigread)
// mergeit (overflow bug in clear) 
// and better code for mergeit routine (overflow bug in cclear)
// malexhet added
// polarmode added both input files assumed polarized

char *trashdir = "/var/tmp";
extern int verbose;
int qtmode = NO;
Indiv **indm1, **indm2;
SNP **snpm1, **snpm2;
int nums1, nums2;
int numi1, numi2;

char *snp1 = NULL;
char *ind1 = NULL;
char *geno1 = NULL;

char *snp2 = NULL;
char *ind2 = NULL;
char *geno2 = NULL;

char *indoutfilename = NULL;
char *snpoutfilename = NULL;
char *genooutfilename = NULL;
char *badsnpname = NULL;

int packout = -1;
int tersem = YES;
extern enum outputmodetype outputmode;
extern int checksizemode;
char *omode = "packedancestrymap";
extern int packmode;
extern long packlen;

int ogmode = NO;
int docheck = YES;
int strandcheck = YES;
int polarmode = NO;
int phasedmode = NO;
int allowdups = NO;

int xchrom = -1;
int lopos = -999999999;
int hipos = 999999999;
int minchrom = 1;
int maxchrom = 97;

/** 
 docheck:  YES  allele flipping check
 hashcheck:  YES  ... NO => snp names, Indiv names changed MUST retain order and number
 strandcheck: (default YES) if NO then alleles are assumed on same strand
 allowdups: YES (second set of data ... dup individuals are killed otherwise fatal error
 rewrote mergeit()  removed call to rmindivs (bug??)
 */

char unknowngender = 'U';

void
setomode (enum outputmodetype *outmode, char *omode);
void
readcommands (int argc, char **argv);
void
outfiles (char *snpname, char *indname, char *gname, SNP **snpm, Indiv **indiv,
          int numsnps, int numind, int packem, int ogmode);
int
checkmatch (SNP *cupt1, SNP *cupt2);

int
mergeit (SNP **snpm1, SNP **snpm2, Indiv ***pindm1, Indiv **indm2, int nums1,
         int nums2, int numi1, int numi2);

int
main (int argc, char **argv)
{
  SNP **snpmarkers;
  Indiv **indivmarkers;
  int numsnps, numindivs;
  unsigned char *packg1, *packg2;

  int **snppos;
  int *snpindx;
  int lsnplist, lindlist, numeg;
  int i, j;
  SNP *cupt, *cupt1, *cupt2, *cupt3;
  Indiv *indx;

  int ch1, ch2;
  int fmnum, lmnum;
  int num, n1, n2;
  int nkill = 0;
  int t, k, x;

  int nignore, numrisks = 1;

  char **genolist;
  int numgenolist;
  int maxmiss;
  int sorder[2];

  tersem = YES;     // no snp counts

  readcommands (argc, argv);

  setomode (&outputmode, omode);
  packmode = YES;
  settersemode (tersem);
  if (phasedmode)
    malexhet = YES;
  if (polarmode)
    {
      strandcheck = NO;
      printf ("polarmode set!\n");
    }

  nums1 = getsnps (snp1, &snpm1, 0.0, NULL, &nignore, numrisks);

  sorder[0] = getsnpordered ();
  putped (1);
  freeped ();

  sorder[1] = getsnpordered ();
  nums2 = getsnps (snp2, &snpm2, 0.0, NULL, &nignore, numrisks);

  putped (2);
  freeped ();

  for (x = 0; x < nums1; ++x)
    {
      cupt1 = snpm1[x];
      cupt1->tagnumber = -1;
    }
  for (x = 0; x < nums2; ++x)
    {
      cupt2 = snpm2[x];
      t = x % 1000;
// if (t==0) printf("zz %d %d\n", x, nums2) ;

      k = snpindex (snpm1, nums1, cupt2->ID);
      if (k < 0)
        {
          cupt2->ignore = YES;
          continue;
        }
      cupt1 = snpm1[k];
      cupt1->tagnumber = x;
      t = checkmatch (cupt1, cupt2);
      if (t == 1)
        continue;
      if (t == 2)
        {
          cupt2->isrfake = YES;
          continue;
        }
      if (t < 0)
        {
          cupt1->ignore = cupt2->ignore = YES;
          continue;
        }
      printf ("allele funny: %s", cupt1->ID);
      printalleles (cupt1, stdout);
      printalleles (cupt2, stdout);
      printnl ();
      cupt1->ignore = cupt2->ignore = YES;
      continue;
    }
  freesnpindex ();
  numi1 = getindivs (ind1, &indm1);
  numi2 = getindivs (ind2, &indm2);

  for (x = 0; x < numi2; ++x)
    {
      k = indindex (indm1, numi1, indm2[x]->ID);
      if ((k >= 0) && (allowdups == NO))
        fatalx ("dup ind: %s\n", indm2[x]->ID);  // fix later?  
      if ((k >= 0) && (allowdups) && (indm1[k]->ignore == NO))
        indm2[x]->ignore = YES;
    }

  setgenotypename (&geno1, ind1);
  getped (1);
  putsnpordered (sorder[0]);
  getgenos (geno1, snpm1, indm1, nums1, numi1, nignore);

  packg1 = (unsigned char *) getpackgenos ();
  clearpackgenos ();

  setgenotypename (&geno2, ind2);
  getped (2);
  putsnpordered (sorder[1]);
  getgenos (geno2, snpm2, indm2, nums2, numi2, nignore);

  /**
   numi1 = rmindivs(snpm1, nums1, indm1, numi1)  ; 
   numi2 = rmindivs(snpm2, nums2, indm2, numi2)  ; 
   */

  packg2 = (unsigned char *) getpackgenos ();
  numindivs = mergeit (snpm1, snpm2, &indm1, indm2, nums1, nums2, numi1, numi2);

  snpmarkers = snpm1;
  numsnps = nums1;
  indivmarkers = indm1;

  free (packg1);
  free (packg2);

//  numsnps = rmsnps(snpmarkers, numsnps, NULL) ;

  printf ("numsnps: %d  numindivs: %d\n", numsnps, numindivs);

  outfiles (snpoutfilename, indoutfilename, genooutfilename, snpmarkers,
            indivmarkers, numsnps, numindivs, packout, ogmode);

  printf ("##end of mergeit run\n");
  return 0;
}
int
checkmatch (SNP *cupt1, SNP *cupt2)
{

  char a1, a2, b1, b2;

  if (docheck == NO)
    return 1;
  if (cupt1->alleles == NULL)
    return -1;
  if (cupt2->alleles == NULL)
    return -1;

  a1 = cupt1->alleles[0];
  a2 = cupt1->alleles[1];

  b1 = cupt2->alleles[0];
  b2 = cupt2->alleles[1];

  a1 = toupper(a1);
  a2 = toupper(a2);

  b1 = toupper(b1);
  b2 = toupper(b2);

  if ((a1 == 'X') && (a2 == 'X') && (strandcheck))
    return -1;  // flipcheck impossible

  if (strandcheck)
    {

      if ((a1 == 'A') && (a2 == 'T'))
        return -1;
      if ((a1 == 'T') && (a2 == 'A'))
        return -1;
      if ((a1 == 'C') && (a2 == 'G'))
        return -1;
      if ((a1 == 'G') && (a2 == 'C'))
        return -1;

      if ((b1 == 'A') && (b2 == 'T'))
        return -1;
      if ((b1 == 'T') && (b2 == 'A'))
        return -1;
      if ((b1 == 'C') && (b2 == 'G'))
        return -1;
      if ((b1 == 'G') && (b2 == 'C'))
        return -1;
    }

  if (polarmode)
    {
      if ((a1 == b1) && (a2 == b2))
        return 1;
      if ((a1 == b1) && (b2 == 'X'))
        return 1;
      b1 = compbase (b1);
      b2 = compbase (b2);
      if ((a1 == b1) && (a2 == b2))
        return 1;
      return -1;
    }

  if ((a1 == b1) && (a2 == 'X'))
    {
      cupt1->alleles[1] = b2;
      return 1;
    }

  if ((a1 == b2) && (a2 == 'X'))
    {
      cupt1->alleles[1] = b1;
      return 2;
    }

  if ((a1 == b1) && (a2 == b2))
    return 1;
  if ((a1 == b2) && (a2 == b1))
    return 2;

  if ((a1 == b1) && (b2 == 'X'))
    return 1;
  if ((a2 == b1) && (b2 == 'X'))
    return 2;

  if (strandcheck == NO)
    return 0;

  b1 = compbase (b1);
  b2 = compbase (b2);

  if ((a1 == b1) && (a2 == 'X'))
    {
      cupt1->alleles[1] = b2;
      return 1;
    }

  if ((a1 == b2) && (a2 == 'X'))
    {
      cupt1->alleles[1] = b1;
      return 2;
    }

  if ((a1 == b1) && (a2 == b2))
    return 1;
  if ((a1 == b2) && (a2 == b1))
    return 2;

  if ((a1 == b1) && (b2 == 'X'))
    return 1;
  if ((a2 == b1) && (b2 == 'X'))
    return 2;

  return 0;

}

void
readcommands (int argc, char **argv)

{
  int i, haploid = 0;
  char *parname = NULL;
  phandle *ph;
  char str[5000];
  char *tempname;
  int n;

  while ((i = getopt (argc, argv, "p:vVf")) != -1)
    {

      switch (i)
        {

        case 'p':
          parname = strdup (optarg);
          break;

        case 'v':
          printf ("version: %s\n", WVERSION);
          break;

        case 'V':
          verbose = YES;
          break;

        case 'f':
          phasedmode = YES;
          break;

        case '?':
          printf ("Usage: bad params.... \n");
          fatalx ("bad params\n");
        }
    }

  pcheck (parname, 'p');
  printf ("parameter file: %s\n", parname);
  ph = openpars (parname);
  dostrsub (ph);

  getstring (ph, "geno1:", &geno1);
  getstring (ph, "snp1:", &snp1);
  getstring (ph, "ind1:", &ind1);

  getstring (ph, "geno2:", &geno2);
  getstring (ph, "snp2:", &snp2);
  getstring (ph, "ind2:", &ind2);

  getstring (ph, "indoutfilename:", &indoutfilename);
  getstring (ph, "snpoutfilename:", &snpoutfilename);
  getstring (ph, "genooutfilename:", &genooutfilename);
  getstring (ph, "outputformat:", &omode);

  getint (ph, "malexhet:", &malexhet);
  getint (ph, "nomalexhet:", &malexhet); /* changed 11/02/06 */

  getint (ph, "docheck:", &docheck);
  getint (ph, "hashcheck:", &hashcheck);
  getint (ph, "strandcheck:", &strandcheck);
  getint (ph, "phasedmode:", &phasedmode);
  getint (ph, "numchrom:", &numchrom);
  getint (ph, "allowdups:", &allowdups);
  getint (ph, "polarmode:", &polarmode);

  writepars (ph);
  closepars (ph);

}
int
mergeit (SNP **snpm1, SNP **snpm2, Indiv ***pindm1, Indiv **indm2, int nums1,
         int nums2, int numi1, int numi2)
{
  SNP *cupt1, *cupt2;
  int k, x, g, t, tt;
  double y;
  long rlen;
  static unsigned char *packg;
  unsigned char *buff;
  Indiv **indm1;
  static Indiv **indivmarkers;
  int numindivs, numsnps;
  int newnumi1, newnumi2;

  indm1 = *pindm1;
  numindivs = numi1 + numi2;
  numsnps = nums1;
  ZALLOC(indivmarkers, numindivs, Indiv *);

  t = 0;
  for (x = 0; x < numi1; ++x)
    {
      if (indm1[x]->ignore)
        continue;
      indivmarkers[t] = indm1[x];
      ++t;
    }
  newnumi1 = t;
  for (x = 0; x < numi2; ++x)
    {
      if (indm2[x]->ignore)
        continue;
      indivmarkers[t] = indm2[x];
      ++t;
    }
  newnumi2 = t - newnumi1;
  numindivs = t;
// we don't bother with a destructor here.   Sloppy code

  y = (double) (numindivs * 2) / (8 * (double) sizeof(char));
  rlen = nnint (ceil (y));
  rlen = MAX(rlen, 48);
  packlen = numsnps * rlen;
  ZALLOC(packg, packlen, unsigned char);
  clearepath (packg);
// wipe to invalid

  buff = packg;
  for (k = 0; k < nums1; k++)
    {
      cupt1 = snpm1[k];
      x = cupt1->tagnumber;
      if (x < 0)
        cupt1->ignore = YES;
      if (cupt1->ignore)
        continue;
      cupt2 = snpm2[x];
      if (cupt2->isrfake)
        {
          if (phasedmode == NO)
            flipalleles (cupt2);
          if (phasedmode == YES)
            flipalleles_phased (cupt2);
        }
      tt = 0;
      for (t = 0; t < numi1; ++t)
        {
          if (indm1[t]->ignore)
            continue;
          g = getgtypes (cupt1, t);
          if (g < 0)
            g = 3;
          wbuff ((unsigned char *) buff, tt, g);
          ++tt;
        }
      for (t = 0; t < numi2; ++t)
        {
          if (indm2[t]->ignore)
            continue;
          g = getgtypes (cupt2, t);
          if (g < 0)
            g = 3;
          wbuff ((unsigned char *) buff, tt, g);
          ++tt;
        }
      cupt1->ngtypes = numindivs;
      cupt1->pbuff = (char *) buff;
      buff += rlen;
    }
  *pindm1 = indivmarkers;
  return numindivs;
}
