#include "Box.h"
#include <stdlib.h>

struct
SBoxPrivate {
  sboolean                      free_data;
  SType                         object_type;
  FreeFunc                      free_func;
  union {
    SObject *                    m_sobject;
    spointer                     m_ptr;
    sboolean                     m_sboolean;
    sint                         m_int;
    slong                        m_long;
    sshort                       m_short;
    schar                        m_char;
    #if 0
    wchar_t                      m_wchar; /*< @depricated */
    #endif
    suchar                       m_uchar;
    suint                        m_uint;
    sulong                       m_ulong;
    sushort                      m_ushort;
    schar *                      m_string;
    #if 0
    wchar_t *                    m_wstring; /*< @depricated */
    #endif
    suchar *                     m_ustring;
  } data;
};

void
s_method_box_free (SBox * self);
char *
s_method_box_to_string (SBox * self);

/* Private function to allocate and set methods to the SBox.
 */
SBox *
internal_s_box_new () {
  SBox * self = s_malloc (sizeof (SBox));
  SBoxClass * klass = s_malloc (sizeof (SBoxClass));
  s_object_initialize (S_OBJECT (self), "SBox");

  s_object_set_class (S_OBJECT (self),
                      S_OBJECT_CLASS (klass));
  s_object_set_free_method (S_OBJECT (self),
                            FREE_METHOD (s_method_box_free));
  s_object_set_to_string_method (S_OBJECT (self),
                                 TO_STRING_FUNC (s_method_box_to_string));

  self->priv->free_func = NULL;

  return self;
}

/* set the error if
 */
void
_internal_s_box_set_err_on_missmatch (SBox * self,
                                     SType expeted_type,
                                     SError * err) {

  assert (err != NULL);
  if (self->priv->object_type != expeted_type) {
    s_error_append (err, S_ERROR_TYPE_ERROR, s_string_new_fmt (
                                    "(SBox) Type missmatch:\n"
                                    "Expected type: %s"
                                    "Type held: %s",
                                    s_type_get_name (expeted_type),
                                    s_type_get_name (self->priv->object_type)
                                    ),
                                    S_ERROR_GET_DEFAULT_DOMAIN);
  }
}

SBox *
s_box_new_pointer (spointer object) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_ptr = object;
  self->priv->object_type = S_TYPE_POINTER;

  return self;
}

SBox *
s_box_new_sobject (SObject * object) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_sobject = object;
  self->priv->object_type = S_TYPE_OBJECT;

  return self;
}

SBox *
s_box_new_int (sint i) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_int = i;
  self->priv->object_type = S_TYPE_INT;

  return self;
}

SBox *
s_box_new_long (slong l) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_long = l;
  self->priv->object_type = S_TYPE_LONG;

  return self;
}

SBox *
s_box_new_short (sshort s) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_short = s;
  self->priv->object_type = S_TYPE_SHORT;

  return self;
}

SBox *
s_box_new_char (schar c) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_char = c;
  self->priv->object_type = S_TYPE_CHAR;

  return self;
}

#if 0
SBox *
s_box_new_wchar (wchar_t wc) {

  SBox * self = internal_s_box_new ();
  self->priv->data.m_wchar = wc;
  self->priv->object_type = S_TYPE_WCHAR;

  return self;
}
#endif

SBox *
s_box_new_uint (suint ui) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_uint = ui;
  self->priv->object_type = S_TYPE_UINT;

  return self;
}


SBox *
s_box_new_ulong (sulong ul) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_ulong = ul;
  self->priv->object_type = S_TYPE_ULONG;

  return self;
}

SBox *
s_box_new_ushort (sushort us)  {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_ushort = us;
  self->priv->object_type = S_TYPE_USHORT;

  return self;
}

SBox *
s_box_new_string (schar * s) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_string = s;
  self->priv->object_type = S_TYPE_STRING;

  return self;
}

#if 0
SBox *
s_box_new_wstring (wchar_t * ws) {
  SBox * self = internal_s_box_new ();
  self->priv->data.m_wstring = ws;
  self->priv->object_type = S_TYPE_WSTRING;

  return self;
}
#endif

void
s_box_free (SBox * self) {
  s_object_free (S_OBJECT (self));
}

void
s_box_set_free_data_on_free (SBox * self, sboolean free_data) {
  self->priv->free_data = free_data;
}

void
s_box_set_free_func (SBox * self, FreeFunc free_func) {
  self->priv->free_func = free_func;
}



/* *********************
   ****** Getters ******
   ********************* */
spointer *
s_box_get_pointer (SBox * self, SError * err) {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_POINTER, err);
  if (err) {
    return NULL;
  }
  return self->priv->data.m_ptr;
}

SObject *
s_box_get_sobject (SBox * self, SError * err) {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_OBJECT, err);
  if (err) {
    return NULL;
  }
  return self->priv->data.m_sobject;
}


