#include <stdlib.h>
#include <string.h>
#include "opm_param.h"
#include "file.h"

static void read_opm_param(opm_param *p, char *file);
static void check_opm_param_null(opm_param *p, char *func);

#define NUM_OPM_PARAM 15
static char opm_param_key[NUM_OPM_PARAM][12]={
   "MIRROR", "MAGNIFY", "ANGLE", "XCENTER", "YCENTER", "HALFSIZE",
   "MAG_CUT1", "MAG_CUT2", "NSET", "NVERIFY",
   "PV_LIMIT", "EPSILON", "TOLERANCE", "MATCHING", "NITER"
};

void set_opm_param(opm_param *p, char *file){
   get_opm_param_default(p);
   read_opm_param(p,file);
}

static void read_opm_param(opm_param *p, char *file){
   int i;
   FILE *fp;
   char line[LINE_LENGTH], keyword[LINE_LENGTH], item[LINE_LENGTH];
   fp=fileopen(file,"r");
   while(!feof(fp)){
      fgets(line,sizeof(line),fp);
      if(comment_line(line)||feof(fp)) continue;
      if(sscanf(line,"%s %s",keyword,item)!=2) continue;
      for(i=0;i<NUM_OPM_PARAM;i++){
         if(strcmp(keyword,opm_param_key[i])==0) set_opm_param1(i,p,item);
      }
   }
   fclose(fp);
}

void set_opm_param1(int mode, opm_param *p, char *item){
   Boolean value_bool;
   double value_dbl;
   int value_int;
   if(mode==0){
      if(strcmp(item,"ON")==0||strcmp(item,"TRUE")==0||strcmp(item,"YES")==0) value_bool=TRUE;
      else value_bool=FALSE;
   } else if(mode==2){
      if(strcmp(item,"UNDEF")==0||strcmp(item,"NULL")==0) value_bool=FALSE;
      else {
         value_bool=TRUE; value_dbl=atof(item);
      }
   } else if(mode==6||mode==7){
      if(strcmp(item,"UNDEF")==0||strcmp(item,"NULL")==0) value_bool=FALSE;
      else {
         value_bool=TRUE; value_dbl=atof(item);
      }
   } else if(mode==8||mode==9||mode==14){
      value_int=atoi(item);
   } else {
      value_dbl=atof(item);
   }
   switch(mode){
   case 0 : p->mirror=value_bool; break;
   case 1 : p->magnify=value_dbl; break;
   case 2 :
      p->rotate=value_bool;
      if(value_bool==TRUE) p->angle=value_dbl;
      else p->angle=0;
      break;
   case 3 : p->xcen=value_dbl; break;
   case 4 : p->ycen=value_dbl; break;
   case 5 : p->size=value_dbl; break;
   case 6 :
      p->cut1=value_bool;
      if(value_bool==TRUE) p->mag_cut1=value_dbl;
      else p->cut1=0;
      break;
   case 7 :
      p->cut2=value_bool;
      if(value_bool==TRUE) p->mag_cut2=value_dbl;
      else p->cut2=0;
      break;
   case 8 : p->nset=value_int; break;
   case 9 : p->nverify=value_int; break;
   case 10 : p->pv_limit=value_dbl; break;
   case 11 : p->epsilon=value_dbl; break;
   case 12 : p->radius=value_dbl; break;
   case 13 : p->matching=value_dbl; break;
   case 14 : p->niter=value_int; break;
   default: return;
   }
}

void get_opm_param_default(opm_param *p){
   check_opm_param_null(p,"get_opm_param_default");
   p->mirror=FALSE;
   p->magnify=1.0;
   p->rotate=FALSE;
   p->angle=0;
   p->xcen=0.0;
   p->ycen=0.0;
   p->size=250.0;
   p->cut1=FALSE;
   p->mag_cut1=0.0;
   p->cut2=FALSE;
   p->mag_cut2=0.0;
   p->nset=50;
   p->nverify=-1;
   p->pv_limit=0.025;
   p->epsilon=0.02;
   p->radius=1.0;
   p->matching=0.5;
   p->niter=3;
}

Boolean get_opm_mirror(opm_param *p){
   check_opm_param_null(p,"get_opm_mirror");
   return (p->mirror);
}

double get_opm_magnify(opm_param *p){
   check_opm_param_null(p,"get_opm_magnify");
   return (p->magnify);
}

Boolean get_opm_rotate(opm_param *p){
   check_opm_param_null(p,"get_opm_rotate");
   return (p->rotate);
}

double get_opm_angle(opm_param *p){
   check_opm_param_null(p,"get_opm_angle");
   return (p->angle);
}

double get_opm_pv_limit(opm_param *p){
   check_opm_param_null(p,"get_opm_pv_limit");
   return (p->pv_limit);
}

double get_opm_epsilon(opm_param *p){
   check_opm_param_null(p,"get_opm_epsilon");
   return (p->epsilon);
}

double get_opm_radius(opm_param *p){
   check_opm_param_null(p,"get_opm_radius");
   return (p->radius);
}

double get_opm_matching(opm_param *p){
   check_opm_param_null(p,"get_opm_matching");
   return (p->matching);
}

