/**
 * Handle calling [[Call]] of built-in String object
 *
 * See also:
 *          ECMA-262 v6, 21.1.1.1
 *
 * @return ecma value
 */
ecma_value_t
ecma_builtin_string_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */
                                   ecma_length_t arguments_list_len) /**< number of arguments */
{
  JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL);

  ecma_value_t ret_value = ECMA_VALUE_EMPTY;

  /* 1. */
  if (arguments_list_len == 0)
  {
    ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY);
  }
#if ENABLED (JERRY_ES2015_BUILTIN_SYMBOL)
  /* 2.a */
  else if (ecma_is_value_symbol (arguments_list_p[0]))
  {
    ret_value = ecma_get_symbol_descriptive_string (arguments_list_p[0]);
  }
#endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */
  /* 2.b */
  else
  {
    ret_value = ecma_op_to_string (arguments_list_p[0]);
  }

  return ret_value;
} /* ecma_builtin_string_dispatch_call */
/**
 * The String object's 'fromCharCode' routine
 *
 * See also:
 *          ECMA-262 v5, 15.5.3.2
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
static ecma_value_t
ecma_builtin_string_object_from_char_code (ecma_value_t this_arg, /**< 'this' argument */
                                           const ecma_value_t args[], /**< arguments list */
                                           ecma_length_t args_number) /**< number of arguments */
{
  JERRY_UNUSED (this_arg);

  if (args_number == 0)
  {
    return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY);
  }

  ecma_value_t ret_value = ECMA_VALUE_EMPTY;
  ecma_string_t *ret_string_p = NULL;
  lit_utf8_size_t utf8_buf_size = args_number * LIT_CESU8_MAX_BYTES_IN_CODE_UNIT;

  JMEM_DEFINE_LOCAL_ARRAY (utf8_buf_p,
                           utf8_buf_size,
                           lit_utf8_byte_t);

  lit_utf8_size_t utf8_buf_used = 0;

  for (ecma_length_t arg_index = 0;
       arg_index < args_number && ecma_is_value_empty (ret_value);
       arg_index++)
  {
    ECMA_OP_TO_NUMBER_TRY_CATCH (arg_num, args[arg_index], ret_value);

    uint32_t uint32_char_code = ecma_number_to_uint32 (arg_num);
    ecma_char_t code_unit = (uint16_t) uint32_char_code;

    JERRY_ASSERT (utf8_buf_used <= utf8_buf_size - LIT_UTF8_MAX_BYTES_IN_CODE_UNIT);
    utf8_buf_used += lit_code_unit_to_utf8 (code_unit, utf8_buf_p + utf8_buf_used);
    JERRY_ASSERT (utf8_buf_used <= utf8_buf_size);

    ECMA_OP_TO_NUMBER_FINALIZE (arg_num);
  }

  if (ecma_is_value_empty (ret_value))
  {
    ret_string_p = ecma_new_ecma_string_from_utf8 (utf8_buf_p, utf8_buf_used);
  }

  JMEM_FINALIZE_LOCAL_ARRAY (utf8_buf_p);

  if (ecma_is_value_empty (ret_value))
  {
    ret_value = ecma_make_string_value (ret_string_p);
  }

  return ret_value;
} /* ecma_builtin_string_object_from_char_code */
/**
 * The Function.prototype object's 'toString' routine
 *
 * See also:
 *          ECMA-262 v5, 15.3.4.2
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
static ecma_value_t
ecma_builtin_function_prototype_object_to_string (ecma_value_t this_arg) /**< this argument */
{
  ecma_value_t ret_value = ECMA_VALUE_EMPTY;

  if (!ecma_op_is_callable (this_arg))
  {
    ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a function."));
  }
  else
  {
    ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__FUNCTION_TO_STRING);
  }
  return ret_value;
} /* ecma_builtin_function_prototype_object_to_string */
/**
 * List names of a String object's lazy instantiated properties
 *
 * @return string values collection
 */
