/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    Pseudo_QC_Object.cpp

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    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/QoreObjectIntern.h"
#include "qore/intern/QoreObjectIterator.h"
#include "qore/intern/QoreClassIntern.h"

extern QoreClass* QC_OBJECTITERATOR;
extern QoreClass* QC_OBJECTKEYITERATOR;
extern QoreClass* QC_OBJECTPAIRITERATOR;
extern QoreClass* QC_OBJECTKEYITERATOR;
extern QoreClass* QC_OBJECTPAIRITERATOR;

//! Methods in this pseudo-class can be executed on @ref object "objects"
/**
 */
qclass <object> [arg=QoreObject* obj = const_cast<QoreObject*>(v.get<QoreObject>());vparent=<value>];

//! Returns Qore::NT_OBJECT
/**
    @return Qore::NT_OBJECT

    @par Example:
    @code{.py}
switch (o.typeCode()) {
    case Qore::NT_OBJECT:
        printf("%y: is an object\n", o);
        break;
}
    @endcode
 */
int <object>::typeCode() [flags=CONSTANT] {
   return NT_OBJECT;
}

//! returns @ref True in all cases for objects
/** @par Example:
    @code{.py}
bool b = n.complexType();
    @endcode

    @return @ref True in all cases for objects

    @see
    - <value>::fullType()

    @since %Qore 0.8.13
*/
bool <object>::complexType() [flags=CONSTANT] {
   return true;
}

//! Returns a list of member names of the object; if called from outside the object, only public members are returned
/**
    @return a list of member names of the object; if called from outside the object, only public members are returned

    @par Example:
    @code{.py}
list<string> l = o.keys();
    @endcode
 */
list<string> <object>::keys() [flags=RET_VALUE_ONLY] {
   return obj->getMemberList(xsink);
}

//! Returns the first member name in the object or @ref nothing if the object has no members; if called from outside the object, the first public member name is returned (if any)
/**
    @return the first member name in the object or @ref nothing if the object has no members; if called from outside the object, the first public member name is returned (if any)

    @par Example:
    @code{.py}
*string n = o.firstKey();
    @endcode

    @see <object>::lastKey()
 */
*string <object>::firstKey() [flags=RET_VALUE_ONLY] {
   return qore_object_private::firstKey(obj, xsink);
}

//! Returns the last member name in the object or @ref nothing if the object has no members; if called from outside the object, the last public member name is returned (if any)
/**
    @return the last member name in the object or @ref nothing if the object has no members; if called from outside the object, the last public member name is returned (if any)

    @par Example:
    @code{.py}
*string n = o.lastKey();
    @endcode

    @see <object>::firstKey()
 */
*string <object>::lastKey() [flags=RET_VALUE_ONLY] {
   return qore_object_private::lastKey(obj, xsink);
}

//! Returns @ref True if the object has no public or private members, @ref False if it does
/** The opposite of <object>::val()

    @return @ref True if the object has no public or private members, @ref False if it does

    @par Example:
    @code{.py}
bool b = o.empty();
    @endcode
 */
bool <object>::empty() [flags=RET_VALUE_ONLY] {
   return !obj->size(xsink);
}

//! Returns the number of members in the object, public and private
/**
    @return the number of members in the object, public and private

    @par Example:
    @code{.py}
int num = o.size();
    @endcode

    @see <object>::sizep()
 */
int <object>::size() [flags=RET_VALUE_ONLY] {
   return obj->size(xsink);
}

//! Returns the class name of the object
/**
    @return the class name of the object

    @par Example:
    @code{.py}
string cn = o.className();
    @endcode

    @note equivalent to get_class_name()
 */
string <object>::className() [flags=CONSTANT] {
   return new QoreStringNode(obj->getClassName());
}

//! Returns @ref True if the object is a system object (ie a constant object like @ref stdin, etc), @ref False if not
/**
    @return @ref True if the object is a system object (ie a constant object like @ref stdin, etc), @ref False if not

    @par Example:
    @code{.py}
bool b = i.isSystem();
    @endcode
 */
bool <object>::isSystem() [flags=CONSTANT] {
   return obj->isSystemObject();
}

//! Returns @ref False if the object has no public or private members, @ref True if it does
/** The opposite of <object>::empty()

    @return @ref False if the object has no public or private members, @ref True if it does

    @par Example:
    @code{.py}
bool b = o.val();
    @endcode

    @see
    - @ref perl-bool-eval "%perl-bool-eval"
    - @ref strict-bool-eval "%strict-bool-eval"
 */
bool <object>::val() [flags=RET_VALUE_ONLY] {
   return (bool)obj->size(xsink);
}

// bool <object>::hasKey(softstring key) [flags=RET_VALUE_ONLY] {}
// bool <object>::hasKeyValue(softstring key) [flags=RET_VALUE_ONLY] {}

//! Returns an @ref Qore::ObjectIterator "ObjectIterator" object for the object's members
/** @return an @ref Qore::ObjectIterator "ObjectIterator" object for the object's members

    @par Example:
    @code{.py}
map printf("+ %y\n", $1), obj.iterator();
    @endcode

    @since %Qore 0.8.6
 */
AbstractIterator <object>::iterator() [flags=CONSTANT] {
   return new QoreObject(QC_OBJECTITERATOR, 0, new QoreObjectIterator(obj));
}

