/***************************************************************************

  gbx_subr_conv.c

  (c) 2000-2017 Benoît Minisini <g4mba5@gmail.com>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.

***************************************************************************/

#include "gb_common.h"

#include "gbx_value.h"
#include "gbx_subr.h"
#include "gbx_local.h"

#include "gbx_string.h"
#include "gbx_date.h"
#include "gbx_number.h"


void SUBR_is_type(ushort code)
{
	static void *jump[] = {
		&&__BAD, &&__BOOLEAN, &&__BAD, &&__BAD, &&__INTEGER, &&__LONG, &&__BAD, &&__FLOAT,
		&&__DATE, &&__BAD, &&__BAD, &&__BAD, &&__BAD, &&__BAD, &&__NUMBER, &&__NULL,
		&&__BAD, &&__BAD
		};

	bool test;
	char *addr;
	int len;
	VALUE temp;

	SUBR_ENTER_PARAM(1);

	code &= 0x3F;

	if (code == T_NULL)
		goto __NULL;

	//VALUE_conv_string(PARAM);
	SUBR_get_string_len(PARAM, &addr, &len);
	VALUE_from_string(&temp, addr, len);

	goto *jump[code];

__BOOLEAN:

	test = temp.type == T_BOOLEAN;
	goto __END;

__INTEGER:

	test = temp.type == T_INTEGER;
	goto __END;

__LONG:

	test = temp.type == T_LONG || temp.type == T_INTEGER;
	goto __END;

__FLOAT:
__NUMBER:

	test = temp.type == T_FLOAT || temp.type == T_LONG || temp.type == T_INTEGER;
	goto __END;

__DATE:

	test = temp.type == T_DATE;
	goto __END;

__NULL:

	test = VALUE_is_null(PARAM);
	goto __END;

__BAD:

	THROW_ILLEGAL();

__END:

	RELEASE(PARAM);
	SP--;
	SP->type = T_BOOLEAN;
	SP->_integer.value = (-test);
	SP++;
}

/*
void SUBR_conv(ushort code)
{
  VALUE_convert(SP - 1, code & 0x3F);
}
*/

void SUBR_type(ushort code)
{
  TYPE type;
	int val;

  SUBR_ENTER_PARAM(1);

	if (code & 0x3F)
	{
		val = TYPE_sizeof_memory(SUBR_get_integer(PARAM));
	}
	else
  {
		type = PARAM->type;
		if (type == T_VARIANT)
			type = PARAM->_variant.vtype;

    if (type == T_CSTRING)
      val = T_STRING;
    else if (TYPE_is_object(type) && type != T_NULL)
      val = T_OBJECT;
    else
			val = type;
  }

  RETURN->_integer.value = val;
  RETURN->type = T_INTEGER;

  SUBR_LEAVE();
}


void SUBR_str(void)
{
  char *addr;
  int len;

  SUBR_ENTER_PARAM(1);

  VALUE_to_string(PARAM, &addr, &len);
  STRING_new_temp_value(RETURN, addr, len);

  SUBR_LEAVE();
}


void SUBR_val(void)
{
  char *addr;
  int len;

  SUBR_ENTER_PARAM(1);

  if (SUBR_check_string(PARAM))
		VALUE_null(RETURN);
	else
	{
		VALUE_get_string(PARAM, &addr, &len);
		VALUE_from_string(RETURN, addr, len);
		VALUE_conv_variant(RETURN);
	}

  SUBR_LEAVE();
}


void SUBR_format(ushort code)
{
  int fmt_type;
  char *format = NULL;
  int len = 0;
  DATE_SERIAL *date;
  char *str;
  int len_str;

  SUBR_ENTER();

  if (NPARAM == 1)
    fmt_type = LF_STANDARD;
  else
  {
		if (PARAM[1].type == T_VARIANT)
			VARIANT_undo(&PARAM[1]);

    if (TYPE_is_string(PARAM[1].type))
    {
      fmt_type = LF_USER;
      VALUE_get_string(&PARAM[1], &format, &len);
			if (!len)
				fmt_type = LF_STANDARD;
    }
    else if (TYPE_is_integer(PARAM[1].type))
    {
      fmt_type = PARAM[1]._integer.value;
			if (fmt_type <= LF_USER || fmt_type >= LF_MAX)
				THROW(E_ARG);
    }
    else
      THROW_TYPE(T_INTEGER, PARAM[1].type);
  }

	if (PARAM->type == T_VARIANT)
		VARIANT_undo(PARAM);

  if (PARAM->type == T_DATE)
  {
    date = DATE_split(PARAM);
    if (LOCAL_format_date(date, fmt_type, format, len, &str, &len_str))
      THROW(E_FORMAT);
  }
  else
  {
    VALUE_conv_float(PARAM);
    if (LOCAL_format_number(PARAM->_float.value, fmt_type, format, len, &str, &len_str, TRUE))
      THROW(E_FORMAT);
  }

  /*if (NPARAM >= 2)
    RELEASE_STRING(&PARAM[1]);*/

  STRING_new_temp_value(RETURN, str, len_str);

  SUBR_LEAVE();
}


void SUBR_hex_bin(ushort code)
{
  int prec = 0;
  int base;
  int max_prec;

  SUBR_ENTER();

  VALUE_conv(PARAM, T_LONG);

	switch(code >> 8)
	{
		case CODE_BIN:
			base = 2;
			max_prec = 64;
			break;
			
		case CODE_HEX:
			base = 16;
			max_prec = 16;
			break;
			
		default:
			if (NPARAM == 0) // Compatibility with 2010 bytecode
			{
				SUBR_upper(1);
				return;
			}
			base = 8;
			max_prec = 22;
			break;
	}

  if (NPARAM == 2)
  {
    VALUE_conv_integer(&PARAM[1]);

    prec = PARAM[1]._integer.value;

    if (prec < 1 || prec > max_prec)
      THROW(E_ARG);
  }

  NUMBER_int_to_string(PARAM->_long.value, prec, base, RETURN);

  SUBR_LEAVE();
}


