// Copyright 2019 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "aiserver.h"
#include "getopt.h"
#include "logger/log.h"
#include "ai_scene_director.h"
#include "nn_vision_rockx.h"

#define HAVE_SIGNAL_PROC 1

typedef struct _AIServerCtx {
    // minilog flags
    int  mFlagMinilog;
    int  mFlagMinilogBacktrace;
    int  mFlagMinilogLevel;
    // dbus flags
    int  mFlagDBusServer;
    int  mFlagDBusDbServer;
    int  mFlagDBusConn;
    // server flags
    bool mQuit;
    std::string mConfigUri;
} AIServerCtx;

static AIServerCtx mAIServerCtx;
static void        parse_args(int argc, char **argv);

namespace rockchip {
namespace aiserver {

AIServer::AIServer() {
    mAIDirector.reset(nullptr);
    mDbusServer.reset(nullptr);
}

void AIServer::setupTaskGraph() {
    mAIDirector.reset(new AISceneDirector());

    LOG_INFO("AIServer: mFlagDBusServer   = %d\n", mAIServerCtx.mFlagDBusServer);
    LOG_INFO("AIServer: mFlagDBusDbServer = %d\n", mAIServerCtx.mFlagDBusDbServer);
    if (mAIServerCtx.mFlagDBusServer) {
        mDbusServer.reset(new DBusServer(mAIServerCtx.mFlagDBusConn, mAIServerCtx.mFlagDBusDbServer));
        assert(mDbusServer);
        mDbusServer->RegisterMediaControl(mAIDirector.get());
        mDbusServer->start();
        LOG_INFO("AIServer: RegisterMediaControl start ok\n");
    }

    mAIDirector->setup();
}

AIServer::~AIServer() {
    if ((mAIServerCtx.mFlagDBusServer) && (nullptr != mDbusServer)) {
        mDbusServer->stop();
    }
    mAIDirector.reset();
    mDbusServer.reset();
}

void AIServer::interrupt() {
    mAIDirector->stop(RT_APP_NN);
    mAIDirector->stop(RT_APP_UVC);
    mAIDirector->interrupt();
}

void AIServer::waitUntilDone() {
    mAIDirector->waitUntilDone();
}

} // namespace aiserver
} // namespace rockchip


//=======================================================================//
//=========================AIServer Main Program=========================//
//=======================================================================//
using namespace rockchip::aiserver;
static AIServer* mAIServerInstance = nullptr;

static void sigterm_handler(int sig) {
    LOG_WARN("quit signal(%d) is caught\n", sig);
    mAIServerCtx.mQuit = true;
    if (nullptr != mAIServerInstance) {
        mAIServerInstance->interrupt();
    }
}

int main(int argc, char *argv[]) {
    mAIServerCtx.mQuit             = false;
    mAIServerCtx.mFlagDBusServer   = true;
    mAIServerCtx.mFlagDBusDbServer = false;
    mAIServerCtx.mFlagDBusConn     = false;

    parse_args(argc, argv);

    LOG_INFO("parse_args done!\n");

    // __minilog_log_init(argv[0], NULL, false, mAIServerCtx.mFlagMinilogBacktrace,
    //                   argv[0], "1.0.0");

    // install signal handlers.
#if HAVE_SIGNAL_PROC
    signal(SIGINT,  sigterm_handler);  // SIGINT  = 2
    signal(SIGQUIT, sigterm_handler);  // SIGQUIT = 3
    signal(SIGTERM, sigterm_handler);  // SIGTERM = 15
    signal(SIGXCPU, sigterm_handler);  // SIGXCPU = 24
    signal(SIGPIPE, SIG_IGN);          // SIGPIPE = 13 is ingnored
#endif

    mAIServerInstance = new AIServer();
    LOG_INFO("create aiserver instance done\n");

    // rt_mem_record_reset();
    mAIServerInstance->setupTaskGraph();
    LOG_INFO("aiserver->setupTaskGraph(); done\n");

    while (!mAIServerCtx.mQuit) {
        usleep(2000000ll);
    }

    mAIServerInstance->waitUntilDone();
    LOG_INFO("aiserver->waitUntilDone(); done\n");

    delete mAIServerInstance;
    mAIServerInstance = nullptr;

    rt_mem_record_dump();
    return 0;
}

static void usage_tip(FILE *fp, int argc, char **argv) {
  fprintf(fp, "Usage: %s [options]\n"
              "Version %s\n"
              "Options:\n"
              "-c | --config      AIServer confg file \n"
              "-o | --dbus_conn   0:system,  1:session \n"
              "-d | --dbus_db     0:disable, 1:enable \n"
              "-s | --dbus_server 0:disable, 1:enable \n"
              "-h | --help        For help \n"
              "\n",
          argv[0], "V1.1");
}

static const char short_options[] = "c:odsh";
static const struct option long_options[] = {
    {"ai_config",   required_argument, NULL, 'c'},
    {"dbus_conn",   optional_argument, 0,    'o'},
    {"dbus_db",     optional_argument, 0,    'd'},
    {"dbus_server", optional_argument, 0,    's'},
    {"help",        no_argument,       0,    'h'},
    {0, 0, 0, 0}
};

static void parse_args(int argc, char **argv) {
    int opt;
    int idx;
    while ((opt = getopt_long(argc, argv, short_options, long_options, &idx))!= -1) {
      switch (opt) {
        case 0: /* getopt_long() flag */
          break;
        case 'c':
          mAIServerCtx.mConfigUri = optarg;
          break;
        case 'o':
          mAIServerCtx.mFlagDBusConn  = atoi(argv[optind]);
          break;
        case 'd':
          mAIServerCtx.mFlagDBusDbServer = atoi(argv[optind]);
          break;
        case 's':
          mAIServerCtx.mFlagDBusServer = atoi(argv[optind]);
          break;
        case 'h':
          usage_tip(stdout, argc, argv);
          exit(EXIT_SUCCESS);
        default:
          usage_tip(stderr, argc, argv);
          exit(EXIT_FAILURE);
      }
    }
}