void
ecma_op_string_list_lazy_property_names (ecma_object_t *obj_p, /**< a String object */
                                         bool separate_enumerable, /**< true -  list enumerable properties
                                                                    *           into main collection,
                                                                    *           and non-enumerable to collection of
                                                                    *           'skipped non-enumerable' properties,
                                                                    *   false - list all properties into main
                                                                    *           collection.
                                                                    */
                                         ecma_collection_header_t *main_collection_p, /**< 'main'
                                                                                       *   collection */
                                         ecma_collection_header_t *non_enum_collection_p) /**< skipped
                                                                                           *   'non-enumerable'
                                                                                           *   collection */
{
  JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_CLASS);

  ecma_collection_header_t *for_enumerable_p = main_collection_p;

  ecma_collection_header_t *for_non_enumerable_p = separate_enumerable ? non_enum_collection_p : main_collection_p;

  ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p;
  JERRY_ASSERT (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_STRING_UL);

  ecma_string_t *prim_value_str_p = ecma_get_string_from_value (ext_object_p->u.class_prop.u.value);

  ecma_length_t length = ecma_string_get_length (prim_value_str_p);

  for (ecma_length_t i = 0; i < length; i++)
  {
    ecma_string_t *name_p = ecma_new_ecma_string_from_uint32 (i);

    /* the properties are enumerable (ECMA-262 v5, 15.5.5.2.9) */
    ecma_append_to_values_collection (for_enumerable_p, ecma_make_string_value (name_p), 0);

    ecma_deref_ecma_string (name_p);
  }

  ecma_append_to_values_collection (for_non_enumerable_p,
                                    ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH),
                                    0);
} /* ecma_op_string_list_lazy_property_names */
/**
 * String object creation operation.
 *
 * See also: ECMA-262 v5, 15.5.2.1
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value
 */
ecma_value_t
ecma_op_create_string_object (const ecma_value_t *arguments_list_p, /**< list of arguments that
                                                                         are passed to String constructor */
                              ecma_length_t arguments_list_len) /**< length of the arguments' list */
{
  JERRY_ASSERT (arguments_list_len == 0
                || arguments_list_p != NULL);

  ecma_value_t prim_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY);

  if (arguments_list_len > 0)
  {
    prim_value = ecma_op_to_string (arguments_list_p[0]);

    if (ECMA_IS_VALUE_ERROR (prim_value))
    {
      return prim_value;
    }

    JERRY_ASSERT (ecma_is_value_string (prim_value));
  }

#ifndef CONFIG_DISABLE_STRING_BUILTIN
  ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_STRING_PROTOTYPE);
#else /* CONFIG_DISABLE_STRING_BUILTIN */
  ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE);
