// [VdB] Borrowed from polyglot

// option.c

// includes

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "util.h"
#include "book.h"
#include "option.h"

#define NNB { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }


option_list_t Option[1];

option_t DefaultOptions[] = {    
   // options

    { "Ponder",              "check","0","0",      "1"           , NULL,0,NNB,MODE_UCI}, 

  { "Log",              "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 
  { "LogFile",          "file","0","0",     "gnuchess.log", NULL,0,NNB,MODE_ALL}, 
  { "LogAppend",        "check","0","0",      "0"           , NULL,0,NNB,MODE_ALL}, 

  { "GameFile",          "file","0","0",     "gnuchess.pgn", NULL,0,NNB,MODE_ALL}, 
  
  { "Hash",              "spin","3","1536",     "48", NULL,0,NNB,MODE_UCI}, 

  //  { "Resign",           "check","0","0",   "0"     , NULL,0,NNB,true}, 

  { "OwnBook",             "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 
  { "BookRandom",       "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 

  { "BookFile",         "file","0","0",       "gnuchess.bin"     , NULL,0,NNB,MODE_ALL}, 

  { "DumpTree",         "check","0","0",      "0"           , NULL,0,NNB,MODE_ALL}, 
  { "DumpFile",         "file","0","0",       "dump.log"     , NULL,0,NNB,MODE_ALL}, 


  { "NullMovePruning",       "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 
  { "NullMoveVerification",  "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 

  { "FutilityPruning",       "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 
  { "LateMovePruning",       "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 

  { "AspirationSearch",       "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 
  { "AspirationWindow",     "spin","0","10000",   "25"        , NULL,0,NNB,MODE_ALL},
  { "MateDistancePruning",       "check","0","0",      "0"           , NULL,0,NNB,MODE_ALL}, 

  { "LMR",              "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL},
  { "AggressiveLMR",     "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 

  { "Razoring",     "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 

  { "UseTT",          "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 
  { "UseMateScores",          "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 
  { "UseTTinPV",          "check","0","0",      "0"           , NULL,0,NNB,MODE_ALL}, 
  { "UseMateScoresinPV",          "check","0","0",      "0"           , NULL,0,NNB,MODE_ALL}, 

  { "UseHistory",          "check","0","0",      "1"           , NULL,0,NNB,MODE_ALL}, 

  { "LazyEvalMargin1",     "spin","0","10000",   "300"        , NULL,0,NNB,MODE_ALL},
  { "LazyEvalMargin2",     "spin","0","10000",   "150"        , NULL,0,NNB,MODE_ALL},

  { "DeltaMargin",     "spin","0","10000",   "300"        , NULL,0,NNB,MODE_ALL},

  { "PanickTimeFactor",     "spin","1","16",   "4"        , NULL,0,NNB,MODE_ALL},

  { "SaveSettingsFile",     "file","0","0",       "gnuchess.ini", NULL,0,NNB,MODE_XBOARD}, 

    { "BookBuilderDepth",     "spin","0","1000",   "1000"        , NULL,0,NNB,MODE_NONE},
    { "BookBuilderLegacyFilter",     "check","0","0",   "0"        , NULL,0,NNB,MODE_NONE},


    // Buttons

  { "ClearHash",       "button","0","0",     "0"           , NULL,0,NNB,MODE_ALL},
  { "Save",             "save","0","0",       "0"           , NULL,0,NNB,MODE_XBOARD},

    // Sentinel
    
  { NULL,               NULL,"0","0",         NULL        , NULL,0,NNB,false},

};


// functions

int OptionValidate(const option_list_t *option) {
    if(option->option_nb<0 || option->option_nb>=OptionNb){
        return false;
    }
    return true;
}


void OptionInit(option_list_t *option) {

    int i;
    option_t *p=DefaultOptions;
    
    option->option_nb=0;
    option->iter=0;
    memset(option->options,0,sizeof(option->options));
    while(p){
        if(p->name){
            OptionInsert(option,p);
            p++;
        }else{
            break;
        }
    }
    for(i=0;i<option->option_nb;i++){
        option->options[i].value=strdup(option->options[i].default_);
    }
}




void OptionInsert(option_list_t *option, option_t *new_option){
    int i;
    option_t *opt;
    ASSERT(option!=NULL);
    ASSERT(new_option!=NULL);
    ASSERT(new_option->name!=NULL);
    opt=OptionFind(option,new_option->name);
    if(!opt){
        opt=&option->options[option->option_nb];
        option->option_nb++;
    }
    if(option->option_nb>=OptionNb){
	Output("tellusererror OptionInsert(): option list overflow");
	exit(1);
    }

    if(new_option->name)     my_string_set(&opt->name,     new_option->name);
    if(new_option->value)    my_string_set(&opt->value,    new_option->value);
    if(new_option->min)      my_string_set(&opt->min,      new_option->min);
    if(new_option->max)      my_string_set(&opt->max,      new_option->max);
    if(new_option->default_) my_string_set(&opt->default_, new_option->default_);
    if(new_option->type)     my_string_set(&opt->type,     new_option->type);
    opt->var_nb=new_option->var_nb;
    for(i=0;i<new_option->var_nb;i++){
        my_string_set(&opt->var[i], new_option->var[i]);
    }
    opt->mode=new_option->mode;
}


int OptionSet(option_list_t *option, 
                const char name[], 
                const char value[]) {

   option_t * opt;
   ASSERT(option!=NULL);
   ASSERT(name!=NULL);
   ASSERT(value!=NULL);

   opt = OptionFind(option,name);
   if (opt == NULL) return false;

   if(my_string_case_equal(opt->type,"check")){
      value=(my_string_equal(value,"1")||
	     my_string_case_equal(value,"true"))?"true":"false";
   }

   my_string_set(&opt->value,value);

   return true;
}


int OptionSetDefault(option_list_t *option,
                           const char name[], 
                           const char value[]) {

   option_t * opt;
   ASSERT(name!=NULL);
   ASSERT(value!=NULL);

   opt = OptionFind(option,name);
   if (opt == NULL) return false;

   if(my_string_case_equal(opt->type,"check")){
      value=(my_string_equal(value,"1")||
	     my_string_case_equal(value,"true"))?"true":"false";
   }

   my_string_set(&opt->default_,value);

   return true;
}


const char * OptionGet(option_list_t *option, const char name[]) {

   option_t * opt;

   ASSERT(name!=NULL);

   opt = OptionFind(option,name);
   if (opt == NULL){
     Output("tellusererror OptionGet(): unknown option \"%s\"\n",name);
     exit(1);
   } 
   return opt->value;
}


const char * OptionGetDefault(option_list_t *option, const char name[]) {

   option_t * opt;

   ASSERT(name!=NULL);

   opt = OptionFind(option,name);

   if (opt == NULL){
     Output("tellusererror OptionGetDefault(): unknown option \"%s\"",name);
     exit(1);
   } 

   return opt->default_;
}


int OptionGetBool(option_list_t *option, const char name[]) {

   const char * value;

   value = OptionGet(option,name);

   if (false) {
   } else if (my_string_case_equal(value,"true") || my_string_case_equal(value,"yes") || my_string_equal(value,"1")) {
      return true;
   } else if (my_string_case_equal(value,"false") || my_string_case_equal(value,"no") || my_string_equal(value,"0")) {
      return false;
   }

   ASSERT(false);

   return false;
}


double OptionGetDouble(option_list_t *option, const char name[]) {

   const char * value;

   value = OptionGet(option,name);

   return atof(value);
}

int OptionGetInt(option_list_t *option, const char name[]) {

   const char * value;

   value = OptionGet(option,name);

   return atoi(value);
}

const char * OptionGetString(option_list_t *option, const char name[]) {

   const char * value;

   value = OptionGet(option,name);

   return value;
}

option_t * OptionFind(option_list_t *option, const char name[]) {

   option_t * opt;
   int i;

   ASSERT(name!=NULL);
   for (i=0; i<option->option_nb; i++){
       opt=option->options+i;
       if (my_string_case_equal(opt->name,name)){
           return opt;
       }
   }
   
   return NULL;
}


void OptionStartIter(option_list_t *option){
    option->iter=0;
}


option_t * OptionNext(option_list_t *option){
    ASSERT(option->iter<=option->option_nb);
    if(option->iter==option->option_nb){
        return NULL;
    }
    return &option->options[option->iter++];
        
}

void OptionFree(option_t *option){
      int i;
      my_string_clear(&option->name);
      my_string_clear(&option->type);
      my_string_clear(&option->min);
      my_string_clear(&option->max);
      my_string_clear(&option->default_);
      my_string_clear(&option->value);
      for(i=0;i<option->var_nb;i++){
         my_string_clear(&option->var[i]);
      }
      option->var_nb=0;
}

void OptionClear(option_list_t *option){
    int i;
    for (i = 0; i < option->option_nb; i++) {
        OptionFree(option->options+i);
   }
   option->option_nb=0;
}


