/* LICENSE:
  =========================================================================
    CMPack'04 Source Code Release for OPEN-R SDK 1.1.5-r2 for ERS7
    Copyright (C) 2004 Multirobot Lab [Project Head: Manuela Veloso]
    School of Computer Science, Carnegie Mellon University
    All rights reserved.
  ========================================================================= */

#include "../headers/system_config.h"

#include <ctype.h>
#include <string.h>

#include <algorithm>
#include <iostream>
#include <utility>

using namespace std;

#include "../headers/Config.h"
#include "../headers/Dictionary.h"
#include "../headers/FileSystem.h"

Dictionary::Dictionary() {
  numCharsInBuf = 0;
}

Dictionary::~Dictionary() {
  DictionaryMap::iterator map_iter;

  for(map_iter=dict_map.begin(); map_iter!=dict_map.end(); map_iter++) {
    delete[] (*map_iter).first;
    delete[] (*map_iter).second;
  }

  dict_map.erase(dict_map.begin(),dict_map.end());
}

void
Dictionary::read(const char *filename) {
  fd=BFS::open(filename,BFS_O_RDONLY);

  if(fd==-1) {
    cout << "problem reading file " << filename << endl;
    return;
  }
  
  while(readValueAssignment())
    ;
  
  BFS::close(fd);
}

bool
Dictionary::readValueAssignment() {
  char *var_name;
  char *value;
  bool success;

  success=readStringUntil("=;",var_name);
  if(!success)
    return false;
  if(var_name[strlen(var_name)-1]!='=')
    return false;
  var_name[strlen(var_name)-1]='\x00';
  trimString(var_name);

  success=readStringUntil(";",value);
  if(!success)
    return false;
  if(value[strlen(value)-1]!=';')
    return false;
  value[strlen(value)-1]='\x00';
  trimString(value);
  if(value[0]=='"' && value[strlen(value)-1]=='"') {
    int length=strlen(value);
    for(int idx=1; idx<length-1; idx++)
      value[idx-1]=value[idx];
    value[length-2]='\x00';
  }

  dict_map.insert(pair<const char * const,const char *>(var_name,value));
  return true;
}

bool
Dictionary::readStringUntil(const char *stop_chars,char *&val) {
  bool stop=false;
  bool first_time=true;

  while(!stop) {
    int start_idx;
    int num_chars_to_consider;
    if(first_time) {
      start_idx=0;
      first_time=false;
      num_chars_to_consider=numCharsInBuf;
    }
    else {
      int num_read;
      num_read=BFS::read(fd,&buf[numCharsInBuf],min(132,BufSize-numCharsInBuf-1));
      if(num_read==0)
        return false;
      start_idx=numCharsInBuf;
      numCharsInBuf+=num_read;
      num_chars_to_consider=num_read;
    }
    buf[numCharsInBuf]=*stop_chars;
    int num_nondelim_chars;
    int num_chars_used;
    num_nondelim_chars=findNextDelimeter(&buf[0],numCharsInBuf+1,stop_chars,num_chars_used);
    if(num_chars_used!=numCharsInBuf+1) {
      // found delimeter
      int num_chars=num_nondelim_chars+1;
      char *str=new char[num_chars+1];
      strNCpyNonComment(str,buf,num_chars);
      str[num_chars]='\x00';
      val=str;
      for(int char_idx=num_chars_used; char_idx<numCharsInBuf; char_idx++)
        buf[char_idx-num_chars_used]=buf[char_idx];
      numCharsInBuf-=num_chars_used;
      stop=true;

      //cout << "read string '" << str << "'" << endl;
    }
  }

  return true;
}

bool
Dictionary::getValueInt(const char *key, int &value) {
  const char *str_value=NULL;
  bool success=false;

  success=getValueString(key,str_value);
  if(!success)
    return false;

  value=atoi(str_value);
  return true;
}

bool
Dictionary::getValueDouble(const char *key, double &value) {
  const char *str_value=NULL;
  bool success=false;

  success=getValueString(key,str_value);
  if(!success)
    return false;

  value=atof(str_value);
  return true;
}

bool
Dictionary::getValueString(const char *key, const char *&value) {
  DictionaryMap::iterator iter;

  iter=dict_map.find(key);
  if(iter!=dict_map.end()) {
    value=(*iter).second;
    return true;
  }
  else {
    return false;
  }
}

void
Dictionary::trimString(char *&str) {
  int num_lead_white;
  int str_len;

  str_len=strlen(str);

  num_lead_white=strspn(str," \t\n\r");

  int start_trail_white=num_lead_white;
  for(int idx=num_lead_white; idx<str_len; idx++) {
    char ch;
    ch=str[idx];
    if(!isspace(ch))
      start_trail_white=idx-num_lead_white+1;

    str[idx-num_lead_white]=ch;
  }

  str[start_trail_white]='\x00';
}

int
Dictionary::findNextDelimeter(const char *str, int num_chars, const char *stop_chars, int &num_chars_used) {
  int src_idx=0;
  int num_chars_found=0;
  //bool hit_slash=false;
  bool in_cpp_comment=false;
  bool delimeter_found=false;
  int cpp_comment_chars_found=0;

  do {
    char ch;
    ch=str[src_idx++];
    switch(ch) {
    case '/':
      if(!in_cpp_comment)
        cpp_comment_chars_found++;
      if(cpp_comment_chars_found==2) {
        in_cpp_comment=true;
        cpp_comment_chars_found=0;
      }
      break;
    case '\n':
      in_cpp_comment=false;
      // fall through
    default:
      if(cpp_comment_chars_found > 0)
        num_chars_found+=cpp_comment_chars_found;
      cpp_comment_chars_found=0;
      break;
    }

    if(!in_cpp_comment && cpp_comment_chars_found==0) {
      num_chars_found++;
      if(strchr(stop_chars,ch)!=NULL) {
        delimeter_found=true;
      }
    }
  } while(!delimeter_found && src_idx<num_chars);

  num_chars_used=src_idx;
  
  return num_chars_found-1;
}

char *
Dictionary::strNCpyNonComment(char *dst, const char *src, int num_chars) {
  int dst_idx=0;
  int src_idx=0;

  bool in_cpp_comment=false;
  int cpp_comment_chars_found=0;

  while(dst_idx<num_chars) {
    char ch;
    ch=src[src_idx++];
    switch(ch) {
    case '/':
      if(!in_cpp_comment)
        cpp_comment_chars_found++;
      if(cpp_comment_chars_found==2) {
        in_cpp_comment=true;
        cpp_comment_chars_found=0;
      }
      break;
    case '\n':
      in_cpp_comment=false;
      // fall through
    default:
      if(cpp_comment_chars_found > 0) {
        strncpy(&dst[dst_idx],"//",cpp_comment_chars_found);
        dst_idx+=cpp_comment_chars_found;
      }
      cpp_comment_chars_found=0;
      break;
    }

    if(!in_cpp_comment && cpp_comment_chars_found==0) {
      dst[dst_idx++]=ch;
    }
  }

  return dst;
}
