//===========================================================================//
// This file is part of the DIP Solver Framework.                            //
//                                                                           //
// DIP is distributed under the Eclipse Public License as part of the        //
// COIN-OR repository (http://www.coin-or.org).                              //
//                                                                           //
// Author: Matthew Galati, SAS Institute Inc. (matthew.galati@sas.com)       //
//                                                                           //
// Conceptual Design: Matthew Galati, SAS Institute Inc.                     //
//                    Ted Ralphs, Lehigh University                          //
//                                                                           //
// Copyright (C) 2002-2013, Lehigh University, Matthew Galati, Ted Ralphs    //
// All Rights Reserved.                                                      //
//===========================================================================//

//===========================================================================//
#include "UtilMacros.h"
#include "UtilParameters.h"

#include <cassert>
#include <iostream>
using namespace std;

#define MAXLINE 1024
//===========================================================================//

//TODO: need a template for usage, need application to register this, defaults
//TODO: this is an ugly combination of old C code and C++, C++-ify it

// ------------------------------------------------------------------------- //
void UtilParameters::ScanCmdLineArgs(int&   argc,
                                     char* argv[])
{
   int i, j;

   //---
   //--- if there are no arguments, return
   //---
   if (argc == 0 || argv == NULL) {
      return;
   }

   j = 1;
   string paramFileName;

   for (i = 1; i < argc; i++) {
      //---
      //--- if "--param" is specified
      //---
      if (strcmp(argv[i], "--param") == 0) {
         //---
         //---   if a filename follows the flag
         //---           grab the filename
         //---
         if (((i + 1) < argc)
               && (argv[i + 1][0] != '-' || argv[i + 1][1] != '-')) {
            paramFileName = argv[++i];
         }

         continue;
      }

      argv[j++] = argv[i];
   }

   argc = j;
   //---
   //--- load the parameter file
   //---
   LoadParamFile(paramFileName);
   //---
   //--- enter the command line flags into the parameter map
   //---   format is --SECTION:PARAMETER value
   //---
   char cmdBuf[MAXLINE];

   for (i = 1; i < argc; i++) {
      if (argv[i][0] == '-' && argv[i][1] == '-') {
         string name (argv[i] + 2);
         string value("");
         strcpy(cmdBuf, argv[i] + 2);
         char* ptr1    = strtok(cmdBuf, ":");
         char* ptr2    = strtok(NULL, ":");
         char* section = NULL;
         char* parm    = NULL;

         if (ptr2 == NULL) {
            //---
            //--- section is NULL
            //---
            parm = ptr1;
         } else {
            //---
            //--- section is not NULL
            //---
            section = ptr1;
            parm    = ptr2;
         }

         if (((i + 1) < argc) && (argv[i + 1][0] != '-' || argv[i + 1][1] != '-')) {
            value = argv[++i];
         }

         Add(section, parm, value.c_str());
         continue;
      }
   }

   //STOP - what is the point of this?
   //
   //      -----   remove all the flags from the command line
   //
   j = 1;

   for (i = 1; i < argc; i++) {
      if (argv[i][0] == '-' && argv[i][1] == '-') {
         string name (argv[i] + 2);
         string value("");

         if (((i + 1) < argc)
               && (argv[i + 1][0] != '-' || argv[i + 1][1] != '-')) {
            value = argv[++i];
         }

         continue;
      }

      argv[j++] = argv[i];
   }

   argc = j;
}

