// [VdB] Borrowed from Polyglot but written by me

// includes

#include <stdio.h>
#include <string.h>
#include <time.h>
#include "ini.h"
#include "errno.h"
#include "util.h"
#include "option.h"
#include "log.h"

// types

typedef enum {
    START               =0,
    SECTION_NAME        =1,
    NAME                =2,
    NAME_SPACE          =3,
    START_VALUE         =4,
    VALUE               =5,
    VALUE_SPACE         =6,
    QUOTE_SPACE         =7,
    FINISHED            =8,
} parse_state_t;

// functions

// IniLineParse()

line_type_t IniLineParse(const char *line,
                                  char *section,
                                  char *name,
                                  char *value){
    int i;
    parse_state_t state=START;
    int name_index=0;
    int value_index=0;
    int section_index=0;
    int index=0;
    char c;
    int type=SYNTAX_ERROR;
    int spaces=0;
    int outer_quote=false;
    while(state!=FINISHED){
        c=line[index++];
        switch(state){
            case START:
                if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
                    type=EMPTY_LINE;
                    state=FINISHED;
                }else if(c=='['){
                    state=SECTION_NAME;
                }else if(c!=' '){
                    name[name_index++]=c;
                    state=NAME;
                }
                goto next;
                break;
            case NAME:
                if(c=='='){
                    state=START_VALUE;
                }else if(c==' '){
                    state=NAME_SPACE;
                    spaces=1;
                }else if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
                    type=SYNTAX_ERROR;
                    state=FINISHED;
                }else{
                    name[name_index++]=c;
                }
                goto next;
                break;    // we don't get here
            case NAME_SPACE:
                if(c==' '){
                    spaces++;
                }else if(c=='='){
                    state=START_VALUE;
                }else if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
                    type=SYNTAX_ERROR;
                    state=FINISHED;
                }else{
                    for(i=0;i<spaces;i++){
                        name[name_index++]=' ';
                    }
                    spaces=0;
                    name[name_index++]=c;
                    state=NAME;
                }
                goto next;
                break;    // we don't get here
            case START_VALUE:
                if(c=='"'){
                    outer_quote=true;
                    spaces=0;
                    state=VALUE_SPACE;
                }else if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
                    type=EMPTY_VALUE;
                    state=FINISHED;
                }else if(c!=' '){
                    value[value_index++]=c;
                    state=VALUE;
                }
                goto next;
                break;    // we don't get here
            case VALUE:
                if(c=='"'){
                    state=QUOTE_SPACE;
                    spaces=0;
                }else if(c==' '){
                    state=VALUE_SPACE;
                    spaces=1;
                }else if((c=='\r' || c=='\n' || c==';' || c=='#'||(c=='\0'))&&
                         !outer_quote){
                    type=NAME_VALUE;
                    state=FINISHED;
                }else{
                    value[value_index++]=c;
                }
                goto next;
                break;    // we don't get here
            case VALUE_SPACE:
                if(c==' '){
                    spaces++;
                }else if((c=='\r' || c=='\n' || c==';' || c=='#'||(c=='\0'))&&
                         !outer_quote){
                    type=NAME_VALUE;
                    state=FINISHED;
                }else{
                    for(i=0;i<spaces;i++){
                        value[value_index++]=' ';
                    }
                    spaces=0;
                    if(c!='"'){
                        value[value_index++]=c;
                        state=VALUE;
                    }else{
                        state=QUOTE_SPACE;
                    }
                }
                goto next;
                break;    // we don't get here
            case QUOTE_SPACE:
                if(c==' '){
                    spaces++;
                }else if(c=='\r' || c=='\n' || c==';' || c=='#'||(c=='\0')){
                    type=NAME_VALUE;
                    state=FINISHED;
                }else{
                    value[value_index++]='"';
                    for(i=0;i<spaces;i++){
                        value[value_index++]=' ';
                    }
                    spaces=0;
                    if(c!='"'){
                        value[value_index++]=c;
                        state=VALUE;
                    }else{
                        state=QUOTE_SPACE;
                    }
                }
                goto next;
                break;    // we don't get here
            case SECTION_NAME:
                if(c==']'){
                    type=SECTION;
                    state=FINISHED;
                }else{
                    section[section_index++]=c;
                }
                goto next;
                break;    // we don't get here
            default:
                break;
        }
      next:        if(!c) break;
    }
    section[section_index]='\0';
    name[name_index]='\0';
    value[value_index]='\0';
    return type;
}

// IniInit()

void IniInit(ini_t *ini){
    memset(ini,0,sizeof(ini_t));
}

// IniClear()

void IniClear(ini_t *ini){
    int i;
    ini_entry_t * entry;
    for(i=0; i< ini->index; i++){
        entry=ini->entries+i;
        if(entry->name!=NULL){
            my_string_clear(&entry->name);
        }
        if(entry->value!=NULL){
            my_string_clear(&entry->value);
        }
        if(entry->comment!=NULL){
            my_string_clear(&entry->comment);
        }
    }
    ini->index=0;
}

