/*
 * Copyright (c) 2024  The AdvantEDGE Authors
 *
 * 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.
 *
 * MEC Demo6 API
 *
 * Demo6 is a MEC application to illustrate the usage of the MEC Sandbox command line APIs.
 *
 * API version: 0.0.1
 * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
 */

package main

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"

	client "github.com/InterDigitalInc/AdvantEDGE/example/demo6/client"

	"github.com/google/uuid"
	"github.com/gorilla/handlers"
	"github.com/roymx/viper"
)

type Config struct {
	Mode          string `mapstructure:"mode"`
	SandboxUrl    string `mapstructure:"sandbox"`
	HttpsOnly     bool   `mapstructure:"https"`
	MecPlatform   string `mapstructure:"mecplatform"`
	SandboxName   string `mapstructure:"sandboxname"`
	AppInstanceId string `mapstructure:"appid"`
	Localurl      string `mapstructure:"localurl"`
	Port          string `mapstructure:"port"`
	CallbackUrl   string `mapstructure:"callbackUrl"`
	CallbackPort  string `mapstructure:"callbackPort"`
}

// MEC 011 registration
// ETSI GS MEC 011 V3.2.1 (2024-04) Clause 7.1.2.6 Type: AppInfo
type AppInfo struct {
	AppName       string `json:"appName"`                 // Name of the application. It shall be consistent with the appName in the AppD, if an AppD is available.
	AppProvider   string `json:"appProvider,omitempty"`   // Provider of the application. It shall be consistent with the appProvider in the AppD, if an AppD is available
	AppDId        string `json:"appDId,omitempty"`        // The application descriptor identifier. It is managed by the application provider to identify the application descriptor in a globally unique way
	AppInstanceId string `json:"appInstanceId,omitempty"` // Identifier of the application instance. Shall be present if the application instance is instantiated by the MEC Management
	IsInsByMec    bool   `json:"isInsByMec,omitempty"`    // Indicate whether the application instance is instantiated by the MEC Management. Default to FALSE if absent.
}

// MEC 011 ServiceInfo
// ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.1.2.2 Type: ServiceInfo
type ServiceInfo struct {
	SerInstanceId string        `json:"serInstanceId,omitempty"`
	SerName       string        `json:"serName"`
	SerCategory   *CategoryRef  `json:"serCategory,omitempty"`
	Version       string        `json:"version"` // Service version
	State         string        `json:"state"`
	TransportInfo TransportInfo `json:"transportInfo"`
	Serializer    string        `json:"serializer"`
	Links         *Links        `json:"_links,omitempty"`
}
type CategoryRef struct {
	Href    string `json:"href"`
	Id      string `json:"id"`
	Name    string `json:"name"`
	Version string `json:"version"`
}
type TransportInfo struct {
	Id           string                      `json:"id"`
	Name         string                      `json:"name"`
	Type_        *string                     `json:"type"`
	Protocol     string                      `json:"protocol"`
	Version      string                      `json:"version"`
	SecurityInfo *SecurityInfo               `json:"securityInfo"`
	Endpoint     *OneOfTransportInfoEndpoint `json:"endpoint"`
}
type SecurityInfo struct {
	OAuth2Info *SecurityInfoOAuth2Info `json:"oAuth2Info,omitempty"`
}
type SecurityInfoOAuth2Info struct {
	GrantTypes    []string `json:"grantTypes"`
	TokenEndpoint string   `json:"tokenEndpoint"`
}
type OneOfTransportInfoEndpoint struct {
	EndPointInfoUris
	EndPointInfoFqdn
	EndPointInfoAddresses
	EndPointInfoAlternative
}
type EndPointInfoUris struct {
	Uris []string `json:"uris"`
}
type EndPointInfoFqdn struct {
	Fqdn []string `json:"fqdn"`
}
type EndPointInfoAddress struct {
	Host string `json:"host"`
	Port int32  `json:"port"`
}
type EndPointInfoAddresses struct {
	Addresses []EndPointInfoAddress `json:"addresses"`
}
type EndPointInfoAlternative struct {
	Alternative *interface{} `json:"alternative"`
}

// MEC 011 Termination subscription
// ETSI GS MEC 011 V3.2.1 (2024-04) Clause 7.1.3.2 Type: AppTerminationNotificationSubscription
type AppTerminationNotificationSubscription struct {
	SubscriptionType  string `json:"subscriptionType"`
	CallbackReference string `json:"callbackReference"`
	Links             *Self  `json:"_links"`
	AppInstanceId     string `json:"appInstanceId"`
}

// MEC013
type UserLocationEventSubscription struct {
	Links                   *Links     `json:"_links,omitempty"`
	Address                 string     `json:"address,omitempty"`
	CallbackReference       string     `json:"callbackReference,omitempty"`
	ClientCorrelator        string     `json:"clientCorrelator,omitempty"`
	ExpiryDeadline          *TimeStamp `json:"expiryDeadline,omitempty"`
	LocationEventCriteria   []string   `json:"locationEventCriteria,omitempty"`
	RequestTestNotification bool       `json:"requestTestNotification,omitempty"`
	SubscriptionType        string     `json:"subscriptionType"`
}

// MEC 030 V2X Subscription
// ETSI GS MEC 030 V3.2.1 Clause 6.3.5 Type: V2xMsgSubscription
type V2xMsgSubscription struct {
	Links                   *Links                            `json:"_links,omitempty"`
	CallbackReference       string                            `json:"callbackReference,omitempty"`
	FilterCriteria          *V2xMsgSubscriptionFilterCriteria `json:"filterCriteria"`
	RequestTestNotification bool                              `json:"requestTestNotification,omitempty"`
	SubscriptionType        string                            `json:"subscriptionType"`
}

// MEC 030 V2X Subscription
// ETSI GS MEC 030 V3.2.1 Clause 6.5.15 Type: V2xMsgFilterCriteria
type V2xMsgSubscriptionFilterCriteria struct {
	MsgType         []string `json:"msgType,omitempty"`
	StdOrganization string   `json:"stdOrganization"`
}

type Plmn struct {
	Mcc string `json:"mcc"`
	Mnc string `json:"mnc"`
}

type CellId struct {
	CellId string `json:"cellId"`
}

type Ecgi struct {
	CellId *CellId `json:"cellId"`
	Plmn   *Plmn   `json:"plmn"`
}

type LocationInfoGeoArea struct {
	Latitude  float32 `json:"latitude"`
	Longitude float32 `json:"longitude"`
}

type LocationInfo struct {
	Ecgi    *Ecgi                `json:"ecgi,omitempty"`
	GeoArea *LocationInfoGeoArea `json:"geoArea,omitempty"`
}

type RouteInfo struct {
	Location *LocationInfo `json:"location"`
	Time     *TimeStamp    `json:"time,omitempty"`
}

type Routes struct {
	RouteInfo []RouteInfo `json:"routeInfo"`
}

type PredictionArea struct {
	Center *LocationInfo `json:"center"`
	Radius string        `json:"radius"`
}

type QosKpi struct {
	Confidence string `json:"confidence,omitempty"`
	KpiName    string `json:"kpiName"`
	KpiValue   string `json:"kpiValue"`
}

type Stream struct {
	StreamId string   `json:"streamId"`
	QosKpi   []QosKpi `json:"qosKpi"`
}

type Qos struct {
	Stream []Stream `json:"stream"`
}

