#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bitread.h"
#include "sequence.h"
#include "error.h"

void genRandomSequence(char * str, int len) {
  for (int i = 0; i < len; i++) {
    switch (rand() % 4) {
    case 0:
      str[i] = 'A';
      break;
    case 1:
      str[i] = 'C';
      break;
    case 2:
      str[i] = 'G';
      break;
    case 3:
      str[i] = 'T';
      break;
    }
  }
}
bool testExtract() {
  int len, offset;
  int i, j;
  char str[1024], str1[256];
  char * fname[1];
  int64 space[16];
  int64 * key = &space[8];
  int ret;

  fname[1] = new char[128];
  strcpy(fname[1], "./tmp_test.fa");

  genRandomSequence(str, 1000);

  FILE * file = fopen(fname[1], "w");
  for (i = 0; i < 20; i++) {
    for (j = 0; j < 50; j++) {
      fprintf(file, "%c", str[i * 50 + j]);
    }
    fprintf(file, "\n");
  }
  fclose(file);

  CompactSequence * sequence = new CompactSequence(true);
  sequence->build(fname, 1, 36, 2);
  if (ret != SUCCESS)
  {
    printf("Failed to load reference sequences.\n");
    return false;
  }

  int64 * seq = sequence->getSequence();

  printf("Testing extract... ");

  for (len = 36; len <= 36; len++) {
    offset = rand() % 500;
    BitRead::extract(seq, key, offset * BITS_PER_BASE, len * BITS_PER_BASE);

    CompactSequence::decompose(str1, len, key);

    for (i = 0; i < len; i++) {
      if (str[i + offset] != str1[i]) {
        printf("Failed at %d bps\n", len);
        return false;
      }
    }
  }

  printf("Passed\n");
  return true;
}

bool testRemoveHead() {
  int len;
  int head, i;
  char str1[256], str2[256];
  int64 space1[16], space2[16];
  int64 mask[WORDS_PER_READ];
  int64 * key1, *key2;

  key1 = &space1[8];
  key2 = &space2[8];

  printf("Testing remove head... ");

  for (len = 36; len <= 128; len++) {

    genRandomSequence(str1, len);

    CompactSequence::compose(str1, len, key1);

    for (head = len - 10; head < len; head++) {

      BitRead::genHeadMask(mask, head * BITS_PER_BASE);

      BitRead::removeHead(key1, key2, mask);

      CompactSequence::decompose(str2, head, key2);
      /*
       printf("\n");
       for (i = 0; i < len; i++)
       printf("%c", str1[i]);
       printf("\n");

       for (i = 0; i < len - head; i++)
       printf(" ");
       for (i = 0; i < head; i++)
       printf("%c", str2[i]);
       printf("\n");
       */
      //check
      for (i = 0; i < head; i++) {
        if (str1[i + len - head] != str2[i]) {
          printf("Failed at %d bps, %d head\n", len, head);
          return false;
        }
      }
    }
  }

  printf("Passed\n");
  return true;
}

bool testRemoveInterval() {
  int len;
  int p, offset, i;
  char str1[256], str2[256];
  int64 space1[16], space2[16];
  int64 * key1, *key2;

  key1 = &space1[8];
  key2 = &space2[8];

  printf("Testing remove interval... ");

  for (len = 36; len <= 128; len++) {

    genRandomSequence(str1, len);

    CompactSequence::compose(str1, len, key1);

    for (p = 2; p <= 8; p++) {

      for (offset = 0; offset < len / p * p; offset += len / p) {

        BitRead::removeInterval(key1, key2, offset * BITS_PER_BASE
            , len / p * BITS_PER_BASE);

        CompactSequence::decompose(str2, len - len / p, key2);
        /*
         for (i = 0; i < len; i++)
         printf("%c", str1[i]);
         printf("\n");

         for (i = 0; i < len - offset - len/p; i++)
         printf("%c", str2[i]);
         for (i = 0; i < len/p; i++)
         printf(" ");
         for (i = len - offset - len/p; i < len - len/p; i++)
         printf("%c", str2[i]);
         printf("\n\n");
         */

        //check
        for (i = 0; i < len - offset - len / p; i++) {
          if (str1[i] != str2[i]) {
            printf("Failed at %d bps, %d partition, %d offset\n", len, p,
                offset);
            return false;
          }
        }

        for (i = len - offset - len / p; i < len - len / p; i++) {
          if (str1[i + len / p] != str2[i]) {
            printf("Failed at %d bps, %d partition, %d offset\n", len, p,
                offset);
            return false;
          }
        }

      }
    }
  }

  printf("Passed\n");
  return true;
}

int main() {
  int i = 1;
  int j = 2;

//	testExtract();

  testRemoveHead();

  testRemoveInterval();

  return 0;
}
