/**
 * @file tees_client_log_android.c
 * @brief tees_log interface implementation for Android and Tizen
 * @author Iaroslav Makarchuk (i.makarchuk@samsung.com)
 * @date Created Oct 3, 2016
 * @par In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include <tees_log.h>

#include <stdarg.h>
#include <stdio.h>

#define DUMP_ROW_LEN 16
#define NAME_UNKNOWN "Unknown"
#define LOG_BUF_SIZE 1024

#ifdef USE_TIZEN
  /* Should be defined for dlog.h to do short log calls (without filenames) */
  #define __MODULE__ ""

  #include "dlog.h"

  #define PLATFORM_LOG_UNKNOWN  DLOG_UNKNOWN
  #define PLATFORM_LOG_DEFAULT  DLOG_DEFAULT
  #define PLATFORM_LOG_VERBOSE  DLOG_VERBOSE
  #define PLATFORM_LOG_DEBUG    DLOG_DEBUG
  #define PLATFORM_LOG_INFO     DLOG_INFO
  #define PLATFORM_LOG_WARN     DLOG_WARN
  #define PLATFORM_LOG_ERROR    DLOG_ERROR
  #define PLATFORM_LOG_FATAL    DLOG_FATAL

  #define PLATFORM_LOG_WRITE    dlog_print
#else /* USE_TIZEN */
  #include <android/log.h>

  #define PLATFORM_LOG_UNKNOWN  ANDROID_LOG_UNKNOWN
  #define PLATFORM_LOG_DEFAULT  ANDROID_LOG_DEFAULT
  #define PLATFORM_LOG_VERBOSE  ANDROID_LOG_VERBOSE
  #define PLATFORM_LOG_DEBUG    ANDROID_LOG_DEBUG
  #define PLATFORM_LOG_INFO     ANDROID_LOG_INFO
  #define PLATFORM_LOG_WARN     ANDROID_LOG_WARN
  #define PLATFORM_LOG_ERROR    ANDROID_LOG_ERROR
  #define PLATFORM_LOG_FATAL    ANDROID_LOG_FATAL

  #define PLATFORM_LOG_WRITE    __android_log_write
#endif /* USE_TIZEN */

TEES_LogLevel g_tees_log_level = TEES_LOG_LEVEL_DEFAULT;

void TEES_LogSetTarget(TEES_LogTarget target,
                          const void *param,
                          uint32_t param_len) {
  (void) param;
  (void) param_len;
  (void) target;
}

void TEES_LogSetLevel(TEES_LogLevel level) {
  if (level <= TEES_LOG_LEVEL_MAX) {
    g_tees_log_level = level;
  }
}

static int GetAndroidLogPrio(TEES_LogLevel level)
{
  switch (level) {
    default:
    case TEES_LOG_LEVEL_NONE: return PLATFORM_LOG_UNKNOWN;
    case TEES_LOG_LEVEL_DEFAULT: return PLATFORM_LOG_DEFAULT;
    case TEES_LOG_LEVEL_VERBOSE: return PLATFORM_LOG_VERBOSE;
    case TEES_LOG_LEVEL_DEBUG: return PLATFORM_LOG_DEBUG;
    case TEES_LOG_LEVEL_INFO: return PLATFORM_LOG_INFO;
    case TEES_LOG_LEVEL_WARNING: return PLATFORM_LOG_WARN;
    case TEES_LOG_LEVEL_ERROR: return PLATFORM_LOG_ERROR;
    case TEES_LOG_LEVEL_CRITICAL:
    case TEES_LOG_LEVEL_MAX: return PLATFORM_LOG_FATAL;
  }

  /* default return to avoid compiling errors */
  return PLATFORM_LOG_UNKNOWN;
}

void TEES_Log(TEES_LogLevel level, const char *function_name, int line,
                 const char *project_name, const char *fmt, ...)
{
  char buf[LOG_BUF_SIZE] = {0};
  int written_len = 0;
  va_list vl;

  if (level < g_tees_log_level || level > TEES_LOG_LEVEL_MAX) {
    return;
  }

  written_len = snprintf(buf, sizeof(buf), "[%s:%d] ",
                         function_name ? function_name : NAME_UNKNOWN, line);

  if (written_len > 0 && (unsigned long)written_len < sizeof(buf)) {
    va_start(vl, fmt);
    vsnprintf(buf + written_len, sizeof(buf) - written_len, fmt, vl);
    va_end(vl);
    PLATFORM_LOG_WRITE(GetAndroidLogPrio(level),
                        project_name ? project_name : NAME_UNKNOWN, buf);
  }
}

void TEES_LogMem(TEES_LogLevel level, const char *function_name, int line,
                    const char *project_name, const void *data,
                    uint32_t data_len)
{
  char buf[LOG_BUF_SIZE] = {0};
  uint32_t written_len = 0;
  char *source_ptr = (char *)data;

  if (level < g_tees_log_level || level > TEES_LOG_LEVEL_MAX || !data) {
    return;
  }

  written_len = snprintf(buf, sizeof(buf), "[%s:%d] [MEM]: %p (%u)\n",
                         function_name ? function_name : NAME_UNKNOWN,
                         line, data, data_len);
  if (written_len) {
    PLATFORM_LOG_WRITE(GetAndroidLogPrio(level),
                        project_name ? project_name : NAME_UNKNOWN, buf);
  }

  written_len = 0;

  while (written_len < data_len) {
    char *dest_ptr = buf;
    uint32_t bytes_to_write = data_len - written_len > DUMP_ROW_LEN ?
                              DUMP_ROW_LEN : data_len - written_len;

    written_len += bytes_to_write;

    while (bytes_to_write--) {
      dest_ptr += snprintf(dest_ptr, sizeof(buf) - (dest_ptr - buf),
                           "%02X", *(source_ptr++));
    }

    PLATFORM_LOG_WRITE(GetAndroidLogPrio(level),
                        project_name ? project_name : NAME_UNKNOWN, buf);
  }
}
