#include <unistd.h>
#include <string>
using namespace std;

#include "constantcolumn.h"
#include "simplefilter.h"
#include "calpontsystemcatalog.h"
#include "parsetree.h"
#include "simplecolumn.h"
#include "calpontselectexecutionplan.h"
using namespace execplan;

#include "cseputils.h"

namespace qfe
{
extern string DefaultSchema;

namespace utils
{
ConstantColumn* createConstCol(const string& valstr)
{
  ConstantColumn* cc = new ConstantColumn(valstr);
  cc->alias(valstr);
  return cc;
}

template <typename T>
ConstantColumn* createConstCol(const string& valstr, T val)
{
  ConstantColumn* cc = new ConstantColumn(valstr, val);
  cc->alias(valstr);
  return cc;
}

SimpleFilter* createSimpleFilter(boost::shared_ptr<CalpontSystemCatalog>& csc,
                                 const CalpontSystemCatalog::TableColName& tcn, const string& opstr,
                                 ConstantColumn* cc)
{
  SimpleFilter* lsf = new SimpleFilter();

  Operator* op = new Operator();
  op->data(opstr);
  CalpontSystemCatalog::ColType ccct;
  ccct = op->resultType();
  ccct.colDataType = cc->resultType().colDataType;
  op->operationType(ccct);

  SOP sop(op);
  lsf->op(sop);

  CalpontSystemCatalog::OID oid = csc->lookupOID(tcn);
  CalpontSystemCatalog::ColType ct = csc->colType(oid);

  SimpleColumn* sc = new SimpleColumn();
  sc->schemaName(tcn.schema);
  sc->tableName(tcn.table);
  sc->tableAlias(tcn.table);
  sc->columnName(tcn.column);
  sc->oid(oid);
  sc->resultType(ct);
  sc->alias(tcn.toString());

  lsf->lhs(sc);
  lsf->rhs(cc);

  return lsf;
}

void appendSimpleFilter(ParseTree*& ptree, SimpleFilter* filter)
{
  if (ptree->data() == 0)
  {
    // degenerate case, this filter goes at this node
    ptree->data(filter);
  }
  else if (ptree->right() == 0 && ptree->left() == 0)
  {
    // this will be the case when there is a single node in the tree
    // that contains a filter.  Here we want to make the root node an
    // 'and' operator, push the existing down to the lhs and make a
    // new node for the new filter
    ParseTree* newLhs = new ParseTree(ptree->data());
    ParseTree* newRhs = new ParseTree(filter);

    Operator* op = new Operator();
    op->data("and");

    ptree->data(op);
    ptree->left(newLhs);
    ptree->right(newRhs);
  }
  else
  {
    // this will be the case once we have a tree with an 'and' at the
    // root node, a filter in the lhs, and an arbitrary height tree
    // with the same properties on the rhs.  Because all operators
    // are guaranteed to be and for now we simply insert a new rhs
    // node and "push down" the existing tree
    Operator* op = new Operator();
    op->data("and");

    ParseTree* newRhs = new ParseTree(op);
    newRhs->left(new ParseTree(filter));
    newRhs->right(ptree->right());
    ptree->right(newRhs);
  }
}

void updateParseTree(boost::shared_ptr<execplan::CalpontSystemCatalog>& csc,
                     execplan::CalpontSelectExecutionPlan*& csep, execplan::SimpleColumn* sc,
                     const std::string& relop, pair<int, string> cval)
{
  execplan::ConstantColumn* cc = 0;

  if (cval.first == 0)
    cc = createConstCol(cval.second, static_cast<int64_t>(atoll(cval.second.c_str())));
  else
    cc = createConstCol(cval.second);

  if (sc->schemaName() == "infinidb_unknown" && !DefaultSchema.empty())
    sc->schemaName(DefaultSchema);

  execplan::SimpleFilter* sf = 0;
  sf = createSimpleFilter(csc, execplan::make_tcn(sc->schemaName(), sc->tableName(), sc->columnName()), relop,
                          cc);
  execplan::ParseTree* ptp = 0;
  ptp = csep->filters();

  if (ptp == 0)
    ptp = new execplan::ParseTree();

  appendSimpleFilter(ptp, sf);
  csep->filters(ptp);
}

// template instantiations
template ConstantColumn* createConstCol<int64_t>(const string& valstr, int64_t val);

}  // namespace utils
}  // namespace qfe