type PredictedQos struct {
	LocationGranularity string          `json:"locationGranularity"`
	NoticePeriod        *TimeStamp      `json:"noticePeriod,omitempty"`
	PredictionArea      *PredictionArea `json:"predictionArea,omitempty"`
	PredictionTarget    string          `json:"predictionTarget"`
	Qos                 *Qos            `json:"qos"`
	Routes              []Routes        `json:"routes,omitempty"`
	TimeGranularity     *TimeStamp      `json:"timeGranularity,omitempty"`
}

// MEC 040
type SystemUpdateNotificationSubscription struct {
	SubscriptionType  string     `json:"subscriptionType"`
	CallbackReference string     `json:"callbackReference"`
	Links             *Links     `json:"links,omitempty"`
	SystemId          []string   `json:"systemId,omitempty"`
	ExpiryDeadline    *TimeStamp `json:"expiryDeadline,omitempty"`
}

// MEC Common types
type LinkType struct {
	// URI referring to a resource
	Href string `json:"href,omitempty"`
}
type Links struct {
	Self *LinkType `json:"self"`
}
type Self struct {
	Self *LinkType `json:"self"`
}
type TimeStamp struct {
	Seconds     int32 `json:"seconds"`
	NanoSeconds int32 `json:"nanoSeconds"`
}

type UeContext struct {
	id string
	v  int32
}

var (
	dir                       string
	fileName                  string
	provider                  string = "github" //"Jupyter2024"
	run                       bool   = true
	done                      chan bool
	cfg                       *client.Configuration = nil
	cl                        *client.APIClient     = nil
	reader                    *bufio.Reader         = nil
	sandboxName               string                = ""
	verificationUri           string                = ""
	userCode                  string                = ""
	mecUrl                    string                = ""
	mecPlateform              string                = ""
	callbackUrl               string                = ""
	callbackPort              string                = ""
	terminationSubscriptionID string                = ""
	scenarios                 []client.SandboxNetworkScenario
	scenario                  []client.Scenario
	scenarioId                int
	services                  []client.SandboxMecServices
	appsInfo                  client.ApplicationInfo
	isRegistered              bool = false
	appServiceInfo            ServiceInfo
	ues                       []UeContext
	subscriptions             []LinkType
)

// Display menu and read selection
const (
	LOGIN                 = "l"
	NAMESPACE             = "n"
	LOGOUT                = "L"
	LIST_SC               = "s"
	SC                    = "S"
	ACTIVATE              = "a"
	DEACTIVATE            = "A"
	LIST_SERVICES         = "m"
	LIST_APP              = "M"
	CREATE_APP            = "c"
	DELETE_APP            = "C"
	MEC011_CONFIRM_READY  = "y"
	MEC011_REGISTRATION   = "r"
	MEC011_DEREGISTRATION = "R"
	MEC011_CREATE_SVC     = "v"
	MEC011_DELETE_SVC     = "V"
	MEC011_GET_SVC        = "g"
	LIST_UES              = "u"
	INC_UE                = "x"
	DEC_UE                = "X"
	MEC013_UE_LOC         = "i"
	MEC013_UE_LOC_SUB     = "j"
	MEC013_UE_LOC_DEL_SUB = "J"
	MEC030_UU_SETTINGS    = "Y"
	MEC030_V2X_SUB        = "z"
	MEC030_V2X_DEL_SUB    = "Z"
	MEC030_V2X_QOS        = "Q"
	MEC040_FED_SYS_GET    = "0"
	MEC040_FED_SRVS_GET   = "1"
	MEC040_FED_SRV_GET    = "2"
	MEC040_FED_SUB_POST   = "3"
	MEC040_FED_SUB_GET    = "4"
	MEC040_FED_SUB_DEL    = "5"
	CAPIF_GET_ALL_SVCS    = "10"
	CAPIF_GET_SVC         = "11"
	CAPIF_CREATE_SVC      = "12"
	CAPIF_DELETE_SVC      = "13"
	CAPIF_SUB_POST        = "14"
	CAPIF_SUB_GET         = "15"
	CAPIF_SUB_DELETE      = "16"
	STATUS                = "T"
	QUIT                  = "q"
)

func clearScreen() {
	fmt.Println("\033[2J")
}

func menu(message string) []string {
	clearScreen()
	fmt.Printf(
		"Mandatory commands:\n"+
			"\t%s: Login, %s: Get Namespace, %s: Logout, %s: Get scenarios list\n"+
			"\t%s <index>: Activate a scenario, %s: Terminate a scenario\n"+
			"Optional commands:\n"+
			"\t%s <index>: Get scenario description\n"+
			"\t%s: Get MEC application services list\n"+
			"\t%s: Get application instances list, %s: Create a new application instance, %s: Delete a new application instance\n"+
			"\t%s: Get UEs, %s <index>: Increase UE, %s <index>: Decrease UE\n"+
			"\t%s: Current status:\n"+
			"MEC 011 App Support:\n"+
			"\t%s: Send ConfirmReady, %s: Send Registration, %s: Send Deregistration\n"+
			"MEC 011 Service Management:\n"+
			"\t%s: Create new service, %s: Delete service, %s: Get list of MEC services\n"+
			"MEC 013:\n"+
			"\t%s <ue address>: Get UE location, %s <ue address>: UE location subscription, %s <subID>: UE location subscription\n"+
			"MEC 030:\n"+
			"\t%s: Get V2X UU unicast setting, %s: V2X Msg subscription, %s <subID>: Delete V2X subscription, %s <[latitudes] [longitudes] [timestamps]: Provide PredictedQoS\n"+
			"\t\t[latitudes] is a set of latitudes separated by comma, [longitudes] is a set of longitudes separated by comma, [timestamps]\n"+
			"\t\tE.g. 43.729416,43.732456 7.414853,7.418417 1653295620,1653299220\n"+
			"MEC 040:\n"+
			"\t%s: Get Federation Systems list, %s <systemId>: Get Federation Services list, %s <systemId> <serviceId>: Get Federation Service, %s: Subscribe, %s [<subId>]: Get subscription, %s <subId>: Delete subscription\n"+
			"MEC CAPIF:\n"+
			"\t%s: Get all services, %s: Get service for the current application instance\n"+
			"%s: Quit\n",
		LOGIN, NAMESPACE, LOGOUT, LIST_SC, ACTIVATE, DEACTIVATE, SC, LIST_SERVICES, LIST_APP, CREATE_APP, DELETE_APP, LIST_UES, INC_UE, DEC_UE, STATUS, MEC011_CONFIRM_READY, MEC011_REGISTRATION, MEC011_DEREGISTRATION, MEC011_CREATE_SVC, MEC011_DELETE_SVC, MEC011_GET_SVC, MEC013_UE_LOC, MEC013_UE_LOC_SUB, MEC013_UE_LOC_DEL_SUB, MEC030_UU_SETTINGS, MEC030_V2X_SUB, MEC030_V2X_DEL_SUB, MEC030_V2X_QOS, MEC040_FED_SYS_GET, MEC040_FED_SRVS_GET, MEC040_FED_SRV_GET, MEC040_FED_SUB_POST, MEC040_FED_SUB_GET, MEC040_FED_SUB_DEL, CAPIF_GET_ALL_SVCS, CAPIF_GET_SVC, QUIT)
	if message != "" {
		fmt.Println("Last message: ", message)
	}
	fmt.Print("Enter your choice: ")

	// Read selection
	choice, _ := reader.ReadString('\n')
	choice = strings.Trim(strings.Trim(choice, "\n"), " ")
	return strings.Split(choice, " ")
}

