/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    Pseudo_QC_Hash.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/QoreHashNodeIntern.h"
#include "qore/intern/QoreHashIterator.h"
#include "qore/intern/QoreHashListIterator.h"

extern QoreClass* QC_HASHLISTITERATOR;
extern QoreClass* QC_HASHITERATOR;
extern QoreClass* QC_HASHKEYITERATOR;
extern QoreClass* QC_HASHPAIRITERATOR;

//! Methods in this pseudo-class can be executed on @ref hash "hash values"
/**
 */
qclass <hash> [arg=const QoreHashNode* h = v.get<QoreHashNode>();vparent=<value>];

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

    @par Example:
    @code{.py}
switch (h.typeCode()) {
    case NT_HASH:
        printf("%y: is a hash\n", h);
        break;
}
    @endcode

    @note this pseudo-method is also implemented in Qore::zzz8valuezzz9, so it's safe to use on all data types
 */
int <hash>::typeCode() [flags=CONSTANT] {
   return NT_HASH;
}

//! returns @ref True if the hash has a complex type (@ref hashdecl or @ref hash_complex_type), @ref False if not
/** @par Example:
    @code{.py}
bool b = n.complexType();
    @endcode

    @return @ref True if the hash has a complex type (@ref hashdecl or @ref hash_complex_type), @ref False if not

    @see
    - <value>::fullType()

    @since %Qore 0.8.13
*/
bool <hash>::complexType() [flags=CONSTANT] {
   const qore_hash_private* hp = qore_hash_private::get(*h);
   return hp->hashdecl || hp->complexTypeInfo ? true : false;
}

//! Returns a list of key names of the hash
/**
    @return a list of key names of the hash

    @par Example:
    @code{.py}
list<string> l = h.keys();
    @endcode

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @see <hash>::values()
 */
list<string> <hash>::keys() [flags=CONSTANT] {
   return h->getKeys();
}

//! Returns a list of values of the hash
/**
    @return a list of values of the hash

    @par Example:
    @code{.py}
list<auto> l = h.values();
    @endcode

    @note
    - equivalent to hash_values()
    - this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")
    - a hash with a declared value type will have a plain untyped list returned; this will be fixed in a future version of %Qore

    @see <hash>::keys()
 */
list<auto> <hash>::values() [flags=CONSTANT] {
    return qore_hash_private::get(*h)->getValues();
}

//! Returns the first key name in the hash or @ref nothing if the hash has no keys
/**
    @return the first key name in the hash or @ref nothing if the hash has no keys

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

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @see <hash>::lastKey()
 */
*string <hash>::firstKey() [flags=CONSTANT] {
   const char *key = h->getFirstKey();
   return key ? new QoreStringNode(key) : 0;
}

//! Returns the value assigned to the first key in the hash if any or @ref nothing if the hash has no keys
/**
    @return the value assigned to the first key in the hash if any or @ref nothing if the hash has no keys

    @par Example:
    @code{.py}
auto v = h.firstValue();
    @endcode

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @see <hash>::lastValue()
 */
auto <hash>::firstValue() [flags=CONSTANT] {
    return qore_hash_private::getFirstKeyValue(h).refSelf();
}

//! Returns the last key name in the hash or @ref nothing if the hash has no keys
/**
    @return the last key name in the hash or @ref nothing if the hash has no keys

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

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @see <hash>::firstKey()
 */
*string <hash>::lastKey() [flags=CONSTANT] {
   const char *key = h->getLastKey();
   return key ? new QoreStringNode(key) : 0;
}

//! Returns the value assigned to the last key in the hash if any or @ref nothing if the hash has no keys
/**
    @return the value assigned to the last key in the hash if any or @ref nothing if the hash has no keys

    @par Example:
    @code{.py}
auto v = h.lastValue();
    @endcode

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @see <hash>::firstValue()
 */
auto <hash>::lastValue() [flags=CONSTANT] {
    return qore_hash_private::getLastKeyValue(h).refSelf();
}

//! Returns @ref True if the key exists in the hash (may or may not be assigned a value), @ref False if not
/**
    @param key the key name to check

    @return @ref True if the key exists in the hash (may or may not be assigned a value), @ref False if not

    @par Example:
    @code{.py}
bool b = h.hasKey(key);
    @endcode

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @see <hash>::hasKeyValue(softstring)
 */
bool <hash>::hasKey(softstring key) [flags=RET_VALUE_ONLY] {
   TempEncodingHelper str(key, QCS_DEFAULT, xsink);
   if (!str)
      return false;

   return h->existsKey(str->getBuffer());
}

//! Returns @ref True if the key exists and is assigned to a value, @ref False if not
/**
    @param key the key name to check

    @return @ref True if the key exists and is assigned to a value, @ref False if not

    @par Example:
    @code{.py}
bool b = h.hasKeyValue(key);
    @endcode

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @see <hash>::hasKey(softstring)
 */