// IniCopy()

void IniCopy(ini_t *dst, ini_t *src){
  int i;
  dst->index=src->index;
  dst->iter=src->iter;
  for(i=0;i<src->index;i++){
    my_string_set(&dst->entries[i].section,src->entries[i].section);
    my_string_set(&dst->entries[i].name,src->entries[i].name);
    my_string_set(&dst->entries[i].value,src->entries[i].value);
  }
}

// IniFind()

ini_entry_t *IniFind(ini_t *ini, const char *section, const char* name){
    int i;
    ini_entry_t * entry;
    for(i=0; i< ini->index; i++){
        entry=ini->entries+i;
        if(my_string_case_equal(entry->name,name) &&
           my_string_case_equal(entry->section,section)){
            return entry;
        }
    }
    return NULL;
}

// IniInsert()

void IniInsert(ini_t *ini, ini_entry_t *entry){
    ini_entry_t * ini_entry;
    ini_entry=IniFind(ini,entry->section,entry->name);
    if(ini_entry!=NULL){
        my_string_set(&ini_entry->value,entry->value);
    }else{
        if(ini->index>=IniEntriesNb){
            Output("tellusererror IniInsert(): too many options\n");
        }
        ini_entry=ini->entries+(ini->index++);
        my_string_set(&ini_entry->value,entry->value);
        my_string_set(&ini_entry->name,entry->name);
        my_string_set(&ini_entry->section,entry->section);
    }
}

// IniInsertEx()

void IniInsertEx(ini_t *ini,
                   const char *section,
                   const char *name,
                   const char *value){
    ini_entry_t entry[1];
    memset(entry,0,sizeof(ini_entry_t));
    my_string_set(&entry->section,section);
    my_string_set(&entry->name,name);
    my_string_set(&entry->value,value);
    IniInsert(ini,entry);
    my_string_clear(&entry->section);
    my_string_clear(&entry->name);
    my_string_clear(&entry->value);
}

// IniParse()

int IniParse(ini_t *ini, const char *filename){
    char name[MAXSTR];
    char value[MAXSTR];
    char section[MAXSTR];
    char line[MAXSTR];
    ini_entry_t entry[1];
    line_type_t result;
    const char *current_section=NULL;
    FILE *f;
    int line_nr=0;
    my_string_set(&current_section,"Main");
    memset(entry,0,sizeof(ini_entry_t));
    f=fopen(filename,"r");
    if(!f) {
        return false;
    }
    while(true){
        if(!fgets(line,MAXSTR,f)){
            break;
        }
        line_nr++;
        result=IniLineParse(line,section,name,value);
        if(result==SECTION){
            my_string_set(&current_section,section);
        }else if(result==NAME_VALUE){
            IniInsertEx(ini,current_section,name,value);
        }else if(result==SYNTAX_ERROR){
            Output("tellusererror ini_parse(): Syntax error in \"%s\": line %d\n",
                     filename,
                     line_nr);
	    return false;
        }else {  // empty line
        }
        
    }
    fclose(f);
    return true;

}

// IniShow()

void IniShow(ini_t *ini){
    int i;
    for(i=0;i<ini->index;i++){
        OutputConsole("OPTION: [%s] %s=\"%s\"\n",
               (ini->entries)[i].section,
               (ini->entries)[i].name,
               (ini->entries)[i].value);
    }
}

// IniStartIter()

void IniStartIter(ini_t *ini){
    ini->iter=0;
}

// IniNext()

ini_entry_t * IniNext(ini_t *ini){
    ASSERT(ini->iter<=ini->index);
    if(ini->iter==ini->index){
        return NULL;
    }
    return &ini->entries[ini->iter++];
}


void IniFromOption(ini_t *ini, option_list_t *option){
    option_t *opt;
    OptionStartIter(option);
    while((opt=OptionNext(option))){
        if(/*!my_string_equal(opt->value,opt->default_)&&*/ !IS_BUTTON(opt->type)){
            IniInsertEx(ini,"gnuchess",opt->name,opt->value);
        }
    }
}

void IniSave(ini_t *ini, const char *filename){
    // TODO Make this generic.
    FILE *f;
    char tmp[MAXSTR];
    ini_entry_t *entry;
    time_t t=time(NULL);
    f=fopen(filename,"w");
    if(!f){
      Output("tellusererror IniSave(): %s: %s.\n",filename,strerror(errno));
      Log("IniSave(): %s: %s.\n",filename,strerror(errno));
      return;
    }
    fprintf(f,"; Created: %s\n",ctime(&t));
    fprintf(f,"[gnuchess]\n");
    IniStartIter(ini);
    while((entry=IniNext(ini))){
	snprintf(tmp,sizeof(tmp),"%s=%s\n",
		 entry->name,
		 entry->value);
	tmp[sizeof(tmp)-1]='\0';
	fprintf(f,"%s",tmp);
    }
}