Boolean get_opm_cut1(opm_param *p){
   check_opm_param_null(p,"get_opm_cut1");
   return (p->cut1);
}

double get_opm_mag_cut1(opm_param *p){
   check_opm_param_null(p,"get_opm_mag_cut1");
   return (p->mag_cut1);
}

Boolean get_opm_cut2(opm_param *p){
   check_opm_param_null(p,"get_opm_cut2");
   return (p->cut2);
}

double get_opm_mag_cut2(opm_param *p){
   check_opm_param_null(p,"get_opm_mag_cut2");
   return (p->mag_cut2);
}

double get_opm_xcen(opm_param *p){
   check_opm_param_null(p,"get_opm_xcen");
   return (p->xcen);
}

double get_opm_ycen(opm_param *p){
   check_opm_param_null(p,"get_opm_ycen");
   return (p->ycen);
}

double get_opm_size(opm_param *p){
   check_opm_param_null(p,"get_opm_size");
   return (p->size);
}

int get_opm_nset(opm_param *p){
   check_opm_param_null(p,"get_opm_nset");
   return (p->nset);
}

int get_opm_nverify(opm_param *p){
   check_opm_param_null(p,"get_opm_nverify");
   return (p->nverify);
}

int get_opm_niter(opm_param *p){
   check_opm_param_null(p,"get_opm_niter");
   return (p->niter);
}

void print_opm_param(FILE *fp, opm_param *p){
   fprintf(fp,"# OPM parameters:\n");
   if(get_opm_mirror(p)==TRUE) fprintf(fp,"#    %-9s = ON\n",opm_param_key[0]);
   else fprintf(fp,"#    %-9s = NULL\n",opm_param_key[0]);
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[1],p->magnify);
   if(p->rotate==TRUE)fprintf(fp,"#    %-9s = %g\n",opm_param_key[2],p->angle);
   else fprintf(fp,"#    %-9s = %s\n",opm_param_key[2],"UNDEF");
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[3],p->xcen);
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[4],p->ycen);
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[5],p->size);
   if(p->cut1==TRUE)fprintf(fp,"#    %-9s = %g\n",opm_param_key[6],p->mag_cut1);
   else fprintf(fp,"#    %-9s = %s\n",opm_param_key[6],"UNDEF");
   if(p->cut2==TRUE)fprintf(fp,"#    %-9s = %g\n",opm_param_key[7],p->mag_cut2);
   else fprintf(fp,"#    %-9s = %s\n",opm_param_key[7],"UNDEF");
   fprintf(fp,"#    %-9s = %d\n",opm_param_key[8],p->nset);
   fprintf(fp,"#    %-9s = %d\n",opm_param_key[9],p->nverify);
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[10],p->pv_limit);
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[11],p->epsilon);
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[12],p->radius);
   fprintf(fp,"#    %-9s = %g\n",opm_param_key[13],p->matching);
   fprintf(fp,"#    %-9s = %d\n",opm_param_key[14],p->niter);
}

void print_opm_param_file(FILE *fp, opm_param *p){
   if(p->mirror==TRUE) fprintf(fp,"%-9s  %s\n",opm_param_key[0],"ON");
   else fprintf(fp,"%-9s  %s\n",opm_param_key[0],"NULL");
   fprintf(fp,"%-9s  %g\n",opm_param_key[1],p->magnify);
   if(p->rotate==TRUE)fprintf(fp,"%-9s  %g\n",opm_param_key[2],p->angle);
   else fprintf(fp,"%-9s  %s\n",opm_param_key[2],"UNDEF");
   fprintf(fp,"%-9s  %g\n",opm_param_key[3],p->xcen);
   fprintf(fp,"%-9s  %g\n",opm_param_key[4],p->ycen);
   fprintf(fp,"%-9s  %g\n",opm_param_key[5],p->size);
   if(p->cut1==TRUE) fprintf(fp,"%-9s  %g\n",opm_param_key[6],p->mag_cut1);
   else fprintf(fp,"%-9s  %s\n",opm_param_key[6],"UNDEF");
   if(p->cut2==TRUE) fprintf(fp,"%-9s  %g\n",opm_param_key[7],p->mag_cut2);
   else fprintf(fp,"%-9s  %s\n",opm_param_key[7],"UNDEF");
   fprintf(fp,"%-9s  %d\n",opm_param_key[8],p->nset);
   fprintf(fp,"%-9s  %d\n",opm_param_key[9],p->nverify);
   fprintf(fp,"%-9s  %g\n",opm_param_key[10],p->pv_limit);
   fprintf(fp,"%-9s  %g\n",opm_param_key[11],p->epsilon);
   fprintf(fp,"%-9s  %g\n",opm_param_key[12],p->radius);
   fprintf(fp,"%-9s  %g\n",opm_param_key[13],p->matching);
   fprintf(fp,"%-9s  %d\n",opm_param_key[14],p->niter);
}

static void check_opm_param_null(opm_param *p, char *func){
   if(p==NULL){
      fprintf(stderr,"Null opm_param was given to %s\n",func);
      exit(1);
   }
}

