<reponame>steveford-obs/rdkservices
/**
* If not stated otherwise in this file or this component's LICENSE
* file the following copyright and licenses apply:
*
* Copyright 2020 RDK Management
*
* 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.
**/

/**
 * @file systemServicesTestClient.cpp
 * @brief Thunder Plugin based Implementation of CPP Test Client for System service API's.
 * @reference RDK-25849.
 */
#include <iostream>
#include <bits/stdc++.h>
#include <ctime>
#include <cassert>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <vector>
#include <map>
#include <string>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <type_traits>
#include <core/core.h>
#include <websocket/websocket.h>
#include <securityagent/SecurityTokenUtil.h>

#define SYSPLUGIN_CALLSIGN		"org.rdk.System"
#define SYSPLUGIN_SERVER_PORT	"127.0.0.1:9998"
#define MAX_LENGTH 1024

using namespace std;
using namespace WPEFramework;

/* Thunder-Security: Security Token */
unsigned char g_ucSecToken[MAX_LENGTH] = {0};
std::string g_strSecToken = "";

uint64_t TimeStamp(void)
{
	return std::chrono::duration_cast<std::chrono::microseconds>
		(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}

std::string makePretty(std::string result)
{
	/* Hack ? - remove the escape characters from the result for aesthetics. */
	result.erase(std::remove(result.begin(), result.end(), '\\'), result.end());
	return result;
}

typedef enum SME_t {
#ifdef DEBUG
	SME_sampleSystemServiceAPI,
#endif /* DEBUG */
	SME_cacheContains = 1,
	SME_clearLastDeepSleepReason,
	SME_enableMoca,
	SME_getAvailableStandbyModes,
	SME_getCachedValue,
	SME_getCoreTemperature,
	SME_getDeviceInfo,
	SME_getDownloadedFirmwareInfo,
	SME_getFirmwareDownloadPercent,
	SME_getFirmwareUpdateInfo,
	SME_getFirmwareUpdateState,
	SME_getLastDeepSleepReason,
	SME_getMacAddresses,
	SME_getMilestones,
	SME_getMode,
	SME_getPowerState,
	SME_getPreferredStandbyMode,
	SME_getPreviousRebootInfo,
	SME_getPreviousRebootInfo2,
	SME_getPreviousRebootReason,
	SME_getRFCConfig,
	SME_getSerialNumber,
	SME_getStateInfo,
	SME_getSystemVersions,
	SME_getTemperatureThresholds,
	SME_getTimeZoneDST,
	SME_getXconfParams,
	SME_hasRebootBeenRequested,
	SME_isGzEnabled,
	SME_queryMocaStatus,
	SME_reboot,
	SME_removeCacheKey,
	SME_requestSystemUptime,
	SME_setCachedValue,
	SME_setDeepSleepTimer,
	SME_setGzEnabled,
	SME_setMode,
	SME_setPowerState,
	SME_setPreferredStandbyMode,
	SME_setTemperatureThresholds,
	SME_setTimeZoneDST,
	SME_updateFirmware,
	SME_MAX,
};

std::map<SME_t, std::string> SMName = {
#ifdef DEBUG
	{SME_sampleSystemServiceAPI, "sampleSystemServiceAPI"},
#endif /* DEBUG */
	{SME_cacheContains, "cacheContains"},
	{SME_clearLastDeepSleepReason, "clearLastDeepSleepReason"},
	{SME_enableMoca, "enableMoca"},
	{SME_getAvailableStandbyModes, "getAvailableStandbyModes"},
	{SME_getCachedValue, "getCachedValue"},
	{SME_getCoreTemperature, "getCoreTemperature"},
	{SME_getDeviceInfo, "getDeviceInfo"},
	{SME_getDownloadedFirmwareInfo, "getDownloadedFirmwareInfo"},
	{SME_getFirmwareDownloadPercent, "getFirmwareDownloadPercent"},
	{SME_getFirmwareUpdateInfo, "getFirmwareUpdateInfo"},
	{SME_getFirmwareUpdateState, "getFirmwareUpdateState"},
	{SME_getLastDeepSleepReason, "getLastDeepSleepReason"},
	{SME_getMacAddresses, "getMacAddresses"},
	{SME_getMilestones, "getMilestones"},
	{SME_getMode, "getMode"},
	{SME_getPowerState, "getPowerState"},
	{SME_getPreferredStandbyMode, "getPreferredStandbyMode"},
	{SME_getPreviousRebootInfo, "getPreviousRebootInfo"},
	{SME_getPreviousRebootInfo2, "getPreviousRebootInfo2"},
	{SME_getPreviousRebootReason, "getPreviousRebootReason"},
	{SME_getRFCConfig, "getRFCConfig"},
	{SME_getSerialNumber, "getSerialNumber"},
	{SME_getStateInfo, "getStateInfo"},
	{SME_getSystemVersions, "getSystemVersions"},
	{SME_getTemperatureThresholds, "getTemperatureThresholds"},
	{SME_getTimeZoneDST, "getTimeZoneDST"},
	{SME_getXconfParams, "getXconfParams"},
	{SME_hasRebootBeenRequested, "hasRebootBeenRequested"},
	{SME_isGzEnabled, "isGzEnabled"},
	{SME_queryMocaStatus, "queryMocaStatus"},
	{SME_reboot, "reboot"},
	{SME_removeCacheKey, "removeCacheKey"},
	{SME_requestSystemUptime, "requestSystemUptime"},
	{SME_setCachedValue, "setCachedValue"},
	{SME_setDeepSleepTimer, "setDeepSleepTimer"},
	{SME_setGzEnabled, "setGzEnabled"},
	{SME_setMode, "setMode"},
	{SME_setPowerState, "setPowerState"},
	{SME_setPreferredStandbyMode, "setPreferredStandbyMode"},
	{SME_setTemperatureThresholds, "setTemperatureThresholds"},
	{SME_setTimeZoneDST, "setTimeZoneDST"},
	{SME_updateFirmware, "updateFirmware"},
	{SME_MAX, "MAX"},
};

bool invokeJSONRPC(JSONRPC::LinkType<Core::JSON::IElement> *remoteObject, std::string method, JsonObject &param, JsonObject &result)
{
	bool ret = false;
	uint32_t retStatus = Core::ERROR_GENERAL;
	std::string response;
	assert(remoteObject != NULL);

	param.ToString(response);
	printf("\ninvokeJSONRPC '%s' with param: '%s'\n", method.c_str(), response.c_str());

	retStatus = remoteObject->Invoke<JsonObject, JsonObject>(5000, _T(method), param, result);
	if (Core::ERROR_NONE != retStatus) {
		printf("\nremoteObject->Invoke '%s' failed [retStatus: 0x%x]\n", method.c_str(), retStatus);
	} else {
		ret = true;
	}
	return ret;
}

/******************************* Begin: Handle Selection *******************************/

#ifdef DEBUG
void sampleSystemServiceAPI(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);
	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}
