/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * 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.
 */

#define LOG_TAG "perfstatsd"

#include <perfstatsd.h>
#include <perfstatsd_service.h>

enum MODE { DUMP_HISTORY, SET_OPTION };

android::sp<Perfstatsd> perfstatsdSp;

void *perfstatsdMain(void *) {
    LOG(INFO) << "main thread started";
    perfstatsdSp = new Perfstatsd();

    while (true) {
        perfstatsdSp->refresh();
        perfstatsdSp->pause();
    }
    return NULL;
}

void help(char **argv) {
    std::string usage = argv[0];
    usage = "Usage: " + usage + " [-s][-d][-o]\n" +
            "Options:\n"
            "    -s, start as service\n"
            "    -d, dump perf stats history for dumpstate_board\n"
            "    -o, set key/value option";

    fprintf(stderr, "%s\n", usage.c_str());
}

int startService(void) {
    pthread_t perfstatsdMainThread;
    errno = pthread_create(&perfstatsdMainThread, NULL, perfstatsdMain, NULL);
    if (errno != 0) {
        PLOG(ERROR) << "Failed to create main thread";
        return -1;
    } else {
        pthread_setname_np(perfstatsdMainThread, "perfstatsd_main");
    }

    android::ProcessState::initWithDriver("/dev/vndbinder");

    if (PerfstatsdPrivateService::start() != android::OK) {
        PLOG(ERROR) << "Failed to start perfstatsd service";
        return -1;
    } else
        LOG(INFO) << "perfstatsd_pri_service started";

    android::ProcessState::self()->startThreadPool();
    android::IPCThreadState::self()->joinThreadPool();
    pthread_join(perfstatsdMainThread, NULL);
    return 0;
}

int serviceCall(int mode, const std::string &key, const std::string &value) {
    android::ProcessState::initWithDriver("/dev/vndbinder");

    android::sp<IPerfstatsdPrivate> perfstatsdPrivateService = getPerfstatsdPrivateService();
    if (perfstatsdPrivateService == NULL) {
        PLOG(ERROR) << "Cannot find perfstatsd service.";
        fprintf(stdout, "Cannot find perfstatsd service.\n");
        return -1;
    }

    switch (mode) {
        case DUMP_HISTORY: {
            std::string history;
            LOG(INFO) << "dump perfstats history.";
            if (!perfstatsdPrivateService->dumpHistory(&history).isOk() || history.empty()) {
                PLOG(ERROR) << "perf stats history is not available";
                fprintf(stdout, "perf stats history is not available\n");
                return -1;
            }
            fprintf(stdout, "%s\n", history.c_str());
            break;
        }
        case SET_OPTION:
            LOG(INFO) << "set option: " << key << " , " << value;
            if (!perfstatsdPrivateService
                     ->setOptions(std::forward<const std::string>(key),
                                  std::forward<const std::string>(value))
                     .isOk()) {
                PLOG(ERROR) << "fail to set options";
                fprintf(stdout, "fail to set options\n");
                return -1;
            }
            break;
    }
    return 0;
}

int serviceCall(int mode) {
    std::string empty("");
    return serviceCall(mode, empty, empty);
}

int main(int argc, char **argv) {
    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));

    int c;
    while ((c = getopt(argc, argv, "sdo:h")) != -1) {
        switch (c) {
            case 's':
                return startService();
            case 'd':
                return serviceCall(DUMP_HISTORY);
            case 'o':
                // set options
                if (argc == 4) {
                    std::string key(argv[2]);
                    std::string value(argv[3]);
                    return serviceCall(SET_OPTION, std::move(key), std::move(value));
                }
                FALLTHROUGH_INTENDED;
            case 'h':
                // print usage
                FALLTHROUGH_INTENDED;
            default:
                help(argv);
                return 2;
        }
    }
    return 0;
}