func login() (string, string, error) {
	fmt.Println(">>> login")

	// Sanity checks
	if sandboxName != "" {
		return "", "", errors.New("Please, logout first")
	}

	// Initialize g;lobal variables
	scenarioId = -1
	appsInfo.Id = ""
	terminationSubscriptionID = ""
	appServiceInfo.SerInstanceId = ""
	subscriptions = make([]LinkType, 0)

	sandbox, _, err := cl.AuthorizationApi.Login(context.TODO(), provider)
	if err != nil {
		return "", "", err
	}
	fmt.Println("login: sandbox: ", sandbox)

	return sandbox.User_code, sandbox.Verification_uri, nil
}

func getNamespace() (string, error) {
	fmt.Println(">>> Get Namespace")

	response, _, err := cl.AuthorizationApi.GetNamespace(context.TODO(), userCode)
	if err != nil {
		return "", err
	}
	fmt.Println("login: Namespace is: ", response)
	sandboxName = response.Sandbox_name

	return sandboxName, nil
}

func logout() error {
	fmt.Println(">>> logout: ", sandboxName)

	// Sanity check
	if sandboxName == "" {
		err := errors.New("logout: Wrong parameters")
		return err
	}

	// Delete subscriptions done
	if len(subscriptions) != 0 {
		for _, l := range subscriptions {
			delete_subscription(l.Href)
		} // End of 'for' statement
	}

	// Delete subscription if any
	if terminationSubscriptionID != "" {
		delete_termination_subscription()
	}

	// Delete MEC service if any
	if appServiceInfo.SerInstanceId != "" {
		mec011_delete_service()
	}

	// Delete registration if any
	if isRegistered {
		mec011_send_deregistration()
	}

	// Delete created AppInstId if any
	if appsInfo.Id != "" {
		deleteMECAppInstId()
		appsInfo.Id = ""
		time.Sleep(2 * time.Second)
	}

	if appServiceInfo.SerInstanceId != "" {
		mec011_delete_service()
		appServiceInfo.SerInstanceId = ""
		time.Sleep(2 * time.Second)
	}

	// Terminate scenario if any
	if scenarioId != -1 {
		terminateScenario(scenarios[scenarioId].Id)
		scenarioId = -1
		time.Sleep(2 * time.Second)
	}

	_, err := cl.AuthorizationApi.Logout(context.TODO(), sandboxName)
	if err != nil {
		return err
	}

	sandboxName = ""

	return nil
}

func getListOfScenarios() ([]client.SandboxNetworkScenario, error) {
	fmt.Println(">>> getListOfScenarios")

	scenarios, _, err := cl.SandboxNetworkScenariosApi.SandboxNetworkScenariosGET(context.TODO(), sandboxName)
	if err != nil {
		fmt.Println("getListOfScenarios: ", err.Error())
		return nil, err
	}
	fmt.Println("getListOfScenarios: ", scenarios)

	return scenarios, nil
}

func getScenario(scenarioId string) ([]client.Scenario, error) {
	fmt.Println(">>> getScenario: ", scenarioId)

	scenario, _, err := cl.SandboxNetworkScenariosApi.SandboxIndividualNetworkScenariosGET(context.TODO(), sandboxName, scenarioId)
	if err != nil {
		fmt.Println("getScenario: ", err.Error())
		return nil, err

	}
	fmt.Println("scenario: ", scenario)

	return scenario, nil
}

func activateScenario(scenarioId string) error {
	fmt.Println(">>> activateScenario: ", scenarioId)

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	}

	_, err := cl.SandboxNetworkScenariosApi.SandboxNetworkScenarioPOST(context.TODO(), sandboxName, scenarioId)
	if err != nil {
		return err
	}

	return nil
}

func terminateScenario(scenarioId string) error {
	fmt.Println(">>> terminateScenario: ", scenarioId)

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	} else if scenarioId == "" {
		return errors.New("No network scenario available")
	}

	_, err := cl.SandboxNetworkScenariosApi.SandboxNetworkScenarioDELETE(context.TODO(), sandboxName, scenarioId)
	if err != nil {
		return err
	}

	return nil
}

func getListOfMECServices() ([]client.SandboxMecServices, error) {
	fmt.Println(">>> getListOfMECServices")

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	}

	services, _, err := cl.SandboxMECServicesApi.SandboxMecServicesGET(context.TODO(), sandboxName)
	if err != nil {
		return nil, err
	}

	return services, nil
}

func getListOfMECAppInstIds() ([]client.ApplicationInfo, error) {
	fmt.Println(">>> getListOfMECAppInstIds")

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	}

	appsInfos, _, err := cl.SandboxAppInstancesApi.SandboxAppInstancesGET(context.TODO(), sandboxName)
	if err != nil {
		return nil, err
	}

	return appsInfos, nil
}

func createMECAppInstId(appInfo client.ApplicationInfo) error {
	fmt.Println(">>> createMECAppInstId: ", appInfo)

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return errors.New("No network scenario available")
	}

	_, _, err := cl.SandboxAppInstancesApi.SandboxAppInstancesPOST(context.TODO(), appInfo, sandboxName)
	if err != nil {
		return err
	}

	return nil
}

func deleteMECAppInstId() error {
	fmt.Println(">>> deleteMECAppInstId")

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return errors.New("No network scenario available")
	}
	if appsInfo.Id == "" {
		return errors.New("No App instance available")
	}

	_, err := cl.SandboxAppInstancesApi.SandboxAppInstancesDELETE(context.TODO(), sandboxName, appsInfo.Id)
	if err != nil {
		return err
	}

	return nil
}

func verify_idx_len(choice string, len int) (int, error) {
	idx, err := strconv.Atoi(choice)
	if err != nil {
		return -1, err
	}
	if idx >= len {
		return -1, errors.New("Index out of range: " + choice)
	}

	return idx, nil
}

func getListOfUes() (ues []UeContext, err error) {
	fmt.Println(">>> getListOfUes")

	// Sanity checks
	if sandboxName == "" {
		return ues, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	}

	u, _, err := cl.SandboxUEControllerApi.SandboxUeControllerGET(context.TODO(), sandboxName)
	if err != nil {
		return ues, err
	}

	for _, k := range u {
		ues = append(ues, UeContext{id: k.Id, v: k.Count})
	}
	fmt.Println("getListOfUes: ues=", ues)

	return ues, nil
}

func increaseUE(idx int) error {
	fmt.Println(">>> increaseUE: idx=", idx)
	fmt.Println(">>> increaseUE: ues=", ues[idx])

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return errors.New("No network scenario available")
	}

	if ues[idx].v == 4 {
		return errors.New("Already reach the maximum value for " + ues[idx].id)
	}
	_, err := cl.SandboxUEControllerApi.SandboxUeControllerPATCH(context.TODO(), sandboxName, ues[idx].id, int32(ues[idx].v))
	if err != nil {
		return err
	}
	ues[idx].v = ues[idx].v + 1

	return nil
}

func decreaseUE(idx int) error {
	fmt.Println(">>> decreaseUE: idx=", idx)
	fmt.Println(">>> decreaseUE: ues=", ues[idx])

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return errors.New("No network scenario available")
	}

	if ues[idx].v == 0 {
		return errors.New("Already reach the minimum value for " + ues[idx].id)
	}
	ues[idx].v = ues[idx].v - 1
	_, err := cl.SandboxUEControllerApi.SandboxUeControllerPATCH(context.TODO(), sandboxName, ues[idx].id, int32(ues[idx].v))
	if err != nil {
		return err
	}

	return nil
}

