#pragma once

#include "defs.h"
#include "baseobject.h"
#include "Error.h"
#include "Func.h"

S_BEGIN_DECLS

/** @file
 * @defgroup SBox SBox
 * @addtogroup SBox
 * @{
 * An SBox is a boxed type.
 */

/** @brief
 * A reference counted box sitting on top of an SObject.
 *
 * When creating an SBox use the s_box_new() macro, it expands to the correct
 * constructor using the C11 _Generic macro.
 *
 * When freeing the SBox use s_object_free() function. If you depend
 * on reference counting use s_object_unref() to do this.
 */
typedef struct SBox SBox;

typedef struct SBoxClass SBoxClass;

typedef struct SBoxPrivate SBoxPrivate;

struct
SBox {
  SObject parent;
  SBoxPrivate * priv;
};

struct
SBoxClass {
  SObjectClass parent_class;
};

#define S_BOX(o) (SBox *)(o);
#define S_BOX_CLASS(k) (SBoxClass *)(k);

#ifndef __MSC_VER
/**
 * C11 Generic macro to create object easily.
 *
 * This is what is most likely to be used in real code, because it makes life
 * a lot easier than to remember all the other commands.
 *
 * The caveat is that there is no way to construct a macro to do the same
 * for the s_box_get_ functions.
 */
#define  s_box_new(x) _Generic((x)\
                               spointer: s_box_new_pointer,\
                               SObject *: s_box_new_sobject,\
                               slong: s_box_new_long,\
                               sshort: s_box_new_short,\
                               schar: s_box_new_char,\
                               wchar_t: s_box_new_wchar,\
                               suchar: s_box_new_uchar,\
                               schar *: s_box_new_string,\
                               wchar_t *: s_box_new_wstring\
                               )(x)
#endif /* __MSC_VER */

/**
 * Creates a new SBox object.
 *
 * @param object The object to be boxed.
 *
 * @return A new SBox object.
 */
S_EXPORTED
SBox *
s_box_new_pointer (spointer object);

/**
 * @param object the SObject to stored in the Box.
 *
 * @return a new SBox object.
 */
S_EXPORTED
SBox *
s_box_new_sobject (SObject * object);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_int (sint i);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_long (slong l);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_short (sshort s);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_char (schar c);

/**
 * @S_DEPRECATED For everyone's sanity.
 * @see s_box_get_pointer.
 */
S_EXPORTED
S_DEPRECATED
SBox *
s_box_new_wchar (wchar_t wc);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_uchar (suchar c);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_uint (suint ui);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_ulong (sulong l);

/**
 * @see s_box_get_pointer.
 */
S_EXPORTED
SBox *
s_box_new_ushort (sushort s);

/**
 * @see s_box_get_pointer.
 */

S_EXPORTED
SBox *
s_box_new_string (schar * s);

/**
 * @see s_box_get_pointer.
 */
S_DEPRECATED
SBox *
s_box_new_wstring (wchar_t * ws);


/**
 * Free the an SBox.
 *
 * @param box The SBox to be freed.
 */
S_EXPORTED
void
s_box_free (SBox * box);

/* *********************
   ****** Getters ******
   ********************* */

 /**
 * @param self The SBox to get the pointer from.
 *
 * @return the pointer stored in the SBox.
 *
 * @note you must cast to the correct type.
 */
S_EXPORTED
spointer *
s_box_get_pointer (SBox * self, SError * err);

 /**
 * @param self The SBox to get the object from.
 *
 * @return the SObject stored in the SBox.
 *
 * @note You should cast to the correct SObject derivative type.
 */
S_EXPORTED
SObject *
s_box_get_sobject (SBox * self, SError * err);

/**
 * @param self The box to get the int from.
 *
 * @return the int that was stored in the
 */
S_EXPORTED
sint
s_box_get_int (SBox * self, SError * err);

/**
 * @param self the box to get the long from.
 *
 * @return the long stored in the box.
 */
S_EXPORTED
slong
s_box_get_long (SBox * self, SError * err);

/**
 * @param self the box to get short from.
 *
 * @return the short from the box.
 */
S_EXPORTED
sshort
s_box_get_short (SBox * self, SError * err);

/**
 * @param self the box to get the char from.
 *
 * @return the char stored in the
 */
S_EXPORTED
schar
s_box_get_char (SBox * self, SError * err);

/**
 * @param self the box to get the wide char from.
 *
 * @return the wide char stored in the box.
 */
S_DEPRECATED
wchar_t
s_box_get_wchar (SBox * self, SError * err);

/**
 * @param self the box to get the char from.
 *
 * @return the char stored in the
 */
S_EXPORTED
suchar
s_box_get_uchar (SBox * self, SError * err);

/**
 * @param self
 */
S_EXPORTED
suint
s_box_get_uint (SBox * self, SError * err);

S_EXPORTED
sulong
s_box_get_ulong (SBox * self, SError * err);

S_EXPORTED
sushort
s_box_get_ushort (SBox * self, SError * err);

S_EXPORTED
schar *
s_box_get_string (SBox * self, SError * err);

S_DEPRECATED
wchar_t *
s_box_get_wstring (SBox * self, SError * err);

S_EXPORTED
suchar *
s_box_get_ustring (SBox * self, SError * err);

/**
 * Gets the SType of the object that is stored in the SBox.
 */
S_EXPORTED
SType
s_box_get_type (SBox * box);

/**
 * Gets the type name of the object.
 *
 * @param box The SBox to get the type name from.
 * @return A String containing the name of the type.
 *
 * @note caller must free the string.
 */
S_EXPORTED
char *
s_box_get_type_name (SBox * box);

/**
 * Set the free func to be used when the box is freed.
 *
 * This will only work on Strings, Pointers and SObjects.
 *
 * @param box the SBox to add the free function to.
 *
 * @param free_func the function to be used when freeing the data.
 */
S_EXPORTED
void
s_box_set_free_func (SBox * box, FreeFunc free_func);

/**
 * Set whether or not the data should be freed when the box is freed.
 */
S_EXPORTED
void
s_box_set_free_data_on_free (SBox * self, sboolean free_data);

/** @} */

S_END_DECLS