bool <hash>::hasKeyValue(softstring key) [flags=RET_VALUE_ONLY] {
   TempEncodingHelper str(key, QCS_DEFAULT, xsink);
   if (!str)
      return false;

   return h->existsKeyValue(str->getBuffer());
}

//! Returns @ref True if the hash has no keys, @ref False if it does
/**
    @return @ref True if the hash has no keys, @ref False if it does

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

    @note this pseudo-method is also implemented in Qore::zzz8valuezzz9, so it's safe to use on all data types
 */
bool <hash>::empty() [flags=CONSTANT] {
   return h->empty();
}

//! Returns the number of keys in the hash
/** The opposite of <hash>::val()

    @return the number of keys in the hash

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

    @note this pseudo-method is also implemented in Qore::zzz8valuezzz9, so it's safe to use on all data types

    @see <hash>::sizep()
 */
int <hash>::size() [flags=CONSTANT] {
   return h->size();
}

//! Returns @ref False if the hash has no keys, @ref True if it does
/** The opposite of <hash>::empty()

    @return @ref False if the hash has no keys, @ref True if it does

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

    @note this pseudo-method is also implemented in Qore::zzz8valuezzz9, so it's safe to use on all data types

    @see
    - @ref perl-bool-eval "%perl-bool-eval"
    - @ref strict-bool-eval "%strict-bool-eval"
 */
bool <hash>::val() [flags=CONSTANT] {
   return !h->empty();
}

//! Returns a @ref Qore::HashIterator "HashIterator" object for the hash
/** @return a @ref Qore::HashIterator "HashIterator" object for the hash

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

    @note this pseudo-method is also implemented in Qore::zzz8valuezzz9, so it's safe to use on all data types

    @since %Qore 0.8.6
 */
AbstractIterator <hash>::iterator() [flags=CONSTANT] {
   return new QoreObject(QC_HASHITERATOR, 0, new QoreHashIterator(h));
}

//! Returns a @ref Qore::HashKeyIterator "HashKeyIterator" object for the hash
/** @return a @ref Qore::HashKeyIterator "HashKeyIterator" object for the hash

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

    @note
    - this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")
    - this pseudo-method is very useful when using a hash as a simulation for a set of strings and quickly iterating the hash with the @ref map "map operator", for example

    @since %Qore 0.8.6.2
 */
HashKeyIterator <hash>::keyIterator() [flags=CONSTANT] {
   return new QoreObject(QC_HASHKEYITERATOR, 0, new QoreHashIterator(h));
}

//! Returns a @ref Qore::HashPairIterator "HashPairIterator" object for the hash
/** @return a @ref Qore::HashPairIterator "HashPairIterator" object for the hash

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

    @note this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")

    @since %Qore 0.8.6.2
 */
HashPairIterator <hash>::pairIterator() [flags=CONSTANT] {
   return new QoreObject(QC_HASHPAIRITERATOR, 0, new QoreHashIterator(h));
}

//! Returns a @ref Qore::HashListIterator "HashListIterator" object for the hash
/** @return a @ref Qore::HashListIterator "HashListIterator" object for the hash

    @par Example:
    @code{.py}
*hash<auto> q = db.select("select * from table_name");
map printf("+ %s\n", $1), h.contextIterator();
    @endcode

    @note
    - a hash with non-list values will be iterated as if each key value were a single element list
    - this pseudo-method is also implemented in Qore::zzz8nothingzzz9, so it's safe to use where the value could be a hash or @ref nothing (ex: @ref hash_or_nothing_type "*hash")
    - this pseudo-method is very useful when iterating query results from @ref Qore::SQL::Datasource::select() "Datasource::select()" or @ref Qore::SQL::DatasourcePool::select() "DatasourcePool::select()" with the @ref map "map operator", for example

    @since %Qore 0.8.6.2
 */
HashListIterator <hash>::contextIterator() [flags=CONSTANT] {
   return new QoreObject(QC_HASHLISTITERATOR, 0, new QoreHashListIterator(h));
}

//! Returns @ref True "True" if the hash argument passed has the same keys in the same order as the current hash, @ref False "False" if not
/** @par Example:
    @code{.py}
bool b = h.compareKeys(oh);
    @endcode

    Values are not compraed, only the key names and order

    @param oh a hash to compare the keys against

    @return @ref True "True" if the hash argument passed has the same keys in the same order as the current hash, @ref False "False" if not

    @since %Qore 0.8.8
 */
bool <hash>::compareKeys(hash<auto> oh) [flags=CONSTANT] {
   if (h->size() != oh->size())
      return false;

   ConstHashIterator hi1(h);
   ConstHashIterator hi2(oh);
   while (hi1.next()) {
      hi2.next();
      if (strcmp(hi1.getKey(), hi2.getKey()))
         return false;
   }

   return true;
}

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

    @see <hash>::size()

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