func mec011_send_confirm_ready() (subId string, response *http.Response, err error) {
	fmt.Println(">>> mec011_send_confirm_ready")

	// Sanity checks
	if sandboxName == "" {
		return "", nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return "", nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return "", nil, errors.New("No App instance available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_app_support/v2/applications/" + appsInfo.Id + "/confirm_ready"
	fmt.Println("mec011_send_confirm_ready: url: " + url)
	// Build message body
	json_body := "{\"indication\":\"READY\"}"
	io_body := strings.NewReader(json_body)
	// Send request and await response
	_, response, err = send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return "", nil, err
	}
	fmt.Println("mec011_send_confirm_ready: confirm ready: " + response.Status)

	return mec011_send_subscribe_termination()
}

func mec011_send_subscribe_termination() (subId string, response *http.Response, err error) {
	fmt.Println(">>> mec011_send_subscribe_termination")

	// Sanity checks
	if sandboxName == "" {
		return "", nil, errors.New("No sandbox available")
	} else if appsInfo.Id == "" {
		return "", nil, errors.New("No App instance available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_app_support/v2/applications/" + appsInfo.Id + "/subscriptions"
	fmt.Println("mec011_send_subscribe_termination: url: " + url)
	// Build message body
	appTerminationBody := AppTerminationNotificationSubscription{
		SubscriptionType:  "AppTerminationNotificationSubscription",
		CallbackReference: callbackUrl + "/asc/termination",
		AppInstanceId:     appsInfo.Id,
	}
	json_body, err := json.Marshal(appTerminationBody)
	if err != nil {
		return "", nil, err
	}
	fmt.Println("mec011_send_subscribe_termination: json_body: " + string(json_body))
	io_body := bytes.NewReader(json_body)
	fmt.Println("mec011_send_subscribe_termination: json_body: ", io_body)
	// Send request and await response
	_, response, err = send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return "", nil, err
	}
	fmt.Println("mec011_send_subscribe_termination: response: " + response.Status)
	fmt.Println("mec011_send_subscribe_termination: Location: " + response.Header["Location"][0])

	subId, err = extract_subscription_id(url, response.Header["Location"][0])
	if err != nil {
		return "", nil, err
	}

	return subId, response, nil
}

func delete_termination_subscription() (err error) {
	fmt.Println(">>> delete_termination_subscription")

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return errors.New("No App instance available")
	} else if terminationSubscriptionID == "" {
		return errors.New("No termination subscription")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_app_support/v2/applications/" + appsInfo.Id + "/subscriptions/" + terminationSubscriptionID
	fmt.Println("delete_termination_subscription: url: " + url)
	// Send request and await response
	_, _, err = send_mec_service_request(http.MethodDelete, url, nil, nil, nil, nil)
	if err != nil {
		fmt.Println("delete_termination_subscription: " + err.Error())
		return err
	}

	terminationSubscriptionID = ""

	return nil
}

func mec011_send_registration() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec011_send_registration: ", appsInfo.Name)
	fmt.Println(">>> mec011_send_registration: ", appsInfo.Id)

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, nil, errors.New("No App instance available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_app_support/v2/registrations"
	fmt.Println("mec011_send_registration: url: " + url)
	// Build message body
	appInfo := AppInfo{
		AppName:       appsInfo.Name,
		AppProvider:   "ETSI",
		AppDId:        uuid.New().String(),
		AppInstanceId: appsInfo.Id,
		IsInsByMec:    true,
	}
	fmt.Println("mec011_send_registration: appInfo: ", appInfo)
	json_body, err := json.Marshal(appInfo)
	if err != nil {
		return nil, nil, err
	}
	fmt.Println("mec011_send_registration: json_body: " + string(json_body))
	io_body := bytes.NewReader(json_body)
	fmt.Println("mec011_send_registration: io_body: ", io_body)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()
	fmt.Println("mec011_send_registration: status: " + response.Status)
	fmt.Println("mec011_send_registration: Location: ", response.Header["Location"][0])
	isRegistered = true

	return body, response, nil
}

func mec011_send_deregistration() (response *http.Response, err error) {
	fmt.Println(">>> mec011_send_deregistration")

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, errors.New("No App instance available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_app_support/v2/registrations/" + appsInfo.Id
	fmt.Println("mec011_send_deregistration: url: " + url)
	// Send request and await response
	_, response, err = send_mec_service_request(http.MethodDelete, url, nil, nil, nil, nil)
	if err != nil {
		return nil, err
	}
	fmt.Println("mec011_send_deregistration: status: " + response.Status)

	isRegistered = false

	return response, nil
}

func mec011_create_service() (resId string, response *http.Response, err error) {
	fmt.Println(">>> mec011_create_service")

	// Sanity checks
	if sandboxName == "" {
		return "", nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return "", nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return "", nil, errors.New("No App instance available")
	} else if appServiceInfo.SerInstanceId != "" {
		return "", nil, errors.New("A MEC service is already created")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_service_mgmt/v1/applications/" + appsInfo.Id + "/services"
	fmt.Println("mec011_create_service: url: " + url)
	// Build message body
	transportType := "REST_HTTP"
	appServiceInfo = ServiceInfo{
		SerName: "demo6 MEC Service",
		SerCategory: &CategoryRef{
			Href:    callbackUrl + "/statistic/v1/quantity",
			Id:      uuid.New().String(),
			Name:    "Demo",
			Version: "1.0.0",
		},
		Version: "1.0.0",
		State:   "ACTIVE",
		TransportInfo: TransportInfo{
			Id:           uuid.New().String(),
			Name:         "HTTP REST API",
			Type_:        &transportType,
			Protocol:     "HTTP",
			Version:      "2.0",
			SecurityInfo: &SecurityInfo{},
			Endpoint:     &OneOfTransportInfoEndpoint{},
		},
		Serializer: "JSON",
	}
	appServiceInfo.TransportInfo.Endpoint.Uris = append(appServiceInfo.TransportInfo.Endpoint.Uris, callbackUrl+"/statistic/v1/quantity")
	json_body, err := json.Marshal(appServiceInfo)
	if err != nil {
		return "", nil, err
	}
	fmt.Println("mec011_create_service: json_body: " + string(json_body))
	io_body := bytes.NewReader(json_body)
	fmt.Println("mec011_create_service: json_body: ", io_body)
	// Send request and await response
	body, response, err := send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return "", nil, err
	}
	defer response.Body.Close()
	if response.StatusCode != 201 {
		return "", nil, errors.New("Invalid Status: " + response.Status)
	}
	// Unmarshal the response body
	fmt.Println("mec011_create_service: body: " + string(body))
	err = json.Unmarshal([]byte(body), &appServiceInfo)
	if err != nil {
		return "", nil, err
	}

	fmt.Println("mec011_create_service: Location: " + response.Header["Location"][0])
	resId, err = extract_subscription_id(url, response.Header["Location"][0])
	if err != nil {
		return "", nil, err
	}

	return resId, response, nil
}

func mec011_delete_service() (err error) {
	fmt.Println(">>> mec011_delete_service")

	// Sanity checks
	if sandboxName == "" {
		return errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return errors.New("No App instance available")
	} else if appServiceInfo.SerInstanceId == "" {
		return errors.New("No MEC service created")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_service_mgmt/v1/applications/" + appsInfo.Id + "/services/" + appServiceInfo.SerInstanceId
	fmt.Println("mec011_delete_service: url: " + url)
	// Send request and await response
	_, _, err = send_mec_service_request(http.MethodDelete, url, nil, nil, nil, nil)
	if err != nil {
		return err
	}

	appServiceInfo.SerInstanceId = ""

	return nil
}

func mec011_get_mec_services() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec011_get_mec_services")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, nil, errors.New("No App instance available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/mec_service_mgmt/v1/services"
	fmt.Println("mec011_get_mec_services: url: " + url)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	return body, nil, nil
}

func send_mec_service_request(method string, path string, body io.Reader, vars url.Values, queryParams url.Values, location *string) (resbody []byte, res *http.Response, err error) {
	fmt.Println(">>> send_mec_service_request: ", appsInfo.Name)

	// Sanity checks
	if sandboxName == "" {
		err = errors.New("No sandbox available")
		return nil, nil, err
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}

	// Setup path and query parameters
	url, err := url.Parse(path)
	if err != nil {
		return nil, nil, err
	}

	// Adding Query Param
	query := url.Query()
	for k, v := range queryParams {
		for _, iv := range v {
			query.Add(k, iv)
		}
	}

	// Encode the parameters.
	url.RawQuery = query.Encode()

	// Generate a new request
	var localVarRequest *http.Request
	if body != nil {
		localVarRequest, err = http.NewRequest(method, url.String(), body)
	} else {
		localVarRequest, err = http.NewRequest(method, url.String(), nil)
	}
	if err != nil {
		return nil, nil, err
	}

	// Override request host, if applicable
	if cfg.Host != "" {
		localVarRequest.Host = cfg.Host
	}

	headers := http.Header{}
	headers.Set("Accept", "application/json")
	localVarRequest.Header = headers

	// Add the user agent to the request.
	localVarRequest.Header.Add("User-Agent", cfg.UserAgent)

	//fmt.Println("send_mec_service_request: localVarRequest: ", localVarRequest)

	// tr := &http.Transport{
	// 	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	// }
	// cfg.HTTPClient = &http.Client{Transport: tr}

	res, err = cfg.HTTPClient.Do(localVarRequest)
	if err != nil {
		return nil, nil, err
	}

	resbody, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, nil, err
	}
	res.Body.Close()

	return resbody, res, err
}

func mec013_get_ue_loc(ue_address string) (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec013_get_ue_loc: ", ue_address)

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if ue_address == "" {
		return nil, nil, errors.New("Wrong parameters")
	}

	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/location/v3/queries/users?address=" + ue_address
	fmt.Println("mec013_get_ue_loc: url: " + url)
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	return body, response, nil
}

func mec013_subscribe_ue_loc(ue_address string) (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec013_subscribe_ue_loc")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if ue_address == "" {
		return nil, nil, errors.New("Wrong parameters")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/location/v3/subscriptions/users"
	fmt.Println("mec013_subscribe_ue_loc: url: " + url)
	// Build message body
	var userLocationEventSubscription = UserLocationEventSubscription{
		Address:                 ue_address,
		CallbackReference:       callbackUrl + "/location/v3/users_notification",
		ClientCorrelator:        "12345",
		RequestTestNotification: false,
		SubscriptionType:        "UserLocationEventSubscription",
	}
	userLocationEventSubscription.LocationEventCriteria = append(userLocationEventSubscription.LocationEventCriteria, "ENTERING_AREA_EVENT")
	userLocationEventSubscription.LocationEventCriteria = append(userLocationEventSubscription.LocationEventCriteria, "LEAVING_AREA_EVENT")
	var m = map[string]UserLocationEventSubscription{}
	m["userLocationEventSubscription"] = userLocationEventSubscription
	json_body, err := json.Marshal(m)
	if err != nil {
		return nil, nil, err
	}
	io_body := bytes.NewReader(json_body)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	fmt.Println("mec013_subscribe_ue_loc: body: " +  string(body))
	var r = map[string]UserLocationEventSubscription{}
	err = json.Unmarshal([]byte(body), &r)
	if err != nil {
		return nil, nil, err
	}
	if val, ok := r["userLocationEventSubscription"]; ok {
		fmt.Println("mec013_subscribe_ue_loc: userLocationEventSubscription: %v", val)
		subscriptions = append(subscriptions, *val.Links.Self)
	} else {
		err = errors.New("Failed to create User location event subscription")
		return nil, nil, err
	}

	return body, response, nil
}

func mec013_delete_ue_loc_subscription(choice string) (response *http.Response, err error) {
	fmt.Println(">>> mec013_delete_ue_loc_subscription: ", choice)

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	} else if choice == "" {
		return nil, errors.New("Wrong parameter")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/location/v3/subscriptions/users/" + choice
	fmt.Println("mec013_delete_ue_loc_subscription: url: " + url)
	// Send request and await response
	response, err = delete_subscription(url)
	if err != nil {
		return nil, err
	}

	return response, nil
}

func mec030_get_v2x_uu_unicast_setting() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec030_get_v2x_uu_unicast_setting")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}

	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/vis/v2/queries/uu_unicast_provisioning_info?location_info=ecgi,268708941961,268711972264"
	fmt.Println("mec030_get_v2x_uu_unicast_setting: url: " + url)
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	return body, response, nil
}

func mec030_subscribe_v2x_messages() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec030_subscribe_v2x_messages")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/vis/v2/subscriptions"
	fmt.Println("mec030_subscribe_v2x_messages: url: " + url)
	// Build message body
	var v2xMsgSubscription = V2xMsgSubscription{
		CallbackReference: callbackUrl + "/vis/v2/v2x_msg_notification",
		FilterCriteria: &V2xMsgSubscriptionFilterCriteria{
			MsgType:         []string{"1", "2", "14", "16"}, // CAM, DENM, CPM & VAM
			StdOrganization: "ETSI",
		},
		SubscriptionType: "V2xMsgSubscription",
	}
	json_body, err := json.Marshal(v2xMsgSubscription)
	if err != nil {
		return nil, nil, err
	}
	io_body := bytes.NewReader(json_body)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	err = json.Unmarshal([]byte(body), &v2xMsgSubscription)
	if err != nil {
		return nil, nil, err
	}
	subscriptions = append(subscriptions, *v2xMsgSubscription.Links.Self)

	return body, response, nil
}

