/************************************************************************************
   Copyright (C) 2020 MariaDB Corporation AB

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not see <http://www.gnu.org/licenses>
   or write to the Free Software Foundation, Inc.,
   51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/


#include "PreparedStatement.h"
#include "class/Protocol.h"
#include "class/Results.h"
#include "ResultSet.h"
#include "class/ResultSetMetaData.h"

namespace mariadb
{
  /*const*/ char paramIndicatorNone= '\0', paramIndicatorNull= STMT_INDICATOR_NULL, paramIndicatorNts= STMT_INDICATOR_NTS, paramIndicatorIgnore= STMT_INDICATOR_IGNORE,
    paramIndicatorIgnoreRow= STMT_INDICATOR_IGNORE_ROW;
  /**
   * Constructor. Base class that permits setting parameters for client and server PrepareStatement.
   *
   * @param connection current connection
   * @param resultSetScrollType one of the following <code>ResultSet</code> constants: <code>
   *     ResultSet.SQL_CURSOR_FORWARD_ONLY</code>, <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
   *     <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
   * @param resultSetConcurrency one of the following <code>ResultSet</code> constants: <code>
   *     ResultSet.CONCUR_READ_ONLY</code> or <code>ResultSet.CONCUR_UPDATABLE</code>
   */
  PreparedStatement::PreparedStatement(
      Protocol* handle,
      int32_t resultSetScrollType
      )
    : guard(handle)
    , resultSetScrollType(resultSetScrollType)
    , batchRes(0)
  {
  }


  PreparedStatement::PreparedStatement(
    Protocol* handle,
    const SQLString& _sql,
    int32_t resultSetScrollType)
    : guard(handle)
    , sql(&_sql)
    , resultSetScrollType(resultSetScrollType)
    , batchRes(0)
  {}


  PreparedStatement::~PreparedStatement()
  {
  }

  /**
   * Check if statement is closed, and throw exception if so.
   *
   * @throws SQLException if statement close
   */
  void PreparedStatement::checkClose() {
    if (closed) {
      throw 1;
    }
  }

  void PreparedStatement::markClosed()
  {
    closed= true;
    guard= nullptr;
  }

  void PreparedStatement::validateParamset(std::size_t paramCount)
  {
  }

  /**
    * Permit to retrieve current connection thread id, or -1 if unknown.
    *
    * @return current connection thread id.
    */
  int64_t PreparedStatement::getServerThreadId()
  {
    return guard->getServerThreadId();
  }

  void PreparedStatement::clearBatch()
  {
    batchArraySize= 0;
  }

  bool PreparedStatement::execute()
  {
    return executeInternal(getFetchSize());
  }

  /**
    * Executes the SQL query in this <code>PreparedStatement</code> object and returns the <code>
    * ResultSet</code> object generated by the query.
    *
    * @return a <code>ResultSet</code> object that contains the data produced by the query; never
    *     <code>null</code>
    * @throws SQLException if a database access error occurs; this method is called on a closed
    *     <code>PreparedStatement</code> or the SQL statement does not return a <code>ResultSet
    *     </code> object
    */
  ResultSet* PreparedStatement::executeQuery()
  {
    if (execute()) {
      return getResultSet();
    }
    return ResultSet::createEmptyResultSet();
  }


  ResultSet* PreparedStatement::getResultSet()
  {
    return results->releaseResultSet();
  }


  void PreparedStatement::setBatchSize(int32_t batchSize)
  {
    batchArraySize= batchSize;
  }


  bool PreparedStatement::getMoreResults()
  {
    return results && results->getMoreResults(false, guard);
  }

  bool PreparedStatement::hasMoreResults()
  {
    return results && results->hasMoreResults(guard);
  }


  ResultSetMetaData* PreparedStatement::getEarlyMetaData()
  {
    return getPrepareResult()->getEarlyMetaData();
  }


  std::size_t PreparedStatement::getParamCount()
  {
    return getPrepareResult()->getParamCount();
  }

  const mariadb::Longs& PreparedStatement::executeBatch()
  {
    checkClose();
    batchRes.wrap(nullptr, 0);
    if (batchArraySize < 1) {
      return batchRes;
    }
    executeBatchInternal(batchArraySize);
    return batchRes.wrap(results->getCmdInformation()->getUpdateCounts());
  }


  //void PreparedStatement::validateParamset(std::size_t paramCount)
  //{
  //  // valid parameters
  //  for (std::size_t i= 0; i < paramCount; i++) {
  //    if (i > parameters.size() || !parameters[i]) {
  //      getLogger()->error("Parameter at position " + std::to_string(i + 1) + " is not set");
  //      exceptionFactory->raiseStatementError(connection, this)->create("Parameter at position "
  //        + std::to_string(i + 1) + " is not set", "07004").Throw();
  //    }
  //  }
  //}

  ///**
  //  * Clears the current parameter values immediately.
  //  *
  //  * <p>In general, parameter values remain in force for repeated use of a statement. Setting a
  //  * parameter value automatically clears its previous value. However, in some cases it is useful to
  //  * immediately release the resources used by the current parameter values; this can be done by
  //  * calling the method <code>clearParameters</code>.
  //  */
  //void PreparedStatement::clearParameters()
  //{
  //  parameters.clear();
  //  initParamset(getPrepareResult()->getParamCount());
  //  hasLongData= false;
  //}

  int64_t PreparedStatement::executeUpdate()
  {
    if (execute()) {
      return 0;
    }
    return getUpdateCount();
  }


  int64_t PreparedStatement::getUpdateCount()
  {
    auto cmdInfo= results ? results->getCmdInformation() : nullptr;
    if (cmdInfo) {
      return cmdInfo->getUpdateCount();
    }
    return -1LL;
  }
} // namespace mariadb