// ------------------------------------------------------------------------- //
void UtilParameters::LoadParamFile(string& paramFileName)
{
   char     buf[MAXLINE];
   char*    ptr = NULL;
   string   curSection("");
   string   bufStr    ("");
   string   name      ("");
   string   value     ("");
   //---
   //--- open the stream pointer
   //---
   ifstream is(paramFileName.c_str());

   if (!is) {
      return;
   }

   //---
   //---   foreach line in the file
   //---     skip comments (#) and blank lines
   //---
   int lineNum = 0;

   while (!is.eof()) {
      is.getline(buf, sizeof(buf) - 1);
      lineNum++;
      ptr = strchr(buf, '#');

      if (ptr != NULL) {
         *ptr = '\0';
      }

      //TODO: move all to use string? do we need buf?
      bufStr = buf;
      bufStr = UtilStrTrim(bufStr);
      strcpy(buf, bufStr.c_str());

      if (strlen(buf) < 3) {
         continue;
      }

      //---
      //--- if line is '[section]'
      //---   create a new section
      //---
      if (buf[0] == '[') {
         ptr = strchr(buf + 1, ']');

         if (ptr == NULL) {
            cerr << "UtilParameters: syntax error on line "
                 << lineNum << " '" << buf << "'" << endl;
         }

         *ptr = '\0';
         curSection = buf + 1;
         continue;
      }

      //---
      //--- if line is 'name = value'
      //---   create a new name/value pair in the current section
      //---
      ptr = strchr(buf, '=');

      if (ptr != NULL) {
         *ptr++ = '\0';
      }

      name  = buf;
      value = "1";
      name  = UtilStrTrim(name);
      name  = UtilStrToLower(name);

      if (ptr != NULL) {
         //TODO: look into old code -> value=Expand(ptr)
         value = ptr;
         value = UtilStrTrim(value);
      }

      Add(curSection, name, value);
   }

   //---
   //--- close file stream
   //---
   is.close();
}

// ------------------------------------------------------------------------- //
void UtilParameters::Add(string& sSection,
                         string& sName,
                         string& sValue)
{
   UtilParam utilParam;
   string    keyname ("");
   keyname  = UtilStrToLower(UtilStrTrim(sSection));
   keyname += "@";
   keyname += UtilStrToLower(UtilStrTrim(sName));
   utilParam.paramName = UtilStrTrim(sValue);
   utilParam.isUsed    = false;
   //TODO: why doesn't insert override??
   // m_paramMap.insert(make_pair(keyname, utilParam));
   m_paramMap[keyname].paramName = utilParam.paramName;
   m_paramMap[keyname].isUsed    = utilParam.isUsed;
}

// ------------------------------------------------------------------------- //
void UtilParameters::Add(const char* section,
                         const char* name,
                         const char* value)
{
   UtilParam utilParam;
   string    keyname ("");
   string    sSection("");
   string    sName   (name);
   string    sValue  (value);

   if (section) {
      sSection = section;
      keyname  = UtilStrToLower(UtilStrTrim(sSection));
   }

   keyname += "@";
   keyname += UtilStrToLower(UtilStrTrim(sName));
   utilParam.paramName = UtilStrTrim(sValue);
   utilParam.isUsed    = false;
   //TODO: why doesn't insert override??
   // m_paramMap.insert(make_pair(keyname, utilParam));
   m_paramMap[keyname].paramName = utilParam.paramName;
   m_paramMap[keyname].isUsed    = utilParam.isUsed;
}

// ------------------------------------------------------------------------- //
UtilParam* UtilParameters::FindEntry(const char* section,
                                     const char* name)
{
   string    keyname ("");
   string    sSection("");
   string    sName   (name);

   if (section) {
      sSection = section;
      keyname  = UtilStrToLower(UtilStrTrim(sSection));
   }

   keyname += "@";
   keyname += UtilStrToLower(UtilStrTrim(sName));
   map<string, UtilParam>::iterator it;
   it = m_paramMap.find(keyname);

   if (it == m_paramMap.end()) {
      return NULL;
   } else {
      return &(it->second);
   }
}

// ------------------------------------------------------------------------- //
string* UtilParameters::Find(const char* section,
                             const char* name)
{
   UtilParam* utilParam = FindEntry(section, name);

   if (utilParam == NULL) {
      Add(section, name, "(undefined)");
      return NULL;
   } else if (utilParam->paramName == "(undefined)") {
      return NULL;
   } else {
      return &utilParam->paramName;
   }
}

