/* 
 * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
 *
 * 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 St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "sql_parser_base.h"
#include "module_utils.h"
#include "grtdb/charset_utils.h"
#include "base/string_utilities.h"
#include <sstream>

#include "base/log.h"

using namespace grt;
using namespace bec;

DEFAULT_LOG_DOMAIN(DOMAIN_SQL_PARSER)

Sql_parser_base::Null_state_keeper::~Null_state_keeper()
{
  //_sql_parser->_grt= NULL;
  //_sql_parser->_grtm= NULL;
  //_sql_parser->_parse_error_cb.disconnect();
  _sql_parser->_active_obj= db_DatabaseObjectRef();
  _sql_parser->_messages_enabled= true;
  _sql_parser->_progress_state= 0.f;
  _sql_parser->_processed_obj_count= 0;
  _sql_parser->_warn_count= 0;
  _sql_parser->_err_count= 0;
  _sql_parser->_sql_script_preamble= std::string();
  _sql_parser->_sql_statement= std::string();
  _sql_parser->_stopped= false;
}
#define NULL_STATE_KEEPER Null_state_keeper _nsk(this);


Sql_parser_base::Sql_parser_base(grt::GRT *grt)
:
EOL(base::EolHelpers::eol(base::EolHelpers::eol_lf)),
_is_ast_generation_enabled(true),
_grt(grt),
_grtm(GRTManager::get_instance_for(_grt))
{
  {
    NULL_STATE_KEEPER // reset all members to null-values
  }

  grt::DictRef options= grt::DictRef::cast_from(_grt->get("/wb/options/options"));
  _case_sensitive_identifiers= options.is_valid() ? (options.get_int("SqlIdentifiersCS", 1) != 0) : true;
}


void Sql_parser_base::set_options(const grt::DictRef &options)
{
}


void Sql_parser_base::messages_enabled(bool value)
{
  _messages_enabled= value;
}


bool Sql_parser_base::messages_enabled()
{
  return _messages_enabled;
}


std::string Sql_parser_base::normalize_identifier_case(const std::string &ident)
{
  return _case_sensitive_identifiers ? ident : base::toupper(ident);
}


void Sql_parser_base::add_log_message(const std::string &text, int entry_type)
{
  // Keep it that way (don't always write to log file). Messages are usually disabled during
  // syntax checks and the like. Simple SQL errors shouldn't go into the log file.
  if (_messages_enabled)
  {
    bool send_to_frontend = (_grt != NULL) && (!_grtm->in_main_thread());

    // TODO: The entry types needs a review. It should be streamlined to the logger levels.
    switch (entry_type)
    {
    case 0:
      {
        log_debug2("%s", (text + "\n").c_str());

        if (send_to_frontend)
          _grt->send_info(text);
        break;
      }
    case 1:
      {
        ++_warn_count;
        log_debug("%s", (text + "\n").c_str());

        if (send_to_frontend)
          _grt->send_warning(text);
        break;
      }
    case 2:
      {
        log_debug("%s", (text + "\n").c_str());

        if (send_to_frontend)
          _grt->send_error(text);
        break;
      }
    default:
      log_debug3("%s", (text + "\n").c_str());
      break;
    }
  }
}

void Sql_parser_base::report_sql_error(int lineno, bool calc_abs_lineno, int err_tok_line_pos, int err_tok_len, const std::string &err_msg, int entry_type, std::string resolution)
{
  ++_err_count;

  if (calc_abs_lineno)
  {
    // count statement line count
    int stmt_lc= base::EolHelpers::count_lines(_sql_statement);
    int sql_script_preamble_line_count= base::EolHelpers::count_lines(_sql_script_preamble);
    lineno= total_line_count() - stmt_lc - sql_script_preamble_line_count + lineno;
  }

  if (_parse_error_cb)
    _parse_error_cb(lineno, err_tok_line_pos, err_tok_len, err_msg);

  std::ostringstream oss;
  if (_active_obj.is_valid())
    oss << _active_obj.get_metaclass()->get_attribute("caption") << " " << *_active_obj->name() << ". ";
  oss << "Line " << lineno << ": " << err_msg << "." << (resolution.empty() ? "" : " ") << resolution;
  add_log_message(oss.str(), entry_type);
}


void Sql_parser_base::step_progress(const std::string &text)
{
  if (!_messages_enabled)
    return;

  //!cycling progress state for now. statement count estimation is needed.
  _progress_state= (float)(div((int)(_progress_state*10) + 1, 10).rem) / 10;

  _grt->send_progress(_progress_state, _("Processing object"), text);
}


void Sql_parser_base::set_progress_state(float state, const std::string &text)
{
  if (!_messages_enabled)
    return;
  _grt->send_progress(state, text);
}


const std::string & Sql_parser_base::sql_statement()
{
  return _sql_statement;
}


void Sql_parser_base::parse_error_cb(Parse_error_cb cb)
{
  _parse_error_cb= cb;
}


Sql_parser_base::Parse_error_cb & Sql_parser_base::parse_error_cb()
{
  return _parse_error_cb;
}


void Sql_parser_base::do_report_sql_statement_border(int begin_lineno, int begin_line_pos, int end_lineno, int end_line_pos)
{
  // calculate lineno shift to get absolute line numbers
  int lineno_shift;
  {
    int stmt_lc= base::EolHelpers::count_lines(_sql_statement);
    int sql_script_preamble_line_count= base::EolHelpers::count_lines(_sql_script_preamble);
    lineno_shift= total_line_count() - stmt_lc - sql_script_preamble_line_count;
  }
  if (report_sql_statement_border)
    report_sql_statement_border(begin_lineno + lineno_shift, begin_line_pos, end_lineno + lineno_shift, end_line_pos);
}
