/* Copyright 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "opcodes-ecma-support.h"

#include "globals.h"
#include "interpreter.h"
#include "opcodes.h"

#include "opcodes-native-call.h"

#include "actuators.h"
#include "common-io.h"
#include "sensors.h"
#include "jerry-libc.h"

/**
 * 'Native call' opcode handler.
 */
ecma_completion_value_t
opfunc_native_call (opcode_t opdata, /**< operation data */
                    int_data_t *int_data) /**< interpreter context */
{
  // const idx_t dst_var_idx = opdata.data.native_call.lhs;
  const idx_t native_call_id_idx = opdata.data.native_call.name;
  const idx_t args_number = opdata.data.native_call.arg_list;

  JERRY_ASSERT (native_call_id_idx < OPCODE_NATIVE_CALL__COUNT);

  int_data->pos++;

  JERRY_STATIC_ASSERT (OPCODE_NATIVE_CALL__COUNT < (1u << (sizeof (native_call_id_idx) * JERRY_BITSINBYTE)));

  ecma_value_t arg_values[args_number + 1 /* length of array should not be zero */];

  ecma_completion_value_t ret_value;

  ecma_length_t args_read;
  ecma_completion_value_t get_arg_completion = fill_varg_list (int_data,
                                                               args_number,
                                                               arg_values,
                                                               &args_read);

  if (ecma_is_completion_value_empty (get_arg_completion))
  {
    JERRY_ASSERT (args_read == args_number);

    switch ((opcode_native_call_t)native_call_id_idx)
    {
      case OPCODE_NATIVE_CALL_LED_TOGGLE:
      {
        JERRY_ASSERT (args_number == 1);
        JERRY_ASSERT (arg_values[0].value_type == ECMA_TYPE_NUMBER);
        ecma_number_t* num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_values[0].value);
        uint32_t int_num = ecma_number_to_uint32 (*num_p);
        led_toggle (int_num);

        ret_value = ecma_make_empty_completion_value ();
        break;
      }
      case OPCODE_NATIVE_CALL_LED_ON:
      {
        JERRY_ASSERT (args_number == 1);
        JERRY_ASSERT (arg_values[0].value_type == ECMA_TYPE_NUMBER);
        ecma_number_t* num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_values[0].value);
        uint32_t int_num = ecma_number_to_uint32 (*num_p);
        led_on (int_num);

        ret_value = ecma_make_empty_completion_value ();
        break;
      }
      case OPCODE_NATIVE_CALL_LED_OFF:
      {
        JERRY_ASSERT (args_number == 1);
        JERRY_ASSERT (arg_values[0].value_type == ECMA_TYPE_NUMBER);
        ecma_number_t* num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_values[0].value);
        uint32_t int_num = ecma_number_to_uint32 (*num_p);
        led_off (int_num);

        ret_value = ecma_make_empty_completion_value ();
        break;
      }
      case OPCODE_NATIVE_CALL_LED_ONCE:
      {
        JERRY_ASSERT (args_number == 1);
        JERRY_ASSERT (arg_values[0].value_type == ECMA_TYPE_NUMBER);
        ecma_number_t* num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_values[0].value);
        uint32_t int_num = ecma_number_to_uint32 (*num_p);
        led_blink_once (int_num);

        ret_value = ecma_make_empty_completion_value ();
        break;
      }
      case OPCODE_NATIVE_CALL_WAIT:
      {
        JERRY_ASSERT (args_number == 1);
        JERRY_ASSERT (arg_values[0].value_type == ECMA_TYPE_NUMBER);
        ecma_number_t* num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_values[0].value);
        uint32_t int_num = ecma_number_to_uint32 (*num_p);
        wait_ms (int_num);

        ret_value = ecma_make_empty_completion_value ();
        break;
      }

      case OPCODE_NATIVE_CALL_PRINT:
      {
        JERRY_ASSERT (args_number == 1);

        ECMA_TRY_CATCH (str_value,
                        ecma_op_to_string (arg_values[0]),
                        ret_value);

        ecma_string_t *str_p = ECMA_GET_POINTER (str_value.u.value.value);

        int32_t chars = ecma_string_get_length (str_p);
        JERRY_ASSERT (chars >= 0);

        ssize_t zt_str_size = (ssize_t) sizeof (ecma_char_t) * (chars + 1);
        ecma_char_t *zt_str_p = mem_heap_alloc_block ((size_t) zt_str_size,
                                                      MEM_HEAP_ALLOC_SHORT_TERM);
        if (zt_str_p == NULL)
        {
          jerry_exit (ERR_OUT_OF_MEMORY);
        }

        ecma_string_to_zt_string (str_p, zt_str_p, zt_str_size);

#if defined (CONFIG_ECMA_CHAR_ASCII)
        __printf ("%s\n", (char*) zt_str_p);
#elif defined (CONFIG_ECMA_CHAR_UTF16)
        JERRY_UNIMPLEMENTED ();
#else /* !CONFIG_ECMA_CHAR_ASCII && !CONFIG_ECMA_CHAR_UTF16 */
# error "!CONFIG_ECMA_CHAR_ASCII && !CONFIG_ECMA_CHAR_UTF16"
#endif /* !CONFIG_ECMA_CHAR_ASCII && !CONFIG_ECMA_CHAR_UTF16 */

        mem_heap_free_block (zt_str_p);

        ret_value = ecma_make_empty_completion_value ();

        ECMA_FINALIZE (str_value);

        break;
      }

      case OPCODE_NATIVE_CALL__COUNT:
      {
        JERRY_UNREACHABLE ();
      }
    }
  }
  else
  {
    JERRY_ASSERT (!ecma_is_completion_value_normal (get_arg_completion));

    ret_value = get_arg_completion;
  }

  for (ecma_length_t arg_index = 0;
       arg_index < args_read;
       arg_index++)
  {
    ecma_free_value (arg_values[arg_index], true);
  }

  return ret_value;
} /* opfunc_native_call */