#endif /* DEBUG */

void cacheContains(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);
	JsonObject parameters, response;
	std::string result;
	std::string key;

	printf("\nKey name to check :");
	std::cin >> key;
	parameters["key"] = key;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void clearLastDeepSleepReason(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);
	JsonObject parameters, response;
	std::string result;
	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void enableMoca(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);
	JsonObject parameters, response;
	std::string result;
	bool enable = false;

	printf("\nEnable ? {value: 0/1} :");
	std::cin >> enable;
	parameters["value"] = enable;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getAvailableStandbyModes(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getCachedValue(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;
	std::string key;

	printf("\nKey name to check :");
	std::cin >> key;
	parameters["key"] = key;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getCoreTemperature(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getDeviceInfo(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;
	JsonArray paramsArray;
	std::string input;

	printf("\nInput 'key(s)' [Type 'end' to finish] :");
	do {
		input.empty();
		std::cin >> input;

		if (!input.length() || (input == "end"))
			break;

		paramsArray.Add(input);
	} while (true);

	/* TODO: "params" or "param" ? */
	parameters["params"] = paramsArray;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getDownloadedFirmwareInfo(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getFirmwareDownloadPercent(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getFirmwareUpdateInfo(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, guid;

	/* GUID is optional. */
	printf("\nGUID (Optional):");
	std::cin >> guid;

	if (guid.length()) {
		parameters["GUID"] = guid;
	}

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getFirmwareUpdateState(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getLastDeepSleepReason(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getMacAddresses(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getMilestones(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getMode(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getPowerState(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getPreferredStandbyMode(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getPreviousRebootInfo(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getPreviousRebootInfo2(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getPreviousRebootReason(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getRFCConfig(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, rfcListStr;
	JsonArray rfcListArray;

	printf("\nInput required RFC List [type 'end' to finish]: ");
	do {
		std::cin >> rfcListStr;
		if (!rfcListStr.length() || (rfcListStr == "end"))
			break;
		rfcListArray.Add(rfcListStr);
	} while (true);

	/* TODO: Documentation updation required. */
	parameters["rfcList"] = rfcListArray;
	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getSerialNumber(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getStateInfo(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, queryState;

	printf("\nInput property state to be queried :");
	std::cin >> queryState;

	parameters["param"] = queryState;
	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getSystemVersions(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getTemperatureThresholds(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getTimeZoneDST(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void getXconfParams(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void hasRebootBeenRequested(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void isGzEnabled(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void queryMocaStatus(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void reboot(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, reason;

	/* Optional: reboot reason. */
	printf("\nInput reboot reason (Optional) :");
	std::cin >> reason;
	if (reason.length()) {
		/* TODO: Update code & Doc to match SM Doc - "reason" with "rebootReason". */
		parameters["rebootReason"] = reason;
	}

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void removeCacheKey(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, key;

	printf("\nInput 'key' to be removed :");
	std::cin >> key;

	parameters["key"] = key;
	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void requestSystemUptime(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setCachedValue(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	JsonObject parameters, response;
	std::string result, key, value;

	printf("\nInput cache 'key' :");
	std::cin >> key;
	printf("\nInput cache 'value' :");
	std::cin >> value;

	parameters["key"] = key;
	parameters["value"] = value;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setDeepSleepTimer(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;
	unsigned int seconds = 0;

	printf("\nInput Seconds :");
	std::cin >> seconds;

	parameters["seconds"] = seconds;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setGzEnabled(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;
	bool enabled = false;

	/* TODO: correct doc with "enabled" */
	printf("\nEnable ? (0/1) :");
	std::cin >> enabled;
	parameters["enabled"] = enabled;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setMode(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response, modeInfo;
	std::string result, mode;
	int duration = 0;

	printf("\nInput 'mode' :");
	std::cin >> mode;
	printf("\nInput 'duration(in seconds)' :");
	std::cin >> duration;

	modeInfo["mode"] = mode;
	modeInfo["duration"] = duration;
	parameters["modeInfo"] = modeInfo;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setPowerState(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, powerState, standbyReason;

	printf("\nInput 'powerState' :");
	std::cin >> powerState;
	printf("\nInput 'standbyReason' :");
	std::cin >> standbyReason;

	parameters["powerState"] = powerState;
	parameters["standbyReason"] = standbyReason;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setPreferredStandbyMode(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, standbyMode;

	printf("\nInput 'standbyMode' :");
	std::cin >> standbyMode;

	parameters["standbyMode"] = standbyMode;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setTemperatureThresholds(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response, thresholds;
	std::string result, WARN, MAX;

	printf("\nInput 'WARN' level :");
	std::cin >> WARN;
	printf("\nInput 'MAX' level :");
	std::cin >> MAX;

	thresholds["WARN"] = WARN;
	thresholds["MAX"] = MAX;
	parameters["thresholds"] = thresholds;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void setTimeZoneDST(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result, timeZone;

	printf("\nInput 'timeZone' :");
	std::cin >> timeZone;

	parameters["timeZone"] = timeZone;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

void updateFirmware(std::string methodName, JSONRPC::LinkType<Core::JSON::IElement> *remoteObject)
{
	printf("[%llu] Inside (%s)\n", TimeStamp(), __FUNCTION__);

	JsonObject parameters, response;
	std::string result;

	if (invokeJSONRPC(remoteObject, methodName, parameters, response)) {
		response.ToString(result);
		printf("\nResponse: '%s'\n", result.c_str());
	}
}

/******************************** End : Handle Selection *******************************/

void showUsage(char *pName)
{
	printf("%s <Thunder Access Environment> <ip:port> <callSign> <token>\n", pName);
	printf("%s THUNDER_ACCESS %s %s <token>\n", pName, SYSPLUGIN_SERVER_PORT, SYSPLUGIN_CALLSIGN);
	exit(0);
}

/* This section is related to the event handler implementation for Thunder Plugin Events. */
namespace Handlers {
	/* Common Event Handler */
	static void onEventHandler(const JsonObject& parameters) {
		std::string message;
		parameters.ToString(message);
		printf("\n[%llu][System-JSONRPCEvt]: '%s'\n", TimeStamp(), makePretty(message).c_str());
	}
}

std::string getMethodName(SME_t SME)
{
	std::string methodName = "None";

	auto it = SMName.find(SME);
	if (SMName.end() != it) {
		methodName = it->second;
	}
	return methodName;
}

int getChoice(void)
{
	int SMEOption;

	printf("\n================================= Menu =================================\n");
	for (int i = 0 ; i < SME_MAX; i++) {
		printf("(%2d) %-30s	 ", i, getMethodName((SME_t)i).c_str());
		if (i%2) {
			printf("\n");
		}
	}
	printf("\n==========> Enter Option(Number)[MAX+ to Quit] : ");
	scanf("%d", &SMEOption);
	printf("\nReceived SMEOption = %d\n", SMEOption);
	printf("\n============================== Processing ==============================\n");
	if (SMEOption < 0)
		SMEOption = SME_MAX;
	return ((SMEOption < SME_MAX)? SMEOption : SME_MAX);
}

/* System plugin - Invoke System plugin with supplied Method test payloads. */
int EvaluateMethods(JSONRPC::LinkType<Core::JSON::IElement>* remoteObject)
{
	int retStatus = 0;

	do {
		switch((retStatus = getChoice())) {
			case SME_cacheContains: cacheContains(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_clearLastDeepSleepReason: clearLastDeepSleepReason(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_enableMoca: enableMoca(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getAvailableStandbyModes: getAvailableStandbyModes(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getCachedValue: getCachedValue(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getCoreTemperature: getCoreTemperature(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getDeviceInfo: getDeviceInfo(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getDownloadedFirmwareInfo: getDownloadedFirmwareInfo(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getFirmwareDownloadPercent: getFirmwareDownloadPercent(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getFirmwareUpdateInfo: getFirmwareUpdateInfo(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getFirmwareUpdateState: getFirmwareUpdateState(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getLastDeepSleepReason: getLastDeepSleepReason(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getMacAddresses: getMacAddresses(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getMilestones: getMilestones(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getMode: getMode(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getPowerState: getPowerState(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getPreferredStandbyMode: getPreferredStandbyMode(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getPreviousRebootInfo: getPreviousRebootInfo(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getPreviousRebootInfo2: getPreviousRebootInfo2(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getPreviousRebootReason: getPreviousRebootReason(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getRFCConfig: getRFCConfig(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getSerialNumber: getSerialNumber(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getStateInfo: getStateInfo(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getSystemVersions: getSystemVersions(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getTemperatureThresholds: getTemperatureThresholds(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getTimeZoneDST: getTimeZoneDST(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_getXconfParams: getXconfParams(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_hasRebootBeenRequested: hasRebootBeenRequested(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_isGzEnabled: isGzEnabled(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_queryMocaStatus: queryMocaStatus(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_reboot: reboot(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_removeCacheKey: removeCacheKey(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_requestSystemUptime: requestSystemUptime(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setCachedValue: setCachedValue(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setDeepSleepTimer: setDeepSleepTimer(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setGzEnabled: setGzEnabled(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setMode: setMode(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setPowerState: setPowerState(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setPreferredStandbyMode: setPreferredStandbyMode(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setTemperatureThresholds: setTemperatureThresholds(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_setTimeZoneDST: setTimeZoneDST(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_updateFirmware: updateFirmware(getMethodName((SME_t)retStatus), remoteObject); break;
			case SME_MAX:
			default:
				break;
		}
	} while (retStatus != SME_MAX);

	return retStatus;
}

int main(int argc, char** argv)
{
	int retStatus = -1;
	std::string env, server, callsign, sToken;

	if (argc != 5) {
		showUsage(argv[0]);
	}

	for (int i = 0; i < argc; i++) {
		switch (i) {
			case 0: break; /* Self-name */
			case 1: env = argv[i]; break;
			case 2: server = argv[i]; break;
			case 3: callsign = argv[i]; break;
			case 4: sToken = argv[i]; break;
			default: showUsage(argv[0]); break;
		}
	}

	Core::SystemInfo::SetEnvironment(_T(env), (_T(server)));

	/* Thunder-Security: Get Security Token */
	if (sToken.length()) {
		g_strSecToken = "token=" + sToken;
		printf("\nObtained token: '%s'\n", g_strSecToken.c_str());
	}

	JSONRPC::LinkType<Core::JSON::IElement> *remoteObject = new JSONRPC::LinkType<Core::JSON::IElement>(_T(callsign), _T(""), false, g_strSecToken);

	if (remoteObject) {
		/* System plugin Events */
		std::vector<std::string> SystemEventNames;
#ifdef DEBUG
		SystemEventNames.push_back("onSampleEvent");
#endif /* DEBUG */
		SystemEventNames.push_back("onSystemPowerStateChanged");
		SystemEventNames.push_back("onSystemModeChanged");
		SystemEventNames.push_back("onFirmwareUpdateInfoReceived");
		SystemEventNames.push_back("onFirmwareUpdateStateChange");
		SystemEventNames.push_back("onTemperatureThresholdChanged");
		SystemEventNames.push_back("onMacAddressesRetreived");
		SystemEventNames.push_back("onRebootRequest");

		printf("\n[%llu][System-MainFunctn] : Register a common Event Handler for all Events...\n", TimeStamp());
		/* Experimental: Register a common Event Handler for all Events */
		for (std::string eventName : SystemEventNames) {
			if (remoteObject->Subscribe<JsonObject>(1000, _T(eventName),
						&Handlers::onEventHandler) == Core::ERROR_NONE) {
				printf("\n[%llu][System-MainFunctn] : Subscribed to '%s'...\n",
					TimeStamp(), eventName.c_str());
			} else {
				printf("\n[%llu][System-MainFunctn] : Failed to subscribed to '%s'...\n",
					TimeStamp(), eventName.c_str());
			}
		}

		/* Busy loop. */
		printf("\n[%llu][System-MainFunctn] : Method validation loop...\n", TimeStamp());
		retStatus = EvaluateMethods(remoteObject);

		/* Clean-Up */
		printf("\n[%llu][System-MainFunctn] : Clean-Up triggered...\n", TimeStamp());

		for (std::string eventName : SystemEventNames) {
			remoteObject->Unsubscribe(1000, _T(eventName));
			printf("\n[%llu][System-MainFunctn] : Unsubscribed from '%s'...\n",
					TimeStamp(), eventName.c_str());
		}
		delete remoteObject;
	} else {
		printf("\n[%llu][System-MainFunctn] : remoteObject creation failed...\n", TimeStamp());
	}

	return retStatus;
}

