#include "ap_config.h"

#ifdef _WIN32
#include "windows.h"
#endif

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>

#include "parse.h"
#include "xml_util.h"
#include "db_table.h"
#include "ap_schema.h"
#include "astropulse.h"
#include "util.h"
#undef read

#include <cstring>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <cerrno>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

namespace Astropulse {

const int NUM_SAMPLES_TO_COPY = 32768 * 2;

std::string cp_wu_to_string(std::string wufilename, int peak_bin) {
  std::string outstring;
  FILE * wufile = fopen(wufilename.c_str(), "r");
  outstring = get_wu_header(wufile);
  outstring += get_wu_raw_data(wufile, peak_bin);
  fclose(wufile); 
}

string get_outfilename(string wufilename, int peak_bin) {
  std::stringstream peak_bin_stream;
  peak_bin_stream << peak_bin;
  return wufilename + "_" + peak_bin_stream.str() + ".swu";
}

void get_wu_header(FILE * wufile, int peak_bin) {
  fseek(wufile, 0, SEEK_SET);
  int type;
  std::string outstring = "";
  std::string tag;
  std::string content;
  std::string whole_xml;

  // do we need to add "\n" after each whole_xml?
  while(get_xml(wufile, type, tag, content, whole_xml)) {
    if(tag == "data") {
      // fprintf(outfile, "<minifile_peak_bin>%d</minifile_peak_bin>\n", peak_bin);
      return;
    }
    outstring += whole_xml;
  }

  fprintf(stderr, "Error in ap_minifiles_db.cpp : cp_header : didn't find data.\n");
  printf("Error in ap_minifiles_db.cpp : cp_header : didn't find data.\n");
}

void get_wu_raw_data(FILE * wufile, long peak_bin) {
  // Assume we are starting at the data already
  long start_byte = max(0, (peak_bin - NUM_SAMPLES_TO_COPY / 2) / 4);
  long max_start = 8 * 1024 * 1024 - NUM_SAMPLES_TO_COPY/4 - 8;
  start_byte = min(start_byte, max_start);
  int ch, result;
  std::string outstring = "";

  fseek(wufile, start_byte, SEEK_CUR);

  for(long i = 0; i < NUM_SAMPLES_TO_COPY / 4; i++) {
    if((ch = getc(wufile)) == EOF) {
      fprintf(stderr, "Error in ap_minifiles_db.cpp : cp_data : can't get char\n");
      printf("Error in ap_minifiles_db.cpp : cp_data : can't get char\n");
      exit(-1);
    }
    if((result = putc((char) ch, outfile)) == EOF) {
      fprintf(stderr, "Error in ap_minifiles_db.cpp : cp_data : can't write to file\n");
      printf("Error in ap_minifiles_db.cpp : cp_data : can't write to file\n");
      exit(-1);
    }
  }
}

/* get_xml
 *
 * return value = true if we read EOF immediately (no more tags)
 * 		  false otherwise
 */
bool get_xml(FILE * file, int& type, string& tag, string& content, string& whole_xml) {
  bool empty_tag = true;
  bool empty_content = true;
  int ch_int;
  char first_ch, second_ch, ch;
  bool end_of_file = false;
  tag = "";
  content = "";
  whole_xml = "";

  /*
  if(!isopen(file)) { 
    fprintf(stderr, "Error in ap_minifiles_db.cpp : get_xml : file not open\n");
    exit(-1);
  }
  */

  ch_int = getc(file);
  if(ch_int == EOF) return true;
  first_ch = (char) ch_int;

  if(first_ch == '<') {  // reading a tag
    whole_xml.push_back(first_ch);
    second_ch = getc_err_on_EOF(file);
    whole_xml.push_back(second_ch);
    if(second_ch == '/') {  // reading an end tag
      type = 1;
      ch = getc_err_on_EOF(file);
      whole_xml.push_back(ch);
      // At this point, we have read and pushed back "</d" of "</data>"
      // and ch = 'd'
    } else {  // reading a start tag
      type = 0;
      ch = second_ch;
      // At this point, we have read and pushed back "<d" of "<data>"
      // and ch = 'd'
    }
    while(ch != '>' && ch != ' ') { // reading tag name
      if(empty_tag) empty_tag = false;
      tag.push_back(ch);  // push back a character known to be good
      ch = getc_err_on_EOF(file); // get next character
      whole_xml.push_back(ch); // whole_xml gets all characters
    }
    if(ch == ' ') { // reading content within tag
      ch = getc_err_on_EOF(file);
      whole_xml.push_back(ch);  // whole_xml gets all characters
      while(ch != '>') {
        if(empty_content) empty_content = false;
        content.push_back(ch);  // push back a character known to be good
        ch = (char) getc(file);
        whole_xml.push_back(ch);
      }
    }
  } else {  // we're reading content between tags
    type=2;
    ch = first_ch;
    while(ch != '<') {
      if(empty_content) empty_content = false;
      content.push_back(ch);
      whole_xml.push_back(ch);
      ch_int = getc(file);
      ch = (char) ch_int;
      if(ch_int == EOF) {
        // end_of_file = true;
        fprintf(stderr, "In ap_minifiles_db.cpp : reached EOF in the middle of a tag\n");
        printf("In ap_minifiles_db.cpp : reached EOF in the middle of a tag\n");
        break;
      }
    }
    fseek(file, -1, SEEK_CUR);  // move filepos back one, get ready to read '<' or EOF
  }
  if(empty_tag) tag = "NO_TAG";
  if(empty_content) content = "NO_CONTENT";
  return end_of_file;
}

char getc_err_on_EOF(FILE* file) {      
  int ch_int = getc(file);
  char ch;
  if(ch_int == EOF) {
      fprintf(stderr, "Error in ap_minifiles_db.cpp : getc_err_on_EOF : unexpected EOF\n");
      printf("Error in ap_minifiles_db.cpp : getc_err_on_EOF : unexpected EOF\n");
  }
  ch = (char) ch_int;
  return ch;
}

}