func mec030_delete_v2x_messages_subscription(choice string) (response *http.Response, err error) {
	fmt.Println(">>> mec030_delete_v2x_messages_subscription: ", choice)

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	}
	if choice == "" {
		return nil, errors.New("Wrong parameter")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/vis/v2/subscriptions/" + choice
	fmt.Println("mec030_delete_v2x_messages_subscription: url: " + url)
	// Send request and await response
	response, err = delete_subscription(url)
	if err != nil {
		return nil, err
	}

	return response, nil
}

// https://www.calculatorsoup.com/calculators/conversions/convert-decimal-degrees-to-degrees-minutes-seconds.php
// https://www.latlong.net/degrees-minutes-seconds-to-decimal-degrees
func mec030_predicted_qos(latitudes string, longitudes string, timestamps string) (body []byte, response *http.Response, err error) {
	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if latitudes == "" || longitudes == "" || timestamps == "" {
		return nil, nil, errors.New("Wrong parameters")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/vis/v2/provide_predicted_qos"
	fmt.Println("mec030_predicted_qos: url: " + url)
	// Build message body
	// 1. Split arguments into slices
	lats := strings.Split(latitudes, ",")
	longs := strings.Split(longitudes, ",")
	ts := strings.Split(timestamps, ",")
	if len(lats) != len(longs) || len(ts) != len(lats) || len(ts) != len(longs) {
		return nil, nil, errors.New("Wrong lenght in parameters")
	}
	// 2. Build routes
	var routes = Routes{}
	for i, _ := range lats {
		lat, err := strconv.ParseFloat(lats[i], 32)
		if err != nil {
			return nil, nil, errors.New("Wrong latitude value: " + lats[i])
		}
		long, err := strconv.ParseFloat(longs[i], 32)
		if err != nil {
			return nil, nil, errors.New("Wrong longitude value: " + longs[i])
		}
		sec, err := strconv.Atoi(ts[i])
		if err != nil {
			return nil, nil, errors.New("Wrong timestamp value: " + ts[i])
		}
		var routeInfo = RouteInfo{
			Location: &LocationInfo{
				GeoArea: &LocationInfoGeoArea{
					Latitude:  float32(lat),
					Longitude: float32(long),
				},
			},
			Time: &TimeStamp{
				NanoSeconds: 0,
				Seconds:     int32(sec),
			},
		}
		routes.RouteInfo = append(routes.RouteInfo, routeInfo)
	}
	// 3. Build PredictedQos
	var predictedQos = PredictedQos{
		PredictionTarget:    "SINGLE_UE_PREDICTION",
		LocationGranularity: "30",
	}
	predictedQos.Routes = append(predictedQos.Routes, routes)
	json_body, err := json.Marshal(predictedQos)
	if err != nil {
		return nil, nil, err
	}
	fmt.Println("mec030_predicted_qos: json_body: " + string(json_body))
	io_body := bytes.NewReader(json_body)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}

	return body, response, nil
}

