//   Match 2.0.1  -- aligns data series using dynamic programming
//   Copyright (C) 2001-2003  Lorraine E. Lisiecki and Philip A. Lisiecki 
//
//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; version 2
//of the License.
//
//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, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
//02111-1307, USA.
//
//The original authors reserve the right to license this program or
//modified versions of this program under other licenses at our
//discretion.
//
//If you publish results generated by this software please cite
//Lisiecki, L. E. and P. A. Lisiecki, The application of dynamic
//  programming to the correlation of paleoclimate records, 
//  submitted to Paleoceanography, 2001
//
//Any questions regarding this license or the operation of this
//software may be directed to Lorraine Lisiecki
//<zogalum@alum.mit.edu>.

#include <fstream>
#include <iostream>
#include <stdio.h>
#include "config.hh"

namespace std {}
using namespace std;

static string gettoken(string &line, string predelim, string postdelim) {
  string::size_type n;  
  n=line.find_first_not_of(predelim);
  if (n==line.npos) return "";
  line=line.substr(n);
  n=line.find_first_of(postdelim);
  string key;
  if(n==line.npos){
    key=line;
    line="";
  } else {
    key=line.substr(0,n);
    line=line.substr(n);
  }
  return key;
}

static int gcd(int a, int b) {
  while(b) {
    int c=b;
    b=a%b;
    a=c;
  }
  return a;
}

static int compare_speeds(const void *s1, const void *s2) {
  const int *a1=(const int *)s1;
  const int *a2=(const int *)s2;
  if(a1[0]*a2[1]>a2[0]*a1[1]) {
    return -1;
  } else if (a1[0]*a2[1]<a2[0]*a1[1]) {
    return 1;
  } else {
    return 0;
  }
}

struct floatkeys{
  string key;
  float *value;
};
struct floatnankeys{
  string key;
  floatnan *value;
};
struct strkeys{
  string key;
  string *value;
};
struct floatlistkeys{
  string key;
  vector<floatnan> *value;
};
struct strlistkeys{
  string key;
  vector<string> *value;
};

#define DEFINE_KEYS floatkeys fk[]={{"nomatch",&nomatch},\
			{"speedchange", &spch},\
			{"speedpenalty",&sppen},\
			{"gappenalty", &gap},\
                        {"gapsizemultiplier", &gapsize_multiplier},\
			{"tiepenalty", &tiepen},\
			{"reversetiepenalty", &tiepenT},\
			{"begin1", &st1},\
			{"end1", &end1},\
			{"numintervals1", &cnt1},\
			{"begin2", &st2},\
			{"end2", &end2},\
			{"numintervals2", &cnt2},\
			{"",0}};\
       floatnankeys fnk[]={{"normbegin1",&normst1},\
			   {"normbegin2",&normst2},\
			   {"normend1",&normend1},\
			   {"normend2",&normend2},\
			   {"",0}};\
       floatlistkeys flk[]={{"mean1",&mean1},\
			    {"mean2",&mean2},\
			    {"stddev1",&stddev1},\
			    {"stddev2",&stddev2},\
			    {"",0}};\
       strlistkeys slk[]={{"series1",&series1},\
		          {"series2",&series2},\
			  {"",0}};\
       strkeys sk[]={{"tiefile",&tiefile},\
		     {"series1gaps",&s1gapfile},\
		     {"series2gaps",&s2gapfile},\
		     {"matchfile", &matchfile},\
		     {"matrixfile", &matrixfile},\
		     {"logfile", &logfile},\
		     {"",0}};