// ------------------------------------------------------------------------- //
string UtilParameters::GetSetting(const char* name,
                                  const std::string  defaultValue,
                                  const char* section)
{
   //---
   //--- build the qualified name using the section
   //--- if the parameter is not found, return the default
   //--- else convert the string to the appropriate type
   //---
   string* pVal = Find(section, name);

   if (pVal == NULL) {
      return defaultValue;
   }

   return *pVal;
}




// ------------------------------------------------------------------------- //
string UtilParameters::GetSetting(const char* name,
                                  const char* defaultValue,
                                  const char* section)
{
   //---
   //--- build the qualified name using the section
   //--- if the parameter is not found, return the default
   //--- else convert the string to the appropriate type
   //---
   string* pVal = Find(section, name);

   if (pVal == NULL) {
      return string(defaultValue);
   }

   return *pVal;
}

// ------------------------------------------------------------------------- //
int UtilParameters::GetSetting(const char* name,
                               const int    defaultValue,
                               const char* section)
{
   //---
   //--- build the qualified name using the section
   //--- if the parameter is not found, return the default
   //--- else convert the string to the appropriate type
   //---
   string* pVal = Find(section, name);

   if (pVal == NULL) {
      return defaultValue;
   }

   int value = atoi(pVal->c_str());
   return value;
}

// ------------------------------------------------------------------------- //
bool UtilParameters::GetSetting(const char* name,
                                const bool   defaultValue,
                                const char* section)
{
   //---
   //--- build the qualified name using the section
   //--- if the parameter is not found, return the default
   //--- else convert the string to the appropriate type
   //---
   string* pVal = Find(section, name);

   if (pVal == NULL) {
      return defaultValue;
   }

   bool value = atoi(pVal->c_str()) != 0;
   return value;
}

// ------------------------------------------------------------------------- //
long UtilParameters::GetSetting(const char* name,
                                const long   defaultValue,
                                const char* section)
{
   //---
   //--- build the qualified name using the section
   //--- if the parameter is not found, return the default
   //--- else convert the string to the appropriate type
   //---
   string* pVal = Find(section, name);

   if (pVal == NULL) {
      return defaultValue;
   }

   long value = atol(pVal->c_str());
   return value;
}

// ------------------------------------------------------------------------- //
double UtilParameters::GetSetting(const char* name,
                                  const double defaultValue,
                                  const char* section)
{
   //---
   //--- build the qualified name using the section
   //--- if the parameter is not found, return the default
   //--- else convert the string to the appropriate type
   //---
   string* pVal = Find(section, name);

   if (pVal == NULL) {
      return defaultValue;
   }

   char*    pEnd  = NULL;
   double   value = strtod(pVal->c_str(), &pEnd);
   return value;
}

#if 0
// ------------------------------------------------------------------------- //
void UtilParameters::Dump(std::ostream& os) const
{
   //
   //    -----   foreach section
   //
   SECTION_MAP::const_iterator iSection;

   for (iSection =  m_sectionMap.begin();
         iSection != m_sectionMap.end(); iSection++) {
      //
      //        -----           output the section name
      //
      std::string sectionName = iSection->first;

      if (sectionName.length() > 0) {
         os << std::endl;
         os << "[" << sectionName << "]" << std::endl;
      }

      const NV_MAP& nv = iSection->second;
      //
      //        -----           foreach parameter in the section
      //
      NV_MAP::const_iterator iParm;

      for (iParm = nv.begin(); iParm != nv.end(); iParm++) {
         const std::string& name = iParm->first;
         const std::string& value = iParm->second.second;
         //
         //    -----                   output the name/value pair
         //
         os << "[" << iParm->second.first << "] "
            << name << "    = "
            << value << std::endl;
      }
   }
}
#endif