func mec40_get_systems_list() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec40_get_systems_list")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/fed_enablement/v1/fed_resources/systems"
	fmt.Println("mec40_get_systems_list: url: " + url)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	return body, response, nil
}

func mec40_get_services_list(choice string) (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec40_get_services_list")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}
	if choice == "" {
		return nil, nil, errors.New("Wrong parameter")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/fed_enablement/v1/fed_resources/systems/" + choice + "/services"
	fmt.Println("mec40_get_services_list: url: " + url)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}

	return body, response, nil
}

func mec40_get_service_list(systemId string, serviceId string) (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec40_get_service_list")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}
	if systemId == "" || serviceId == "" {
		return nil, nil, errors.New("Wrong parameter")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/fed_enablement/v1/fed_resources/systems/" + systemId + "/services/" + serviceId
	fmt.Println("mec40_get_service_list: url: " + url)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}

	return body, response, nil
}

func mec40_create_subscription() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec40_create_subscription")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/fed_enablement/v1/subscriptions"
	fmt.Println("mec40_create_subscription: url: " + url)
	// Build message body
	var systemUpdateNotificationSubscription = SystemUpdateNotificationSubscription{
		SubscriptionType:  "SystemUpdateNotificationSubscription",
		CallbackReference: callbackUrl + "/fed/v1/notification",
	}
	json_body, err := json.Marshal(systemUpdateNotificationSubscription)
	if err != nil {
		return nil, nil, err
	}
	io_body := bytes.NewReader(json_body)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodPost, url, io_body, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	err = json.Unmarshal([]byte(body), &systemUpdateNotificationSubscription)
	if err != nil {
		return nil, nil, err
	}
	subscriptions = append(subscriptions, *systemUpdateNotificationSubscription.Links.Self)

	return body, response, nil
}

func mec40_get_subscriptions(choice []string) (body []byte, response *http.Response, err error) {
	fmt.Println(">>> mec40_get_subscriptions: ", choice)

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/fed_enablement/v1/subscriptions"
	if len(choice) == 2 { // Individual GET
		url = url + "/" + choice[1]
	}
	fmt.Println("mec40_get_subscriptions: url: " + url)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	return body, response, nil
}

func mec40_delete_subscriptions(choice string) (response *http.Response, err error) {
	fmt.Println(">>> mec40_delete_subscriptions: ", choice)

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	}
	if choice == "" {
		return nil, errors.New("Wrong parameter")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/fed_enablement/v1/subscriptions/" + choice
	fmt.Println("mec40_delete_subscriptions: url: " + url)
	// Send request and await response
	response, err = delete_subscription(url)
	if err != nil {
		return nil, err
	}

	return response, nil
}

func capif_get_all_svcs() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> capif_get_all_svcs")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/service-apis/v1/allServiceAPIs"
	fmt.Println("capif_get_all_svcs: url: " + url)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	return body, response, nil
}

func capif_get_svc() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> capif_get_svc")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, nil, errors.New("No appInstanceId available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/published-apis/v1/" + appsInfo.Id + "/service-apis"
	fmt.Println("capif_get_svc: url: " + url)
	// Send request and await response
	body, response, err = send_mec_service_request(http.MethodGet, url, nil, nil, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	return body, response, nil
}

func capif_create_svc() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> capif_create_svc")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, nil, errors.New("No network scenario available")
	}

	return nil, nil, errors.New("Not implemented")
}

func capif_delete_svc(choice string) (response *http.Response, err error) {
	fmt.Println(">>> capif_delete_svc: ", choice)

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, errors.New("No appInstanceId available")
	}
	if choice == "" {
		return nil, errors.New("Wrong parameter")
	}

	return nil, errors.New("Not implemented")
}

func capif_create_subscription() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> capif_create_subscription")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, nil, errors.New("No network scenario available")
	}

	// Set URL
	url := mecUrl + "/" + sandboxName + "/" + mecPlateform + "/service-apis/v1/" + appsInfo.Id + "/subscriptions"
	fmt.Println("capif_create_subscription: url: " + url)

	return nil, nil, errors.New("Not implemented")
}

func capif_get_subscriptions() (body []byte, response *http.Response, err error) {
	fmt.Println(">>> capif_get_subscriptions")

	// Sanity checks
	if sandboxName == "" {
		return nil, nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, nil, errors.New("No appInstanceId available")
	}

	return nil, nil, errors.New("Not implemented")
}

func capif_delete_subscriptions(choice string) (response *http.Response, err error) {
	fmt.Println(">>> capif_delete_subscriptions: ", choice)

	// Sanity checks
	if sandboxName == "" {
		return nil, errors.New("No sandbox available")
	} else if scenarioId == -1 {
		return nil, errors.New("No network scenario available")
	} else if appsInfo.Id == "" {
		return nil, errors.New("No appInstanceId available")
	}
	if choice == "" {
		return nil, errors.New("Wrong parameter")
	}

	return nil, errors.New("Not implemented")
}

func app_status() (resp string) {
	resp = ""
	if sandboxName != "" {
		resp += "Sandbox: " + sandboxName
	}
	if appsInfo.Id != "" {
		resp += ", appsInfo.Id: " + appsInfo.Id
	}
	if appServiceInfo.SerInstanceId != "" {
		resp += ", SerInstanceId: " + appServiceInfo.SerInstanceId
		resp += "- uri: " + appServiceInfo.Links.Self.Href
	}
	if terminationSubscriptionID != "" {
		resp += ", Subscription: " + terminationSubscriptionID
	}
	if isRegistered {
		resp += ", Demo6 app registered"
	} else {
		resp += ", Demo6 app not registered"
	}
	if len(subscriptions) != 0 {
		resp += ", #" + strconv.Itoa(len(subscriptions)) + " subscriptions created"
	} else {
		resp += ", no subscriptions created"
	}

	return resp
}