bool config::read(string file){
  ifstream is(file.c_str());
  if(!is){
    cerr<<"Error opening config file "<< file << "\n";
    return false;
  }
  DEFINE_KEYS;
  while(!is.eof() && !is.fail()) {
    string line;
    if (getline(is,line)){
      string::size_type n;
      n=line.find('#');
      if (n!=line.npos){
	line=line.substr(0,n);
      }
      string key=gettoken(line, " \t\r", " \t=\r");
      if(key=="") continue;

      int i;
      for(i=0; fk[i].value; i++){
	if (fk[i].key==key){
	  string value=gettoken(line," \t=\r"," \t\r");
	  if (value=="" || sscanf(value.c_str(),"%f",fk[i].value)!=1) {
	    cerr<<"Value for '" << key << "' is bad or missing.\n";
	    return false;
	  }
	  goto nextline;
	}
      }
      for(i=0; fnk[i].value; i++){
	if (fnk[i].key==key){
	  string value=gettoken(line," \t=\r"," \t\r");
	  if (value=="") {
	    cerr<<"Value for '" << key << "' is missing.\n";
	    return false;
	  }
	  floatnan fvalue;
	  try {
	    fvalue=floatnan(value,0);
	  } catch (floatnan::parse_error) {
	    cerr<<"Value for '" << key << "' is bad or missing.\n";
	    return false;
	  }
	  (*fnk[i].value)=fvalue;
	  goto nextline;
	}
      }
      for(i=0; sk[i].value; i++){
	if (sk[i].key==key){
	  string value=gettoken(line," \t=\r"," \t\r");
	  if (value=="") {
	    cerr<<"Value for '" << key << "' is missing.\n";
	    return false;
	  }
	  *sk[i].value=value;
	  goto nextline;
	}
      }
      for(i=0; flk[i].value; i++){
	if (flk[i].key==key){
	  string value=gettoken(line," \t=\r"," \t\r");
	  if (value=="") {
	    cerr<<"Value for '" << key << "' is missing.\n";
	    return false;
	  }
	  do {
	    floatnan fvalue;
	    try {
	      fvalue=floatnan(value,0);
	    } catch (floatnan::parse_error) {
	      cerr<<"Value for '" << key << "' is bad or missing.\n";
	      return false;
	    }
	    flk[i].value->push_back(fvalue);
	    value=gettoken(line," \t\r"," \t\r");
	  } while(value!="");
	  goto nextline;
	}
      }
      for(i=0; slk[i].value; i++){
	if (slk[i].key==key){
	  string value=gettoken(line," \t=\r"," \t\r");
	  if (value=="") {
	    cerr<<"Value for '" << key << "' is missing.\n";
	    return false;
	  }
	  do {
	    slk[i].value->push_back(value);
	    value=gettoken(line," \t\r"," \t\r");
	  } while(value!="");
	  goto nextline;
	}
      }
      if (key=="speeds"){
	nspeeds=0;
	while(true) {
	  string num=gettoken(line," \t\r=,",":");
	  if (num=="") break;
	  string den=gettoken(line,":"," \t\r=,");
	  speeds=(int (*)[2])realloc(speeds,(nspeeds+1)*sizeof(speeds[0]));
	  if(sscanf(num.c_str(),"%d",&speeds[nspeeds][0])!=1){
	    cerr<<"speeds numerator is bad "<<num<<"\n";
	    return false;
	  }
	  if(sscanf(den.c_str(),"%d",&speeds[nspeeds][1])!=1){
	    cerr<<"speeds denominator is bad "<<den<<"\n";
	    return false;
	  }
	  nspeeds++;
	}
	goto nextline;
      }

      if(key=="targetspeed"){
	string num=gettoken(line," \t\r=,",":");
	string den=gettoken(line,":"," \t\r=,");
	if(sscanf(num.c_str(),"%d",&targetspeed[0])!=1){
	  cerr<<"targetspeed numerator is bad "<<num<<"\n";
	  return false;
	}
	if(sscanf(den.c_str(),"%d",&targetspeed[1])!=1){
	  cerr<<"targetspeed denominator is bad "<<den<<"\n";
	  return false;
	}
	goto nextline;
      }

      cerr<<"Unrecognized key '"<<key<<"'\n";
      return false;
    }
  nextline:;
  }
  bool result=true;
  if(st1>=end1) {
    cerr<<"End of series1 (end1) must be greater than beginning (begin1).\n";
    result=false;
  }
  if(st2>=end2) {
    cerr<<"End of series2 (end2) must be greater than beginning (begin2).\n";
    result=false;
  }
  if(cnt1<1) {
    cerr<<"The number of intervals in series1 (numintervals1) must be greater than 0.\n";
    result=false;
  }
  if(cnt2<1) {
    cerr<<"The number of intervals in series2 (numintervals2) must be greater than 0.\n";
    result=false;
  }
  if(series1.size()==0) {
    cerr<<"No file is given for series1.\n";
    result=false;
  }
  if(series2.size()==0) {
    cerr<<"No file is given for series2.\n";
    result=false;
  }
  if(series1.size()!=series2.size()) {
    cerr<<"The same number of files is not given for the two series.\n";
    result=false;
  }
  if(mean1.size()!=series1.size() && mean1.size()!=0) {
    cerr<<"The number of means must match the number of series1 files.\n";
    result=false;
  }
  if(mean2.size()!=series2.size() && mean2.size()!=0) {
    cerr<<"The number of means must match the number of series2 files.\n";
    result=false;
  }
  if(stddev1.size()!=series1.size() && stddev1.size()!=0) {
    cerr<<"The number of stddevs must match the number of series1 files.\n";
    result=false;
  }
  if(stddev2.size()!=series2.size() && stddev2.size()!=0) {
    cerr<<"The number of stddevs must match the number of series2 files.\n";
    result=false;
  }
  if(nspeeds==0) {
    cerr<<"Number of speeds is 0!\n";
    result=false;
  }
  for(int i=0;i<nspeeds;i++) {
    int d=gcd(speeds[i][0],speeds[i][1]);
    speeds[i][0]/=d;
    speeds[i][1]/=d;
  }
  qsort(speeds,nspeeds,sizeof(speeds[0]),compare_speeds);
  {
    int d=gcd(targetspeed[0],targetspeed[1]);
    targetspeed[0]/=d;
    targetspeed[1]/=d;
  }
  return result;
}

