/*
 * Licensed to Open Source Radio Access Network(OS-RAN) Alliance and OAI
 * Software Alliance under one or more contributor license agreements. The
 * initial OpenXG series projects are derided from OAI projects, the files from
 * OAI projects are all in compliance with OAI Public License, Version 1.1.
 * codes and files developed from OpenXG projects and from OS-RAN Alliance
 * are all under OS-RAN licenses; you may not use this file except in compliance
 * with the license.  You may get a copy of the license at:
 *	http://www.openxg.org.cn/?falu_69.html
 * For more information about OpenXG, please contact:
 * contact@openxg.org.cn
 */


#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>

#include "itti.hpp"
#include "async_shell_cmd.hpp"
#include "itti_async_shell_cmd.hpp"
#include "logger.hpp"
#include "common_defs.h"

#include <stdexcept>

using namespace util;

extern itti_mw* itti_inst;
void async_cmd_task(void*);

//------------------------------------------------------------------------------
void async_cmd_task(void* args_p) {
  const task_id_t task_id = TASK_ASYNC_SHELL_CMD;

  const thread_sched_params* const sched_params =
      (const util::thread_sched_params* const) args_p;
  sched_params->apply(task_id, Logger::async_cmd());

  itti_inst->notify_task_ready(task_id);

  do {
    std::shared_ptr<itti_msg> shared_msg = itti_inst->receive_msg(task_id);
    auto* msg                            = shared_msg.get();
    switch (msg->msg_type) {
      case ASYNC_SHELL_CMD:
        if (itti_async_shell_cmd* to =
                dynamic_cast<itti_async_shell_cmd*>(msg)) {
          int rc = system((const char*) to->system_command.c_str());

          if (rc) {
            Logger::async_cmd().error(
                "Failed cmd from %d: %s ", to->origin,
                (const char*) to->system_command.c_str());
            if (to->is_abort_on_error) {
              Logger::async_cmd().error(
                  "Terminate cause failed cmd %s at %s:%d",
                  to->system_command.c_str(), to->src_file.c_str(),
                  to->src_line);
              itti_inst->send_terminate_msg(to->origin);
            }
          }
        }
        break;

      case TIME_OUT:
        if (itti_msg_timeout* to = dynamic_cast<itti_msg_timeout*>(msg)) {
          Logger::async_cmd().info("TIME-OUT event timer id %d", to->timer_id);
        }
        break;

      case TERMINATE:
        if (itti_msg_terminate* terminate =
                dynamic_cast<itti_msg_terminate*>(msg)) {
          Logger::async_cmd().info("Received terminate message");
          return;
        }
        break;

      case HEALTH_PING:
        break;

      default:
        Logger::smf_app().info("no handler for msg type %d", msg->msg_type);
    }

  } while (true);
}

//------------------------------------------------------------------------------
async_shell_cmd::async_shell_cmd(util::thread_sched_params& sched_params) {
  Logger::async_cmd().startup("Starting...");

  if (itti_inst->create_task(
          TASK_ASYNC_SHELL_CMD, async_cmd_task, &sched_params)) {
    Logger::async_cmd().error("Cannot create task TASK_ASYNC_SHELL_CMD");
    throw std::runtime_error("Cannot create task TASK_ASYNC_SHELL_CMD");
  }
  Logger::async_cmd().startup("Started");
}

//------------------------------------------------------------------------------
int async_shell_cmd::run_command(
    const task_id_t sender_itti_task, const bool is_abort_on_error,
    const char* src_file, const int src_line, const std::string& cmd_str) {
  itti_async_shell_cmd cmd(
      sender_itti_task, TASK_ASYNC_SHELL_CMD, cmd_str, is_abort_on_error,
      src_file, src_line);
  std::shared_ptr<itti_async_shell_cmd> msg =
      std::make_shared<itti_async_shell_cmd>(cmd);
  int ret = itti_inst->send_msg(msg);
  if (RETURNok != ret) {
    Logger::async_cmd().error(
        "Could not send ITTI message to task TASK_ASYNC_SHELL_CMD");
    return RETURNerror;
  }
  return RETURNok;
}