func extract_subscription_id(base_url string, subscription_url string) (string, error) {
	fmt.Println(">>> extract_subscription_id: base_url: " + base_url)
	fmt.Println(">>> extract_subscription_id: subscription_url: " + subscription_url)

	re := regexp.MustCompile(base_url + "/(?P<sub_id>.+)")
	if re == nil {
		return "", errors.New("Regexp creation failure")
	}
	matches := re.FindStringSubmatch(subscription_url)
	if matches == nil {
		return "", errors.New("Matching failure")
	}

	return matches[re.SubexpIndex("sub_id")], nil
}

func delete_subscription(url string) (response *http.Response, err error) {
	fmt.Println(">>> delete_subscription: ", url)

	// Send request and await response
	_, response, err = send_mec_service_request(http.MethodDelete, url, nil, nil, nil, nil)
	if err != nil {
		return nil, err
	}

	delete_subscription_from_list(url)

	return response, nil
}

func delete_subscription_from_list(url string) {
	fmt.Println(">>> delete_subscription: ", url)
	for s, l := range subscriptions {
		if l.Href == url {
			subscriptions = append(subscriptions[:s], subscriptions[s+1:]...)
			break
		}
	}
}

func mec013_notification(w http.ResponseWriter, r *http.Request) {
	fmt.Println(">>> mec013_notification: ", r)
	w.WriteHeader(http.StatusOK)
}

func v2x_msg_notification(w http.ResponseWriter, r *http.Request) {
	fmt.Println(">>> v2x_msg_notification: ", r)
	w.WriteHeader(http.StatusOK)
}

func fed_notification(w http.ResponseWriter, r *http.Request) {
	fmt.Println(">>> fed_notification: ", r)
	w.WriteHeader(http.StatusOK)
}

func capif_notification(w http.ResponseWriter, r *http.Request) {
	fmt.Println(">>> capif_notification: ", r)
	w.WriteHeader(http.StatusOK)
}

func mec011_service_statistic_get(w http.ResponseWriter, r *http.Request) {
	fmt.Println(">>> mec011_service_statistic_get: ", r)
	w.WriteHeader(http.StatusOK)
}

func main() {
	if len(os.Args) < 2 {
		// no config argument
		fmt.Errorf("Missing parameter, require file path to configurations!")
		return
	}

	// Read configuration file path in command line arugments
	configPath := os.Args[1]
	dir = strings.TrimSpace(filepath.Dir(configPath))
	fileName = strings.TrimSpace(filepath.Base(configPath))
	fmt.Println("dir: ", dir)
	fmt.Println("fileName: ", fileName)

	// Retrieve environmental variable
	var config Config
	// trim file extension
	envName := strings.TrimSuffix(fileName, ".yaml")
	fmt.Println("Using config values from ", dir, "/", envName)
	config, err := LoadConfig(dir, envName)
	if err != nil {
		fmt.Errorf(err.Error())
		return
	}
	// mecUrl is url of the sandbox system
	mecUrl = config.SandboxUrl
	// Check mecUrl if uses https
	if config.HttpsOnly {
		if !strings.HasPrefix(mecUrl, "https://") {
			mecUrl = "https://" + mecUrl
		}
	} else if !config.HttpsOnly {
		if !strings.HasPrefix(mecUrl, "http://") {
			mecUrl = "http://" + mecUrl
		}
	} else {
		fmt.Errorf("Wrong configuration!")
		return
	}
	if strings.HasSuffix(mecUrl, "/") {
		mecUrl = strings.TrimSuffix(mecUrl, "/")
	}
	mecPlateform = config.MecPlatform
	fmt.Println("mecUrl: " + mecUrl)
	fmt.Println("mecPlateform: " + mecPlateform)

	callbackUrl = config.CallbackUrl + ":" + config.CallbackPort
	fmt.Println("callbackUrl: " + callbackUrl)

	cfg = client.NewConfiguration()
	if cfg == nil {
		fmt.Errorf("Failed to create client configuration!")
		return
	}
	cfg.BasePath = mecUrl + "/sandbox-api/v1"
	fmt.Println("cfg.BasePath: " + cfg.BasePath)
	cl = client.NewAPIClient(cfg)
	if cl == nil {
		fmt.Errorf("Failed to create client reference!")
		return
	}

	go func() {
		// Start demo6 server
		reader = bufio.NewReader(os.Stdin)

		var message string = ""
		for run {
			// Display menu and read selection
			choice := menu(message)

			// Apply it
			fmt.Println("choice: ", choice)
			if strings.Compare(choice[0], "q") == 0 {
				run = false
				break
			} else {
				message = process_choice(choice)
			}

		} // End of 'for' statement

	}()

	// Listen for SIGKILL
	go func() {
		sigchan := make(chan os.Signal, 10)
		signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)
		<-sigchan
		fmt.Println("Waiting to shut down program !")
		run = false
	}()

	// Start REST API Server
	go func() {
		//fmt.Println(">>> Start REST API Server on port : ", config.CallbackPort)
		router := NewRouter()
		methods := handlers.AllowedMethods([]string{"OPTIONS", "DELETE", "GET", "HEAD", "POST", "PUT"})
		header := handlers.AllowedHeaders([]string{"content-type"})
		fmt.Println(http.ListenAndServe(":"+config.CallbackPort, handlers.CORS(methods, header)(router)))
		run = false
		fmt.Println("<<< Stop REST API Server")
	}()

	// Listen for demo6 error exit program
	go func() {
		<-done
		fmt.Println("Waiting to shut down program !")
		run = false
	}()

	for {
		// Invoke graceful termination upon program kill
		if !run {
			fmt.Println("Invoking demo6 graceful termination")
			if sandboxName != "" {
				logout()
			}
			break
		}
		time.Sleep(time.Second)
	}
}

func LoadConfig(path string, name string) (config Config, err error) {
	viper.SetConfigType("yaml")
	viper.AddConfigPath(path)
	viper.SetConfigName(name)
	viper.AutomaticEnv()

	err = viper.ReadInConfig()
	if err != nil {
		return config, err
	}

	err = viper.Unmarshal(&config)
	if err != nil {
		return config, err
	}
	return

}