void config::write(ostream &os){
  DEFINE_KEYS;
  for(int i=0; fk[i].value; i++){
    os<<fk[i].key<<" "<<*fk[i].value<<"\n";
  }
  for(int i=0; fnk[i].value; i++){
    os<<fnk[i].key<<" "<<*fnk[i].value<<"\n";
  }
  for(int i=0; sk[i].value; i++){
    if((*sk[i].value)!=""){
      os<<sk[i].key<<" "<<*sk[i].value<<"\n";
    } else{
      os<<"#"<<sk[i].key<<" is not set.\n";
    }
  }
  for(int i=0; flk[i].value; i++){
    if(flk[i].value->size()!=0){
      os<<flk[i].key;
      for(unsigned int k=0; k<flk[i].value->size(); k++) 
	os<<" "<<((*flk[i].value)[k]);
      os<<"\n";
    } else{
      os<<"#"<<flk[i].key<<" is not set.\n";
    }
  }
  for(int i=0; slk[i].value; i++){
    if(slk[i].value->size()!=0){
      os<<slk[i].key;
      for(unsigned int k=0; k<slk[i].value->size(); k++) 
	os<<" "<<((*slk[i].value)[k]);
      os<<"\n";
    } else{
      os<<"#"<<slk[i].key<<" is not set.\n";
    }
  }
  os<<"speeds";
  for(int i=0;i<nspeeds;i++) {
    os<<" "<<speeds[i][0]<<":"<<speeds[i][1];
  }
  os<<"\n";
  os<<"targetspeed "<<targetspeed[0]<<":"<<targetspeed[1]<<"\n";
}

#if 0
main(){
  config c;
  bool result= c.read("test.conf");
  //  cout<<result<<" "<<c.nomatch<<" "<<c.spch<<" "<<c.series1<<"\n";
  //  cout<<c.nspeeds;
  //  cout<<"\n";
  c.write(cout);
  return 0;
}
#endif
