/* Copyright (C) 2014 InfiniDB, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/*******************************************************************************
 * $Id: inputmgr.cpp 2258 2013-05-08 17:33:40Z dcathey $
 *
 *******************************************************************************/

#include "inputmgr.h"

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cerrno>

#include "calpontsystemcatalog.h"
#include "we_define.h"

using namespace std;
using namespace WriteEngine;
using namespace execplan;

namespace bulkloadxml
{
//------------------------------------------------------------------------------
// InputMgr constructor
//------------------------------------------------------------------------------
InputMgr::InputMgr(const string& job)
{
  fParms[JOBID] = job;  // add or override default value
}

//------------------------------------------------------------------------------
// InputMgr destructor
//------------------------------------------------------------------------------
/* virtual */
InputMgr::~InputMgr()
{
}

//------------------------------------------------------------------------------
// printUsage
//------------------------------------------------------------------------------
void InputMgr::printUsage()
{
  //@bug 391
  cerr << "Usage: "
       << "colxml [options] dbName" << endl
       << endl;
  cerr << "Options: " << endl;
  cerr << "   -d Delimiter (default '|')\n";
  cerr << "   -e Maximum allowable errors (per table)\n";
  cerr << "   -h Print this message\n";
  cerr << "   -j Job id (numeric)\n";
  cerr << "   -l Load file name\n";
  cerr << "   -n \"name in quotes\"\n";
  cerr << "   -p Path for XML job description file that is generated\n";
  cerr << "   -s \"Description in quotes\"\n";
  cerr << "   -t Table name\n";
  cerr << "   -u User\n";
  cerr << "   -r Number of read buffers\n";
  cerr << "   -c Application read buffer size (in bytes)\n";
  cerr << "   -w I/O library buffer size (in bytes), used to read files\n";
  cerr << "   -x Extension of file name (default \".tbl\")\n";
  cerr << "   -E EnclosedByChar (if data has enclosed values)\n";
  cerr << "   -C EscapeChar\n";
  cerr << "   -b Debug level (1-3)\n\n";
  cerr << "   dbName - Required parm specifying the name of the database;" << endl
       << "            all others are optional\n\n";
  cerr << "Example:\n\t"
       << "colxml -t lineitem -j 123 tpch\n";
  //    exit(0);
}

//------------------------------------------------------------------------------
// Provide input to this class through argc/argv interface.
//------------------------------------------------------------------------------
bool InputMgr::input(int argc, char** argv)
{
  std::vector<std::string> l_tableList;
  int ch;

  while ((ch = getopt(argc, argv, "b:d:s:j:l:e:n:p:t:u:r:c:w:x:hE:C:")) != EOF)
  {
    switch (ch)
    {
      case 't':
      {
        l_tableList.push_back(optarg);
        break;
      }

      case 'l':
      {
        fLoadNames.push_back(optarg);
        break;
      }

      case 'r':
      case 'c':
      case 'w':
      case 'j':
      case 'b':
      case 'e':
      {
        if (verifyArgument(optarg) < 0)
        {
          cout << "Argument associated with option -" << (char)ch << " is not a number." << endl;
          return false;
        }

        errno = 0;
        long lValue = strtol(optarg, 0, 10);

        if (errno != 0)
        {
          cout << "Option -" << (char)ch << " is invalid or out of range" << endl;
          return false;
        }

        // Limit to INT_MAX because we eventually store in an "int"
        if (((ch == 'e') && (lValue < 0)) || ((ch != 'e') && (lValue < 1)) || (lValue > INT_MAX))
        {
          cout << "Option -" << (char)ch << " is invalid or out of range." << endl;
          return false;
        }
      }
      /* FALLTHRU */
      case 'd':
      case 's':
      case 'f':
      case 'n':
      case 'p':
      case 'u':
      case 'x':
      case 'E':
      case 'C':
      {
        char l_option[4];
        snprintf(l_option, sizeof(l_option), "-%c", ch);
        ParmList::iterator p = fParms.find(l_option);

        if (fParms.end() != p)
          p->second = optarg;

        break;
      }

      case 'h':
      {
        printUsage();
        return false;
      }

      default:
      {
        cout << "Try '" << argv[0] << " -h' for more information." << endl;
        return false;
      }
    }
  }

  if (optind < argc)
  {
    fSchema = argv[optind++];

    if (optind < argc)
    {
      cout << "Extraneous arguments are ignored." << endl;
    }
  }
  else
  {
    cout << "Schema value is required." << endl;
    cout << "Try '" << argv[0] << " -h' for more information." << endl;
    return false;
  }

  for (unsigned ndx = 0; ndx < l_tableList.size(); ndx++)
  {
    fTables.push_back(execplan::CalpontSystemCatalog::TableName(fSchema, l_tableList[ndx]));
  }

  return true;
}

//------------------------------------------------------------------------------
// Get list of tables for fSchema, and store the list in fTables.
//------------------------------------------------------------------------------
bool InputMgr::loadCatalogTables()
{
  boost::shared_ptr<execplan::CalpontSystemCatalog> systemCatPtr =
      execplan::CalpontSystemCatalog::makeCalpontSystemCatalog(BULK_SYSCAT_SESSION_ID);
  systemCatPtr->identity(execplan::CalpontSystemCatalog::EC);
  const vector<pair<CalpontSystemCatalog::OID, CalpontSystemCatalog::TableName> > tables =
      systemCatPtr->getTables(fSchema);

  for (vector<pair<CalpontSystemCatalog::OID, CalpontSystemCatalog::TableName> >::const_iterator it =
           tables.begin();
       it != tables.end(); ++it)
  {
    fTables.push_back((*it).second);
  }

  return (!fTables.empty());
}

//------------------------------------------------------------------------------
// Verify that string argument is numeric
//------------------------------------------------------------------------------
int InputMgr::verifyArgument(char* arg)
{
  while (*arg != 0)
  {
    if (!isdigit(*arg))
    {
      return -1;
    }

    arg++;
  }

  return 0;
}

//------------------------------------------------------------------------------
// print contents to specified stream
//------------------------------------------------------------------------------
/* virtual */
void InputMgr::print(std::ostream& os) const
{
  os << *this;
}

//------------------------------------------------------------------------------
// operator<< for InputMgr
//------------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& os, const InputMgr& m)
{
  os << "\n\tSchema:\t" << m.fSchema << "\n\t"
     << "Tables:\t";

  for (InputMgr::TableList::const_iterator i = m.fTables.begin(); i != m.fTables.end(); ++i)
    os << i->table << "  ";

  os << "\n\t"
     << "Load Files: ";

  for (InputMgr::LoadNames::const_iterator i = m.fLoadNames.begin(); i != m.fLoadNames.end(); ++i)
    os << *i << "  ";

  for (InputMgr::ParmList::const_iterator i = m.fParms.begin(); i != m.fParms.end(); ++i)
  {
    // Don't report the enclosedBy if not enabled by user
    if ((i->first == XMLGenData::ENCLOSED_BY_CHAR) && (i->second.length() < 1))
      continue;

    os << "\n\t" << i->first << "\t" << i->second;
  }

  os << std::endl;
  return os;
}

}  // namespace bulkloadxml