func process_choice(choice []string) string {

	var message string
	if strings.Compare(choice[0], LOGIN) == 0 {
		userCode, verificationUri, _ = login()
		message = fmt.Sprintf("User Code: %s and Verification URI is %s: ", userCode, verificationUri)
	} else if strings.Compare(choice[0], LOGOUT) == 0 {
		err := logout()
		if err != nil {
			return err.Error()
		}
		message = "Sandbox terminated"
	} else if strings.Compare(choice[0], NAMESPACE) == 0 {
		sandbox, err := getNamespace()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("Sandbox ID is: %s", sandbox)
	} else if strings.Compare(choice[0], LIST_SC) == 0 {
		scenarios, _ = getListOfScenarios()
		message = fmt.Sprintf("scenarios: %s", fmt.Sprint(scenarios))
	} else if strings.Compare(choice[0], SC) == 0 {
		if len(choice) == 1 {
			return "Index is not set"
		}
		idx, err := verify_idx_len(choice[1], len(scenarios))
		if err != nil {
			return fmt.Sprintf("Invalid index: %s", err.Error())
		}
		scenario, _ = getScenario(scenarios[idx].Id)
		message = fmt.Sprintf("Scenario %s:", fmt.Sprint(scenario))
	} else if strings.Compare(choice[0], ACTIVATE) == 0 {
		if len(choice) == 1 {
			return "Index is not set"
		}
		var err error
		scenarioId, err = verify_idx_len(choice[1], len(scenarios))
		if err != nil {
			return fmt.Sprintf("Invalid index: %s", err.Error())
		}
		err = activateScenario(scenarios[scenarioId].Id)
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("Scenario %s activated (wait some seconds before the next command)", scenarios[scenarioId].Id)
	} else if strings.Compare(choice[0], DEACTIVATE) == 0 {
		err := terminateScenario(scenarios[scenarioId].Id)
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("Scenario %s terminated (wait some seconds before the next command)", scenarios[scenarioId].Id)
		scenarioId = -1
	} else if strings.Compare(choice[0], LIST_SERVICES) == 0 {
		var err error
		services, err = getListOfMECServices()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("Services:  %s", fmt.Sprint(services))
	} else if strings.Compare(choice[0], LIST_APP) == 0 {
		appsInfos, err := getListOfMECAppInstIds()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("AppInstIds:  %s", fmt.Sprint(appsInfos))
	} else if strings.Compare(choice[0], CREATE_APP) == 0 {
		if appsInfo.Id != "" {
			return fmt.Sprintf("App instance id already created: %s", appsInfo.Id)
		}
		appsInfo = client.ApplicationInfo{
			Id:       uuid.New().String(),
			Name:     "demo6 test app",
			NodeName: mecPlateform,
			Type_:    "USER",
			Persist:  false,
		}
		err := createMECAppInstId(appsInfo)
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("appsInfo:  %s created", fmt.Sprint(appsInfo.Id))
	} else if strings.Compare(choice[0], DELETE_APP) == 0 {
		err := deleteMECAppInstId()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("appsInfo:  %s deleted", fmt.Sprint(appsInfo.Id))
		appsInfo.Id = ""
	} else if strings.Compare(choice[0], LIST_UES) == 0 {
		ues = make([]UeContext, 0)
		var err error
		ues, err = getListOfUes()
		if err != nil {
			return fmt.Sprintf("Failed to get the list of UEs: %s", err.Error())
		}
		message = fmt.Sprintf("List of UEs %v", ues)
	} else if strings.Compare(choice[0], INC_UE) == 0 {
		if len(choice) == 1 {
			return "Index is not set"
		}
		ueId, err := verify_idx_len(choice[1], len(ues))
		if err != nil {
			return fmt.Sprintf("Invalid index: %s", err.Error())
		}
		err = increaseUE(ueId)
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("Increase %s, new value: %d", ues[ueId].id, ues[ueId].v)
	} else if strings.Compare(choice[0], DEC_UE) == 0 {
		if len(choice) == 1 {
			return "Index is not set"
		}
		ueId, err := verify_idx_len(choice[1], len(ues))
		if err != nil {
			return fmt.Sprintf("Invalid index: %s", err.Error())
		}
		err = decreaseUE(ueId)
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("Decrease %s, new value: %d", ues[ueId].id, ues[ueId].v)
	} else if strings.Compare(choice[0], MEC011_CONFIRM_READY) == 0 {
		var err error
		terminationSubscriptionID, _, err = mec011_send_confirm_ready()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("Termination subscription ID:  %s", terminationSubscriptionID)
	} else if strings.Compare(choice[0], MEC011_REGISTRATION) == 0 {
		var err error
		body, _, err := mec011_send_registration()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC011_DEREGISTRATION) == 0 {
		var err error
		response, err := mec011_send_deregistration()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", response.Status)
	} else if strings.Compare(choice[0], MEC011_CREATE_SVC) == 0 {
		var err error
		resId, _, err := mec011_create_service()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body: resource id: %s", resId)
	} else if strings.Compare(choice[0], MEC011_DELETE_SVC) == 0 {
		err := mec011_delete_service()
		if err != nil {
			return err.Error()
		}
		message = "MEC Service deleted."
	} else if strings.Compare(choice[0], MEC011_GET_SVC) == 0 {
		body, _, err := mec011_get_mec_services()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))

	} else if strings.Compare(choice[0], MEC013_UE_LOC) == 0 {
		if len(choice) == 1 {
			return "UE address is not set"
		}
		body, _, err := mec013_get_ue_loc(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC013_UE_LOC_SUB) == 0 {
		if len(choice) == 1 {
			return "UE address is not set"
		}
		body, _, err := mec013_subscribe_ue_loc(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC013_UE_LOC_DEL_SUB) == 0 {
		if len(choice) == 1 {
			return "Subscription ID is not set"
		}
		_, err := mec013_delete_ue_loc_subscription(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  Subscription %s deleted", choice[1])
	} else if strings.Compare(choice[0], MEC030_UU_SETTINGS) == 0 {
		var err error
		body, _, err := mec030_get_v2x_uu_unicast_setting()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC030_V2X_SUB) == 0 {
		var err error
		body, _, err := mec030_subscribe_v2x_messages()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC030_V2X_DEL_SUB) == 0 {
		if len(choice) == 1 {
			return "Subscription ID is not set"
		}
		_, err := mec030_delete_v2x_messages_subscription(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  Subscription %s deleted", choice[1])
	} else if strings.Compare(choice[0], MEC030_V2X_QOS) == 0 {
		if len(choice) != 4 {
			return "Invalid entries for this command"
		}
		body, _, err := mec030_predicted_qos(choice[1], choice[2], choice[3])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", body)
	} else if strings.Compare(choice[0], MEC040_FED_SYS_GET) == 0 {
		body, _, err := mec40_get_systems_list()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC040_FED_SRVS_GET) == 0 {
		if len(choice) == 1 {
			return "System ID is not set"
		}
		body, _, err := mec40_get_services_list(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC040_FED_SRV_GET) == 0 {
		if len(choice) < 3 {
			return "System ID and/or Service ID are not set"
		}
		body, _, err := mec40_get_service_list(choice[1], choice[2])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC040_FED_SUB_POST) == 0 {
		body, _, err := mec40_create_subscription()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC040_FED_SUB_GET) == 0 {
		body, _, err := mec40_get_subscriptions(choice)
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], MEC040_FED_SUB_DEL) == 0 {
		if len(choice) == 1 {
			return "Subscription ID is not set"
		}
		response, err := mec40_delete_subscriptions(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response: %s", response.Status)
	} else if strings.Compare(choice[0], CAPIF_GET_ALL_SVCS) == 0 {
		body, _, err := capif_get_all_svcs()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response: %s", string(body))
	} else if strings.Compare(choice[0], CAPIF_GET_SVC) == 0 {
		body, _, err := capif_get_svc()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response: %s", string(body))
	} else if strings.Compare(choice[0], CAPIF_CREATE_SVC) == 0 {
		body, _, err := capif_create_svc()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response: %s", string(body))
	} else if strings.Compare(choice[0], CAPIF_DELETE_SVC) == 0 {
		if len(choice) == 1 {
			return "apiId is not set"
		}
		response, err := capif_delete_svc(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response: %s", response.Status)
	} else if strings.Compare(choice[0], CAPIF_SUB_POST) == 0 {
		body, _, err := capif_create_subscription()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], CAPIF_SUB_GET) == 0 {
		body, _, err := capif_get_subscriptions()
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response body:  %s", string(body))
	} else if strings.Compare(choice[0], CAPIF_SUB_DELETE) == 0 {
		if len(choice) == 1 {
			return "Subscription ID is not set"
		}
		response, err := capif_delete_subscriptions(choice[1])
		if err != nil {
			return err.Error()
		}
		message = fmt.Sprintf("response: %s", response.Status)
	} else if strings.Compare(choice[0], STATUS) == 0 {
		resp := app_status()
		message = fmt.Sprintf("Current status: %s", resp)
	} else {
		message = fmt.Sprintf("Invalid command: %s", choice)
	}

	return message
}