sint
s_box_get_int (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_INT, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_int;
}


slong
s_box_get_long (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_LONG, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_long;
}

sshort
s_box_get_short (SBox * self, SError * err) {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_SHORT, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_short;
}

schar
s_box_get_char (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_CHAR, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_char;
}

#if 0
wchar_t
s_box_get_wchar (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_WCHAR, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_wchar;
}
#endif

suint
s_box_get_uint (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_UINT, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_uint;
}

sulong
s_box_get_ulong (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_ULONG, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_ulong;
}

sushort
s_box_get_ushort (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_USHORT, err);
  if (err) {
    return 0;
  }
  return self->priv->data.m_ushort;
}

schar *
s_box_get_string (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_STRING, err);
  if (err) {
    return NULL;
  }
  return self->priv->data.m_string;
}

#if 0
wchar_t *
s_box_get_wstring (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_WSTRING, err);
  if (err) {
    return NULL;
  }
  return self->priv->data.m_wstring;
}
#endif

suchar *
s_box_get_ustring (SBox * self, SError * err)  {
  _internal_s_box_set_err_on_missmatch (self, S_TYPE_USTRING, err);
  if (err) {
    return NULL;
  }
  return self->priv->data.m_ustring;
}

void
s_method_box_free (SBox * self) {
  SBoxClass * klass = S_BOX_CLASS (s_object_get_class(S_OBJECT(self)));

  SBoxPrivate * priv = self->priv;

  FreeFunc free_func = priv->free_func;

  if (priv->free_data) {
    switch (priv->object_type) {
      case S_TYPE_OBJECT:
        s_object_free (priv->data.m_sobject);
        break;
      case S_TYPE_POINTER:
        if (free_func) {
          free_func (priv->data.m_ptr);
        } else {
          s_free (priv->data.m_ptr);
        }
        break;
      case S_TYPE_STRING:
        s_free (priv->data.m_string);
        break;
      #if 0
      case S_TYPE_WSTRING:
        s_free (priv->data.m_wstring);
        break;
      #endif
      default:
        s_warn_print ("[SBox] free_data was set despite not being an object"
                      "that can be freed.");
    }
  }

  s_free (self->priv);
  s_free (klass);

}

char *
s_method_box_to_string (SBox * self) {
  char * ret_val = NULL;

  switch (self->priv->object_type) {
    case (S_TYPE_INT):
      ret_val = s_string_new_fmt ("(SBox, Char: %i)",
                                  self->priv->data.m_int);
      break;
    case (S_TYPE_CHAR):
      ret_val = s_string_new_fmt ("(SBox, Char: %c)",
                                  self->priv->data.m_char);
      break;
    case (S_TYPE_LONG):
      ret_val = s_string_new_fmt ("(SBox, Long: %li)",
                                  self->priv->data.m_long);
      break;
    case (S_TYPE_OBJECT):
      ret_val = s_string_new_fmt ("(SBox, SObject: %s)",
                                  s_object_to_string(
                                                   self->priv->data.m_sobject));
      break;
    case (S_TYPE_POINTER):
      ret_val = s_string_new_fmt ("(SBox, Pointer: %lli)",
                                  self->priv->data.m_ptr);
      break;
    case (S_TYPE_SHORT):
      ret_val = s_string_new_fmt ("(SBox, Short: %hi)",
                                  self->priv->data.m_short);
      break;
    case (S_TYPE_STRING):
      ret_val = s_string_new_fmt ("(SBox, String: \"%s\")",
                                  self->priv->data.m_string);
      break;
    case (S_TYPE_UINT):
      ret_val = s_string_new_fmt ("(SBox, Unsigned Int: %u)",
                                  self->priv->data.m_uint);
      break;
    case (S_TYPE_ULONG):
      ret_val = s_string_new_fmt ("(SBox, Unsigned Long: %lu)",
                                  self->priv->data.m_ulong);
      break;
    case (S_TYPE_USHORT):
      ret_val = s_string_new_fmt ("(SBox, Unsigned Short: %hu)",
                                  self->priv->data.m_ushort);
      break;
    #if 0
    case (S_TYPE_WCHAR):
      ret_val = s_string_new_fmt ("(SBox, Wide Char: %lc)",
                                  self->priv->data.m_wchar);
      break;
    
    case (S_TYPE_WSTRING):
      ret_val = s_string_new_fmt ("(SBox, Wide String: %s)",
                                  s_wstring_to_string (
                                                   self->priv->data.m_wstring));
      break;
    #endif
    case (S_TYPE_USTRING):
      ret_val = s_string_new_fmt ("(SBox, U String: %s)",
                                  s_ustring_to_string (
                                                   self->priv->data.m_ustring));
      break;
    default:
      s_err_print ("THIS SHOULD NOT BE ALBE TO BE REACHED.");
  }
  return ret_val;
}