//! Returns a @ref Qore::ObjectKeyIterator "ObjectKeyIterator" object for the object, iterating the object's members
/** @return a @ref Qore::ObjectKeyIterator "ObjectKeyIterator" object for the object, iterating the object's members

    @par Example:
    @code{.py}
map printf("+ %s\n", $1), obj.keyIterator();
    @endcode

    @since %Qore 0.8.6.2
 */
ObjectKeyIterator <object>::keyIterator() [flags=CONSTANT] {
   return new QoreObject(QC_OBJECTKEYITERATOR, 0, new QoreObjectIterator(obj));
}

//! Returns a @ref Qore::ObjectPairIterator "ObjectPairIterator" object for the object's members
/** @return a @ref Qore::ObjectPairIterator "ObjectPairIterator" object for the object's members

    @par Example:
    @code{.py}
map printf("+ %s\n", $1), obj.pairIterator();
    @endcode

    @since %Qore 0.8.6.2
 */
ObjectPairIterator <object>::pairIterator() [flags=CONSTANT] {
   return new QoreObject(QC_OBJECTPAIRITERATOR, 0, new QoreObjectIterator(obj));
}

//! Returns @ref True "True" if the given method exists (can be non-static or static) and is callable from the current context
/** @par Example:
    @code{.py}
bool b = obj.hasCallableMethod("getStatus");
    @endcode

    @return @ref True "True" if the given method exists (can be non-static or static) and is callable from the current context

    @see
    - hasCallableNormalMethod()
    - hasCallableStaticMethod

    @note
    - returns @ref False "False" if the method doesn't exist or is not callable due to being private, for example
    - if a @ref methodGate_methods "methodGate() method" exists but no explicit definition of the given method exists, then this pseudo-method will return @ref False "False", but any method name can be called on the object due to the existence of the @ref methodGate_methods "methodGate() method"
    - this pseudo-method will also return @ref True "True" if the given method exists and is accessible in an inherited class

    @since %Qore 0.8.8
 */
bool <object>::hasCallableMethod(string name) [flags=CONSTANT] {
   return qore_class_private::runtimeHasCallableMethod(*(obj->getClass()), name->getBuffer());
}

//! Returns @ref True "True" if the given non-static method exists and is callable from the current context
/** @par Example:
    @code{.py}
bool b = obj.hasCallableNormalMethod("getStatus");
    @endcode

    @return @ref True "True" if the given non-static method exists and is callable from the current context

    @see
    - hasCallableMethod()
    - hasCallableStaticMethod

    @note
    - returns @ref False "False" if the method doesn't exist or is not callable due to being private, for example
    - if a @ref methodGate_methods "methodGate() method" exists but no explicit definition of the given method exists, then this pseudo-method will return @ref False "False", but any method name can be called on the object due to the existence of the @ref methodGate_methods "methodGate() method"
    - this pseudo-method will also return @ref True "True" if the given method exists and is accessible in an inherited class

    @since %Qore 0.8.8
 */
bool <object>::hasCallableNormalMethod(string name) [flags=CONSTANT] {
   return qore_class_private::runtimeHasCallableNormalMethod(*(obj->getClass()), name->getBuffer());
}

//! Returns @ref True "True" if the given static method exists and is callable from the current context
/** @par Example:
    @code{.py}
bool b = obj.hasCallableStaticMethod("getStatus");
    @endcode

    @return @ref True "True" if the given static method exists and is callable from the current context

    @see
    - hasCallableMethod()
    - hasCallableStaticMethod

    @note
    - returns @ref False "False" if the static method doesn't exist or is not callable due to being private, for example
    - this pseudo-method will also return @ref True "True" if the given static method exists and is accessible in an inherited class

    @since %Qore 0.8.8
 */
bool <object>::hasCallableStaticMethod(string name) [flags=CONSTANT] {
   return qore_class_private::runtimeHasCallableStaticMethod(*(obj->getClass()), name->getBuffer());
}

//! Returns @ref True since objects can return a non-zero size
/** @return @ref True since objects can return a non-zero size

    @see <object>::size()

    @since %Qore 0.8.9
*/
bool <object>::sizep() [flags=CONSTANT] {
   return true;
}

//! Return a unique string for the data independent of the content
/** @return a unique string for the data independent of the content

    @note an SHA1 hash is created with a random salt created when the %Qore library is initialized and the internal pointer address

    @since %Qore 0.8.12
 */
string <object>::uniqueHash() [flags=CONSTANT] {
   QoreStringNode* rv = new QoreStringNode;
   qore_get_ptr_hash(*rv, obj);
   return rv;
}

//! resolve the string as a call reference
/** @param identifier the string to resolve; function or static class method, can include namespace path

    @return the call reference to the given function or static method; the string is resolved as follows:
    - if in a class method, a non-static method lookup is made
    - if not found, then a static method lookup is made
    - if not found, then a function lookup is made
    - if not found, an \c CALL-REFERENCE-ERROR exception is thrown

    @throw CALL-REFERENCE-ERROR cannot resolve call reference; method not accessible in the calling context

    @note
    - Pseudo-methods run with the class context of the caller, which enables this method to access any accessible
      private method; use this pseudo-method instead of @ref Qore::get_call_reference() "get_call_reference()" to get
      call references to accessible private methods in a class context
    - call references to non-static methods contain a weak reference to the object; the lifetime of the object is not
      extended by the call reference

    @see
    - @ref Qore::get_call_reference()
    - @ref Qore::Program::getCallReference()

    @since %Qore 0.9.4
 */
code <object>::getCallReference(string identifier) [flags=RET_VALUE_ONLY] {
    return get_call_reference_intern(obj, identifier, xsink);
}
