/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
  ql_context.qpp

  Qore Programming Language

  Copyright (C) 2003 - 2023 David Nichols

  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the "Software"),
  to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
  and/or sell copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  DEALINGS IN THE SOFTWARE.

  Note that the Qore library is released under a choice of three open-source
  licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
  information.
*/

#include <qore/Qore.h>
#include "qore/intern/Context.h"

static AbstractQoreNode* cx_err(const char* func, ExceptionSink* xsink) {
   return xsink->raiseException("CONTEXT-ERROR", "function %s() called without a current data iteration context", func);
}

/** @defgroup context_functions Context Functions
*/
///@{
//! Returns the current value of the given column while iterating a @ref context "context statement"
/**
    @param key the column name to return the value for

    @return the current value of the given column while iterating a @ref context "context statement"

    @par Example:
    @code{.py}
context (ds.select(sql)) {
    auto val = cx_value("column1");
}
    @endcode

    @throw CONTEXT-ERROR this exception is thrown if called without an active @ref context "context statement"
    @throw ENCODING-CONVERSION-ERROR the given string could not be converted to the @ref default_encoding "default character encoding"

    @since %Qore 0.8.4
 */
auto cx_value(string key) [flags=RET_VALUE_ONLY] {
   Context* c = get_context_stack();
   if (!c)
      return cx_err("cx_value", xsink);

   TempEncodingHelper tmp(key, QCS_DEFAULT, xsink);
   if (!tmp)
      return QoreValue();

   return c->eval(tmp->getBuffer(), xsink);
}

//! Returns @ref True if currently iterating the first element in a @ref context "context statement", @ref False if not
/**
    @return @ref True if currently iterating the first element in a @ref context "context statement", @ref False if not

    @par Example:
    @code{.py}
context (ds.select(sql)) {
    if (cx_first())
        print("first row!\n");
}
    @endcode

    @throw CONTEXT-ERROR this exception is thrown if called without an active @ref context "context statement"

    @since %Qore 0.8.4
 */
bool cx_first() [flags=RET_VALUE_ONLY] {
   Context* c = get_context_stack();
   if (!c)
      return cx_err("cx_first", xsink);

   return c->isFirst();
}

//! Returns @ref True if currently iterating the last element in a @ref context "context statement", @ref False if not
/**
    @return @ref True if currently iterating the last element in a @ref context "context statement", @ref False if not

    @par Example:
    @code{.py}
context (ds.select(sql)) {
    if (cx_last())
        print("last row!\n");
}
    @endcode

    @throw CONTEXT-ERROR this exception is thrown if called without an active @ref context "context statement"

    @since %Qore 0.8.4
 */
bool cx_last() [flags=RET_VALUE_ONLY] {
   Context* c = get_context_stack();
   if (!c)
      return cx_err("cx_last", xsink);

   return c->isLast();
}

//! Returns the current row number within the active @ref context "context statement" (starting from 0)
/**
    @return the current row number within the active @ref context "context statement" (starting from 0)

    @par Example:
    @code{.py}
context (ds.select(sql)) {
    printf("row %d/%d:\n", cx_pos(), cx_total());
}
    @endcode

    @throw CONTEXT-ERROR this exception is thrown if called without an active @ref context "context statement"

    @since %Qore 0.8.4
 */
int cx_pos() [flags=RET_VALUE_ONLY] {
   Context* c = get_context_stack();
   if (!c)
      return cx_err("cx_pos", xsink);

   return c->getPos();
}

//! Returns the total number of rows within the active @ref context "context statement"
/**
    @return the total number of rows within the active @ref context "context statement"

    @par Example:
    @code{.py}
context (ds.select(sql)) {
    printf("row %d/%d:\n", cx_total(), cx_total());
}
    @endcode

    @throw CONTEXT-ERROR this exception is thrown if called without an active @ref context "context statement"

    @since %Qore 0.8.4
 */
int cx_total() [flags=RET_VALUE_ONLY] {
   Context* c = get_context_stack();
   if (!c)
      return cx_err("cx_total", xsink);

   return c->getTotal();
}
///@}