#endif /* !CONFIG_DISABLE_STRING_BUILTIN */

  ecma_object_t *object_p = ecma_create_object (prototype_obj_p,
                                                sizeof (ecma_extended_object_t),
                                                ECMA_OBJECT_TYPE_CLASS);

  ecma_deref_object (prototype_obj_p);

  ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
  ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_STRING_UL;
  ext_object_p->u.class_prop.u.value = prim_value;

  return ecma_make_object_value (object_p);
} /* ecma_op_create_string_object */
/**
 * Dispatcher of the built-in's routines
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
ecma_value_t
ecma_builtin_date_prototype_dispatch_routine (uint16_t builtin_routine_id, /**< built-in wide routine
                                                                            *   identifier */
                                              ecma_value_t this_arg, /**< 'this' argument value */
                                              const ecma_value_t arguments_list[], /**< list of arguments
                                                                                    *   passed to routine */
                                              ecma_length_t arguments_number) /**< length of arguments' list */
{
  if (JERRY_UNLIKELY (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_JSON))
  {
    return ecma_builtin_date_prototype_to_json (this_arg);
  }

  if (!ecma_is_value_object (this_arg)
      || !ecma_object_class_is (ecma_get_object_from_value (this_arg), LIT_MAGIC_STRING_DATE_UL))
  {
    return ecma_raise_type_error (ECMA_ERR_MSG ("Date object expected"));
  }

  ecma_object_t *object_p = ecma_get_object_from_value (this_arg);

  ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
  ecma_number_t *prim_value_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t,
                                                                 ext_object_p->u.class_prop.u.value);

  if (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_TIME)
  {
    return ecma_make_number_value (*prim_value_p);
  }

  if (builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_TIME)
  {
    ecma_value_t time = (arguments_number >= 1 ? arguments_list[0]
                                               : ECMA_VALUE_UNDEFINED);

    ecma_value_t ret_value = ECMA_VALUE_EMPTY;

    /* 1. */
    ECMA_OP_TO_NUMBER_TRY_CATCH (time_num, time, ret_value);
    *prim_value_p = ecma_date_time_clip (time_num);

    ret_value = ecma_make_number_value (time_num);
    ECMA_OP_TO_NUMBER_FINALIZE (time_num);

    return ret_value;
  }

  if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_SET_UTC_MILLISECONDS)
  {
    ecma_number_t this_num = *prim_value_p;

    if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id))
    {
      this_num += ecma_date_local_time_zone (this_num);
    }

    if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET)
    {
      return ecma_builtin_date_prototype_dispatch_get (builtin_routine_id, this_num);
    }

    return ecma_builtin_date_prototype_dispatch_set (builtin_routine_id,
                                                     ext_object_p,
                                                     this_num,
                                                     arguments_list,
                                                     arguments_number);
  }

  if (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_ISO_STRING)
  {
    if (ecma_number_is_nan (*prim_value_p) || ecma_number_is_infinity (*prim_value_p))
    {
      return ecma_raise_range_error (ECMA_ERR_MSG ("Date must be a finite number."));
    }

    return ecma_date_value_to_iso_string (*prim_value_p);
  }

  if (ecma_number_is_nan (*prim_value_p))
  {
    return ecma_make_magic_string_value (LIT_MAGIC_STRING_INVALID_DATE_UL);
  }

  switch (builtin_routine_id)
  {
    case ECMA_DATE_PROTOTYPE_TO_STRING:
    {
      return ecma_date_value_to_string (*prim_value_p);
    }
    case ECMA_DATE_PROTOTYPE_TO_DATE_STRING:
    {
      return ecma_date_value_to_date_string (*prim_value_p);
    }
    case ECMA_DATE_PROTOTYPE_TO_TIME_STRING:
    {
      return ecma_date_value_to_time_string (*prim_value_p);
    }
    default:
    {
      JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_UTC_STRING);
      return ecma_date_value_to_utc_string (*prim_value_p);
    }
  }
} /* ecma_builtin_date_prototype_dispatch_routine */
/**
 * Dispatch get date functions
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
static ecma_value_t
ecma_builtin_date_prototype_dispatch_get (uint16_t builtin_routine_id, /**< built-in wide routine
                                                                        *   identifier */
                                          ecma_number_t date_num) /**< date converted to number */
{
  if (ecma_number_is_nan (date_num))
  {
    return ecma_make_magic_string_value (LIT_MAGIC_STRING_NAN);
  }

  switch (builtin_routine_id)
  {
    case ECMA_DATE_PROTOTYPE_GET_FULL_YEAR:
    case ECMA_DATE_PROTOTYPE_GET_UTC_FULL_YEAR:
#ifndef CONFIG_DISABLE_ANNEXB_BUILTIN
    case ECMA_DATE_PROTOTYPE_GET_YEAR:
#endif /* !CONFIG_DISABLE_ANNEXB_BUILTIN */
    {
      date_num = ecma_date_year_from_time (date_num);

#ifndef CONFIG_DISABLE_ANNEXB_BUILTIN
      if (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_YEAR)
      {
        date_num -= 1900;
      }
#endif /* !CONFIG_DISABLE_ANNEXB_BUILTIN */

      break;
    }
    case ECMA_DATE_PROTOTYPE_GET_MONTH:
    case ECMA_DATE_PROTOTYPE_GET_UTC_MONTH:
    {
      date_num = ecma_date_month_from_time (date_num);
      break;
    }
    case ECMA_DATE_PROTOTYPE_GET_DATE:
    case ECMA_DATE_PROTOTYPE_GET_UTC_DATE:
    {
      date_num = ecma_date_date_from_time (date_num);
      break;
    }
    case ECMA_DATE_PROTOTYPE_GET_DAY:
    case ECMA_DATE_PROTOTYPE_GET_UTC_DAY:
    {
      date_num = ecma_date_week_day (date_num);
      break;
    }
    case ECMA_DATE_PROTOTYPE_GET_HOURS:
    case ECMA_DATE_PROTOTYPE_GET_UTC_HOURS:
    {
      date_num = ecma_date_hour_from_time (date_num);
      break;
    }
    case ECMA_DATE_PROTOTYPE_GET_MINUTES:
    case ECMA_DATE_PROTOTYPE_GET_UTC_MINUTES:
    {
      date_num = ecma_date_min_from_time (date_num);
      break;
    }
    case ECMA_DATE_PROTOTYPE_GET_SECONDS:
    case ECMA_DATE_PROTOTYPE_GET_UTC_SECONDS:
    {
      date_num = ecma_date_sec_from_time (date_num);
      break;
    }
    case ECMA_DATE_PROTOTYPE_GET_MILLISECONDS:
    case ECMA_DATE_PROTOTYPE_GET_UTC_MILLISECONDS:
    {
      date_num = ecma_date_ms_from_time (date_num);
      break;
    }
    default:
    {
      JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET);
      date_num = ecma_date_timezone_offset (date_num);
      break;
    }
  }

  return ecma_make_number_value (date_num);
} /* ecma_builtin_date_prototype_dispatch_get */