package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"

	meepdaimgr "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-dai-mgr"

	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
	//	met "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-metrics"
	mod "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-model"
	mq "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-mq"

	"github.com/gorilla/mux"
)

//const INITIAL = 0
//const UPDATED = 1

//json format using spacing to facilitate reading
const testScenario string = `
{
    "version":"1.5.3",
    "name":"test-scenario",
    "deployment":{
        "netChar":{
            "latency":50,
            "latencyVariation":5,
            "throughputDl":1000,
            "throughputUl":1000
        },
        "domains":[
            {
                "id":"PUBLIC",
                "name":"PUBLIC",
                "type":"PUBLIC",
                "netChar":{
                    "latency":6,
                    "latencyVariation":2,
                    "throughputDl":1000000,
                    "throughputUl":1000000
                },
                "zones":[
                    {
                        "id":"PUBLIC-COMMON",
                        "name":"PUBLIC-COMMON",
                        "type":"COMMON",
                        "netChar":{
                            "latency":5,
                            "latencyVariation":1,
                            "throughput":1000000
                        },
                        "networkLocations":[
                            {
                                "id":"PUBLIC-COMMON-DEFAULT",
                                "name":"PUBLIC-COMMON-DEFAULT",
                                "type":"DEFAULT",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":50000,
                                    "throughputUl":50000,
                                    "packetLoss":1
                                }
                            }
                        ]
                    }
                ]
            },
            {
                "id":"4da82f2d-1f44-4945-8fe7-00c0431ef8c7",
                "name":"operator-cell1",
                "type":"OPERATOR-CELLULAR",
                "netChar":{
                    "latency":6,
                    "latencyVariation":2,
                    "throughputDl":1000,
                    "throughputUl":1000
                },
                "cellularDomainConfig":{
                    "mnc":"456",
                    "mcc":"123",
                    "defaultCellId":"1234567"
                },
                "zones":[
                    {
                        "id":"operator-cell1-COMMON",
                        "name":"operator-cell1-COMMON",
                        "type":"COMMON",
                        "netChar":{
                            "latency":5,
                            "latencyVariation":1,
                            "throughput":1000
                        },
                        "networkLocations":[
                            {
                                "id":"operator-cell1-COMMON-DEFAULT",
                                "name":"operator-cell1-COMMON-DEFAULT",
                                "type":"DEFAULT",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                }
                            }
                        ]
                    },
                    {
                        "id":"0836975f-a7ea-41ec-b0e0-aff43178194d",
                        "name":"zone1",
                        "type":"ZONE",
                        "netChar":{
                            "latency":5,
                            "latencyVariation":1,
                            "throughput":1000
                        },
                        "networkLocations":[
                            {
                                "id":"zone1-DEFAULT",
                                "name":"zone1-DEFAULT",
                                "type":"DEFAULT",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                },
                                "physicalLocations":[
                                    {
                                        "id":"97b80da7-a74a-4649-bb61-f7fa4fbb2d76",
                                        "name":"zone1-edge1",
                                        "type":"EDGE",
                                        "connected":true,
                                        "processes":[
                                            {
                                                "id":"fcf1269c-a061-448e-aa80-6dd9c2d4c548",
                                                "name":"zone1-edge1-iperf",
                                                "type":"EDGE-APP",
                                                "image":"meep-docker-registry:30001/iperf-server",
                                                "commandArguments":"-c, export; iperf -s -p $IPERF_SERVICE_PORT",
                                                "commandExe":"/bin/bash",
                                                "serviceConfig":{
                                                    "name":"zone1-edge1-iperf",
                                                    "meSvcName":"iperf",
                                                    "ports":[
                                                        {
                                                            "protocol":"UDP",
                                                            "port":80
                                                        }
                                                    ]
                                                },
                                                "netChar":{
                                                    "throughputDl":1000,
                                                    "throughputUl":1000
                                                }
                                            },
                                            {
                                                "id":"35697e68-c627-4b8d-9cd7-ad8b8e226aee",
                                                "name":"zone1-edge1-svc",
                                                "type":"EDGE-APP",
                                                "image":"meep-docker-registry:30001/demo-server",
                                                "environment":"MGM_GROUP_NAME=svc, MGM_APP_ID=zone1-edge1-svc, MGM_APP_PORT=80",
                                                "serviceConfig":{
                                                    "name":"zone1-edge1-svc",
                                                    "meSvcName":"svc",
                                                    "ports":[
                                                        {
                                                            "protocol":"TCP",
                                                            "port":80
                                                        }
                                                    ]
                                                },
                                                "netChar":{
                                                    "throughputDl":1000,
                                                    "throughputUl":1000
                                                }
                                            }
                                        ],
                                        "netChar":{
                                            "throughputDl":1000,
                                            "throughputUl":1000
                                        }
                                    }
                                ]
                            },
                            {
                                "id":"7a6f8077-b0b3-403d-b954-3351e21afeb7",
                                "name":"zone1-poa-cell1",
                                "type":"POA-4G",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                },
                                "poa4GConfig":{
                                    "cellId":"2345678"
                                },
                                "physicalLocations":[
                                    {
                                        "id":"32a2ced4-a262-49a8-8503-8489a94386a2",
                                        "name":"ue1",
                                        "type":"UE",
                                        "connected":true,
                                        "wireless":true,
                                        "processes":[
                                            {
                                                "id":"9bdd6acd-f6e4-44f6-a26c-8fd9abd338a7",
                                                "name":"ue1-iperf",
                                                "type":"UE-APP",
                                                "image":"meep-docker-registry:30001/iperf-client",
                                                "commandArguments":"-c, export; iperf -u -c $IPERF_SERVICE_HOST -p $IPERF_SERVICE_PORT\n-t 3600 -b 50M;",
                                                "commandExe":"/bin/bash",
                                                "netChar":{
                                                    "throughputDl":1000,
                                                    "throughputUl":1000
                                                }
                                            }
                                        ],
                                        "netChar":{
                                            "throughputDl":1000,
                                            "throughputUl":1000
                                        }
                                    },
                                    {
                                        "id":"b1851da5-c9e1-4bd8-ad23-5925c82ee127",
                                        "name":"zone1-fog1",
                                        "type":"FOG",
                                        "connected":true,
                                        "processes":[
                                            {
                                                "id":"c2f2fb5d-4053-4cee-a0ee-e62bbb7751b6",
                                                "name":"zone1-fog1-iperf",
                                                "type":"EDGE-APP",
                                                "image":"meep-docker-registry:30001/iperf-server",
                                                "commandArguments":"-c, export; iperf -s -p $IPERF_SERVICE_PORT;",
                                                "commandExe":"/bin/bash",
                                                "serviceConfig":{
                                                    "name":"zone1-fog1-iperf",
                                                    "meSvcName":"iperf",
                                                    "ports":[
                                                        {
                                                            "protocol":"UDP",
                                                            "port":80
                                                        }
                                                    ]
                                                },
                                                "netChar":{
                                                    "throughputDl":1000,
                                                    "throughputUl":1000
                                                }
                                            },
                                            {
                                                "id":"53b5806b-e213-4c5a-a181-f1c31c24287b",
                                                "name":"zone1-fog1-svc",
                                                "type":"EDGE-APP",
                                                "image":"meep-docker-registry:30001/demo-server",
                                                "environment":"MGM_GROUP_NAME=svc, MGM_APP_ID=zone1-fog1-svc, MGM_APP_PORT=80",
                                                "serviceConfig":{
                                                    "name":"zone1-fog1-svc",
                                                    "meSvcName":"svc",
                                                    "ports":[
                                                        {
                                                            "protocol":"TCP",
                                                            "port":80
                                                        }
                                                    ]
                                                },
                                                "netChar":{
                                                    "throughputDl":1000,
                                                    "throughputUl":1000
                                                }
                                            }
                                        ],
                                        "netChar":{
                                            "throughputDl":1000,
                                            "throughputUl":1000
                                        }
                                    }
                                ]
                            },
                            {
                                "id":"7ff90180-2c1a-4c11-b59a-3608c5d8d874",
                                "name":"zone1-poa-cell2",
                                "type":"POA-4G",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                },
                                "poa4GConfig":{
                                    "cellId":"3456789"
                                }
                            }
                        ]
                    },
                    {
                        "id":"d1f06b00-4454-4d35-94a5-b573888e7ea9",
                        "name":"zone2",
                        "type":"ZONE",
                        "netChar":{
                            "latency":5,
                            "latencyVariation":1,
                            "throughput":1000
                        },
                        "networkLocations":[
                            {
                                "id":"zone2-DEFAULT",
                                "name":"zone2-DEFAULT",
                                "type":"DEFAULT",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                },
                                "physicalLocations":[
                                    {
                                        "id":"fb130d18-fd81-43e0-900c-c584e7190302",
                                        "name":"zone2-edge1",
                                        "type":"EDGE",
                                        "connected":true,
                                        "processes":[
                                            {
                                                "id":"5c8276ba-0b78-429d-a0bf-d96f35ba2c77",
                                                "name":"zone2-edge1-iperf",
                                                "type":"EDGE-APP",
                                                "image":"meep-docker-registry:30001/iperf-server",
                                                "commandArguments":"-c, export; iperf -s -p $IPERF_SERVICE_PORT;",
                                                "commandExe":"/bin/bash",
                                                "serviceConfig":{
                                                    "name":"zone2-edge1-iperf",
                                                    "meSvcName":"iperf",
                                                    "ports":[
                                                        {
                                                            "protocol":"UDP",
                                                            "port":80
                                                        }
                                                    ]
                                                },
                                                "netChar":{
                                                    "throughputDl":1000,
                                                    "throughputUl":1000
                                                }
                                            },
                                            {
                                                "id":"53fa28f0-80e2-414c-8841-86db9bd37d51",
                                                "name":"zone2-edge1-svc",
                                                "type":"EDGE-APP",
                                                "image":"meep-docker-registry:30001/demo-server",
                                                "environment":"MGM_GROUP_NAME=svc, MGM_APP_ID=zone2-edge1-svc, MGM_APP_PORT=80",
                                                "serviceConfig":{
                                                    "name":"zone2-edge1-svc",
                                                    "meSvcName":"svc",
                                                    "ports":[
                                                        {
                                                            "protocol":"TCP",
                                                            "port":80
                                                        }
                                                    ]
                                                },
                                                "netChar":{
                                                    "throughputDl":1000,
                                                    "throughputUl":1000
                                                }
                                            }
                                        ],
                                        "netChar":{
                                            "throughputDl":1000,
                                            "throughputUl":1000
                                        }
                                    }
                                ]
                            },
                            {
                                "id":"c44b8937-58af-44b2-acdb-e4d1c4a1510b",
                                "name":"zone2-poa1",
                                "type":"POA",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":20,
                                    "throughputUl":20
                                }
                            }
                        ]
                    }
                ]
            },
            {
                "id":"e29138fb-cf03-4372-8335-fd2665b77a11",
                "name":"operator1",
                "type":"OPERATOR",
                "netChar":{
                    "latency":6,
                    "latencyVariation":2,
                    "throughputDl":1000,
                    "throughputUl":1000
                },
                "zones":[
                    {
                        "id":"operator1-COMMON",
                        "name":"operator1-COMMON",
                        "type":"COMMON",
                        "netChar":{
                            "latency":5,
                            "latencyVariation":1,
                            "throughputDl":1000,
                            "throughputUl":1000
                        },
                        "networkLocations":[
                            {
                                "id":"operator1-COMMON-DEFAULT",
                                "name":"operator1-COMMON-DEFAULT",
                                "type":"DEFAULT",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                }
                            }
                        ]
                    },
                    {
                        "id":"7d8bee73-6d5c-4c5a-a3a0-49ebe3cd2c71",
                        "name":"zone3",
                        "type":"ZONE",
                        "netChar":{
                            "latency":5,
                            "latencyVariation":1,
                            "throughputDl":1000,
                            "throughputUl":1000
                        },
                        "networkLocations":[
                            {
                                "id":"zone3-DEFAULT",
                                "name":"zone3-DEFAULT",
                                "type":"DEFAULT",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                }
                            },
                            {
                                "id":"ecc2a41b-7381-4108-a037-52862c520733",
                                "name":"poa1",
                                "type":"POA",
                                "netChar":{
                                    "latency":1,
                                    "latencyVariation":1,
                                    "throughputDl":1000,
                                    "throughputUl":1000
                                }
                            }
                        ]
                    }
                ]
            }
        ]
    }
}`

const redisTestAddr = "localhost:30380"
const influxTestAddr = "http://localhost:30986"
const testScenarioName = "testScenario"

var m *mod.Model
var mqLocal *mq.MsgQueue

const (
	postgresTestHost = "localhost"
	postgresTestPort = "30432"
)

const (
	associateDevAppId1 = "associateDevAppId1"
	callbackReference1 = "callbackReference1"
	//appLocationUpdates1   = true
	//appAutoInstantiation1 = false
	appName1        = "onboarded-demo4"
	appProvider1    = "ETSI"
	appDVersion1    = "v0.1.0"
	appDescription1 = "Basic HTTP Ping Pong"

	//associateDevAppId2    = "associateDevAppId2"
	callbackReference2 = "callbackReference2"
	//appLocationUpdates2   = false
	//appAutoInstantiation2 = false
	//appName2        = "appName2"
	//appProvider2    = "appProvider2"
	//appDVersion2    = "appDVersion2"
	//appDescription2 = "appDescription2"
)

var ( // Need to take address
	contextId1        string = "onboarded-demo4"
	appDId1           string = "onboarded-demo4"
	appSoftVersion1   string = "v0.1.0"
	appPackageSource1 string = "appPackageSource1"
	//appInstanceId1_1     string         = "appInstanceId1-1"
	//appInstanceId1_2     string         = "appInstanceId1-2"
	//appInstanceId1_3     string         = "appInstanceId1-3"
	referenceURI1_1 meepdaimgr.Uri = "referenceURI1-1"
	//referenceURI1_3      meepdaimgr.Uri = "referenceURI1-3"
	area1 = Polygon{[][][]float32{{{7.420433, 43.729942}, {7.420659, 43.73036}, {7.420621, 43.731045}, {7.420922, 43.73129}}, {{7.420434, 43.729943}, {7.420659, 43.73036}, {7.420621, 43.731045}, {7.420922, 43.73129}}}}
	//civicAddressElement1        = []LocationConstraintsCivicAddressElement{{1, "Value1"}, {10, "Value10"}}
	countryCode1 string = "33"
	memory1      int32  = 1024
	storage1     int32  = 1024
	latency1     int32  = 1024
	bandwidth1   int32  = 1024
	serviceCont1 int32  = 0

	//contextId2           string = "contextId2"
	//appDId2         string = "appDId2"
	//appSoftVersion2 string = "appSoftVersion2"
	//appPackageSource2    string = "appPackageSource2"
	//appInstanceId2_1     string = "appInstanceId2-1"
	//appInstanceId2_2     string = "appInstanceId2-2"
	//area2                       = Polygon{[][][]float32{{{7.43166, 43.736156}, {7.431723, 43.736115}, {7.431162, 43.735607}, {7.430685, 43.73518}}}}
	//civicAddressElement2        = []LocationConstraintsCivicAddressElement{{2, "Value2"}, {20, "Value20"}}
	//countryCode2         string = "countryCode2"
	//memory2              int32  = 1024 * 2
	//storage2             int32  = 1024 * 2
	//latency2             int32  = 1024 * 2
	//bandwidth2           int32  = 1024 * 2
	//serviceCont2         int32  = 0
)

func TestNotImplemented(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	_, err := sendRequest(http.MethodDelete, "/subscriptions/1", nil, nil, nil, http.StatusNotImplemented, IndividualSubscriptionDELETE)
	if err != nil {
		t.Fatalf("Failed to get expected response")
	}

}

func TestAppListGET(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	initializeVars()

	err := Init()
	if err != nil {
		t.Fatalf("Error initializing test basic procedure")
	}
	err = Run()
	if err != nil {
		t.Fatalf("Error running test basic procedure")
	}

	fmt.Println("Set a scenario")
	initialiseScenario(testScenario)

	time.Sleep(1000 * time.Millisecond)
	updateScenario("mobility1")

	/******************************
	 * expected response section
	 ******************************/
	// Fill LocationConstraints table
	appLocationConstraints := make([]LocationConstraints, 1)
	appLocationConstraints[0] = LocationConstraints{nil, nil, countryCode1}
	// Fill ApplicationListAppInfo
	var appInfo ApplicationListAppInfo
	appInfo.AppDId = appDId1
	appInfo.AppDVersion = appDVersion1
	appInfo.AppDescription = appDescription1
	appInfo.AppLocation = appLocationConstraints
	appInfo.AppName = appName1
	appInfo.AppProvider = appProvider1
	appInfo.AppSoftVersion = appSoftVersion1
	// Fill ApplicationListAppList
	var applicationListAppList ApplicationListAppList
	applicationListAppList.AppInfo = &appInfo
	applicationListAppList.VendorSpecificExt = nil
	appInfo.AppCharcs = new(ApplicationListAppInfoAppCharcs)
	appInfo.AppCharcs.Memory = memory1
	appInfo.AppCharcs.Storage = storage1
	appInfo.AppCharcs.Latency = latency1
	appInfo.AppCharcs.Bandwidth = bandwidth1
	appInfo.AppCharcs.ServiceCont = serviceCont1
	// ApplicationList
	var appList ApplicationList
	appList.AppList = make([]ApplicationListAppList, 1)
	appList.AppList[0] = applicationListAppList
	log.Info("ApplicationList: ", appList)

	// Convert into JSON
	var expected_json_response string = convertApplicationListToJson(&appList)
	log.Info("json response: ", expected_json_response)

	/******************************
	 * request body section
	 ******************************/

	/******************************
	 * request execution section
	 ******************************/
	rr, err := sendRequest(http.MethodGet, "/app_list?appName=\""+appName1+"\"", nil, nil, nil, http.StatusOK, MeAppListGET)
	if err != nil {
		t.Fatalf(err.Error())
	}
	log.Info("sendRequest done")

	var respBody ApplicationList
	err = json.Unmarshal([]byte(rr), &respBody)
	if err != nil {
		t.Fatalf(err.Error())
	}
	fmt.Println("respBody: ", respBody)
	receivedAppList := convertJsonToApplicationList(rr)
	if !validateApplicationList(receivedAppList, appList) {
		t.Errorf("handler returned unexpected body: got %v want %v", rr, expected_json_response)
	}
	log.Info("Received expected response")

	rr, err = sendRequest(http.MethodGet, "/app_list?appName=\"unknownApp\"", nil, nil, nil, http.StatusOK, MeAppListGET)
	if err != nil {
		t.Fatalf(err.Error())
	}
	log.Info("sendRequest done")

	err = json.Unmarshal([]byte(rr), &respBody)
	if err != nil {
		t.Fatalf(err.Error())
	}
	fmt.Println("respBody: ", respBody)
	receivedAppList = convertJsonToApplicationList(rr)
	var emptyApplicationList ApplicationList
	if !validateApplicationList(receivedAppList, emptyApplicationList) {
		t.Errorf("handler returned unexpected body: got %v want %v", rr, expected_json_response)
	}
	log.Info("Received expected response")

	/******************************
	 * back to initial state section
	 ******************************/
	terminateScenario()
}

func TestDevAppContextsPOST(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	initializeVars()

	err := Init()
	if err != nil {
		t.Fatalf("Error initializing test basic procedure")
	}
	err = Run()
	if err != nil {
		t.Fatalf("Error running test basic procedure")
	}

	fmt.Println("Set a scenario")
	initialiseScenario(testScenario)

	time.Sleep(1000 * time.Millisecond)
	updateScenario("mobility1")

	testDevAppContextsPOST(t)

	log.Info("Received expected response")

	/******************************
	 * back to initial state section
	 ******************************/
	terminateScenario()
}

func TestDevAppContextDELETE(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	initializeVars()

	err := Init()
	if err != nil {
		t.Fatalf("Error initializing test basic procedure")
	}
	err = Run()
	if err != nil {
		t.Fatalf("Error running test basic procedure")
	}

	fmt.Println("Set a scenario")
	initialiseScenario(testScenario)

	time.Sleep(1000 * time.Millisecond)
	updateScenario("mobility1")

	appContext := testDevAppContextsPOST(t)
	log.Info("Received expected response")

	/******************************
	 * expected response section
	 ******************************/

	/******************************
	 * request execution section
	 ******************************/

	/******************************
	 * request vars section
	 ******************************/
	vars := make(map[string]string)
	vars["contextId"] = appContext.ContextId

	_, err = sendRequest(http.MethodDelete, "/app_contexts", nil, vars, nil, http.StatusNoContent, DevAppContextDELETE)
	if err != nil {
		t.Fatalf(err.Error())
	}
	log.Info("sendRequest done")

	/******************************
	 * back to initial state section
	 ******************************/
	terminateScenario()
}

func TestDevAppContextPUT(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	initializeVars()

	err := Init()
	if err != nil {
		t.Fatalf("Error initializing test basic procedure")
	}
	err = Run()
	if err != nil {
		t.Fatalf("Error running test basic procedure")
	}

	fmt.Println("Set a scenario")
	initialiseScenario(testScenario)

	time.Sleep(1000 * time.Millisecond)
	updateScenario("mobility1")

	appContext := testDevAppContextsPOST(t)
	log.Info("Received expected response")

	/******************************
	 * expected response section
	 ******************************/

	/******************************
	 * request execution section
	 ******************************/

	/******************************
	 * request vars section
	 ******************************/
	vars := make(map[string]string)
	vars["contextId"] = appContext.ContextId

	// Update appContext.CallbackReference
	appContext.CallbackReference = callbackReference2
	log.Info("New appContext: ", appContext)
	// Convert into JSON
	body, err := json.Marshal(appContext)
	if err != nil {
		t.Fatalf(err.Error())
	}

	/******************************
	 * request execution section
	 ******************************/
	_, err = sendRequest(http.MethodPut, "/app_contexts", bytes.NewBuffer(body), vars, nil, http.StatusNoContent, DevAppContextPUT)
	if err != nil {
		t.Fatalf(err.Error())
	}
	log.Info("sendRequest done")

	/******************************
	 * back to initial state section
	 ******************************/
	terminateScenario()
}

func testDevAppContextsPOST(t *testing.T) *AppContext {

	/******************************
	 * expected response section
	 ******************************/
	// Fill LocationConstraints
	var expected_appLocationConstraints = LocationConstraints{&area1, make([]LocationConstraintsCivicAddressElement, 0), countryCode1}
	// Fill UserAppInstanceInfo
	expected_appContextAppInfoUserAppInstanceInfo := make([]AppContextAppInfoUserAppInstanceInfo, 1)
	expected_appContextAppInfoUserAppInstanceInfo[0].AppLocation = &expected_appLocationConstraints
	expected_appContextAppInfoUserAppInstanceInfo[0].ReferenceURI = string(referenceURI1_1)
	// Fill AppContextAppInfo
	var expected_appContextAppInfo AppContextAppInfo
	expected_appContextAppInfo.AppDId = appDId1
	expected_appContextAppInfo.AppDVersion = appDVersion1
	expected_appContextAppInfo.AppDescription = appDescription1
	expected_appContextAppInfo.AppName = appName1
	expected_appContextAppInfo.AppProvider = appProvider1
	expected_appContextAppInfo.AppSoftVersion = appSoftVersion1
	expected_appContextAppInfo.AppPackageSource = appPackageSource1
	expected_appContextAppInfo.UserAppInstanceInfo = expected_appContextAppInfoUserAppInstanceInfo
	// Fill AppContext
	var expected_appContext AppContext
	expected_appContext.AppAutoInstantiation = false
	expected_appContext.AppInfo = &expected_appContextAppInfo
	expected_appContext.AppLocationUpdates = true
	expected_appContext.AssociateDevAppId = associateDevAppId1
	expected_appContext.CallbackReference = callbackReference1
	expected_appContext.ContextId = contextId1
	log.Info("expected_appContext: ", expected_appContext)
	// Convert into JSON
	var expected_json_response string = convertAppContextToJson(&expected_appContext)
	log.Info("json response: ", expected_json_response)

	/******************************
	 * request body section
	 ******************************/
	// Fill LocationConstraints
	var appLocationConstraints = LocationConstraints{&area1, make([]LocationConstraintsCivicAddressElement, 0), countryCode1}
	// Fill UserAppInstanceInfo
	appContextAppInfoUserAppInstanceInfo := make([]AppContextAppInfoUserAppInstanceInfo, 1)
	appContextAppInfoUserAppInstanceInfo[0].AppLocation = &appLocationConstraints
	appContextAppInfoUserAppInstanceInfo[0].ReferenceURI = string(referenceURI1_1)
	// Fill AppContextAppInfo
	var appContextAppInfo AppContextAppInfo
	appContextAppInfo.AppDId = appDId1
	appContextAppInfo.AppDVersion = appDVersion1
	appContextAppInfo.AppDescription = appDescription1
	appContextAppInfo.AppName = appName1
	appContextAppInfo.AppProvider = appProvider1
	appContextAppInfo.AppSoftVersion = appSoftVersion1
	appContextAppInfo.AppPackageSource = appPackageSource1
	appContextAppInfo.UserAppInstanceInfo = appContextAppInfoUserAppInstanceInfo
	// Fill AppContext
	var appContext AppContext
	appContext.AppAutoInstantiation = false
	appContext.AppInfo = &appContextAppInfo
	appContext.AppLocationUpdates = true
	appContext.AssociateDevAppId = associateDevAppId1
	appContext.CallbackReference = callbackReference1
	log.Info("appContext: ", appContext)
	// Convert into JSON
	body, err := json.Marshal(appContext)
	if err != nil {
		t.Fatalf(err.Error())
	}

	/******************************
	 * request execution section
	 ******************************/
	rr, err := sendRequest(http.MethodPost, "/app_contexts", bytes.NewBuffer(body), nil, nil, http.StatusCreated, DevAppContextsPOST)
	if err != nil {
		t.Fatalf(err.Error())
	}
	log.Info("sendRequest done")

	var respBody AppContext
	err = json.Unmarshal([]byte(rr), &respBody)
	if err != nil {
		t.Fatalf(err.Error())
	}
	fmt.Println("respBody: ", respBody)
	receivedAppContext := convertJsonToAppContext(rr)
	if !validateAppContext(receivedAppContext, appContext) {
		t.Errorf("handler returned unexpected body: got %v want %v", rr, expected_json_response)
	}

	return receivedAppContext
}

func initializeVars() {
	mod.DbAddress = redisTestAddr
	redisAddr = redisTestAddr
	influxAddr = influxTestAddr
	sandboxName = testScenarioName
	os.Setenv("MEEP_SANDBOX_NAME", testScenarioName)
	postgresHost = postgresTestHost
	postgresPort = postgresTestPort

}

func initialiseScenario(testScenario string) {

	//clear DB
	cleanUp()

	cfg := mod.ModelCfg{
		Name:      testScenarioName,
		Namespace: sandboxName,
		Module:    "test-mod",
		UpdateCb:  nil,
		DbAddr:    redisAddr,
	}

	var err error
	m, err = mod.NewModel(cfg)
	if err != nil {
		log.Error("Failed to create model: ", err)
		return
	}
	log.Info("initialiseScenario: model created")

	// Create message queue
	mqLocal, err = mq.NewMsgQueue(mq.GetLocalName(testScenarioName), "test-mod", testScenarioName, redisAddr)
	if err != nil {
		log.Error("Failed to create Message Queue with error: ", err)
		return
	}
	log.Info("Message Queue created")

	fmt.Println("Set Model")
	err = m.SetScenario([]byte(testScenario))
	if err != nil {
		log.Error("Failed to set model: ", err)
		return
	}

	err = m.Activate()
	if err != nil {
		log.Error("Failed to activate scenario with err: ", err.Error())
		return
	}

	msg := mqLocal.CreateMsg(mq.MsgScenarioActivate, mq.TargetAll, testScenarioName)
	err = mqLocal.SendMsg(msg)
	if err != nil {
		log.Error("Failed to send message: ", err)
		return
	}

	time.Sleep(100 * time.Millisecond)

}

func updateScenario(testUpdate string) {

	switch testUpdate {
	case "mobility1":
		// mobility event of ue1 to zone2-poa1
		elemName := "ue1"
		destName := "zone2-poa1"

		_, _, err := m.MoveNode(elemName, destName, nil)
		if err != nil {
			log.Error("Error sending mobility event")
		}

		msg := mqLocal.CreateMsg(mq.MsgScenarioUpdate, mq.TargetAll, testScenarioName)
		err = mqLocal.SendMsg(msg)
		if err != nil {
			log.Error("Failed to send message: ", err)
		}
	case "mobility2":
		// mobility event of ue1 to zone2-poa1
		elemName := "ue1"
		destName := "zone1-poa-cell1"

		_, _, err := m.MoveNode(elemName, destName, nil)
		if err != nil {
			log.Error("Error sending mobility event")
		}

		msg := mqLocal.CreateMsg(mq.MsgScenarioUpdate, mq.TargetAll, testScenarioName)
		err = mqLocal.SendMsg(msg)
		if err != nil {
			log.Error("Failed to send message: ", err)
		}
	case "mobility3":
		// mobility event of ue1 to zone1-poa-cell2
		elemName := "ue1"
		destName := "zone1-poa-cell2"

		_, _, err := m.MoveNode(elemName, destName, nil)
		if err != nil {
			log.Error("Error sending mobility event")
		}

		msg := mqLocal.CreateMsg(mq.MsgScenarioUpdate, mq.TargetAll, testScenarioName)
		err = mqLocal.SendMsg(msg)
		if err != nil {
			log.Error("Failed to send message: ", err)
		}
	default:
	}
	time.Sleep(100 * time.Millisecond)
}

func terminateScenario() {
	if mqLocal != nil {
		_ = Stop()
		msg := mqLocal.CreateMsg(mq.MsgScenarioTerminate, mq.TargetAll, testScenarioName)
		err := mqLocal.SendMsg(msg)
		if err != nil {
			log.Error("Failed to send message: ", err)
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func sendRequest(method string, url string, body io.Reader, vars map[string]string, query map[string]string, code int, f http.HandlerFunc) (string, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil || req == nil {
		return "", err
	}
	if vars != nil {
		req = mux.SetURLVars(req, vars)
	}
	if query != nil {
		q := req.URL.Query()
		for k, v := range query {
			q.Add(k, v)
		}
		req.URL.RawQuery = q.Encode()
	}

	// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(f)

	// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
	// directly and pass in our Request and ResponseRecorder.
	handler.ServeHTTP(rr, req)

	time.Sleep(50 * time.Millisecond)

	// Check the status code is what we expect.
	if status := rr.Code; status != code {
		s := fmt.Sprintf("Wrong status code - got %v want %v", status, code)
		return "", errors.New(s)
	}
	return string(rr.Body.String()), nil
}

func validateApplicationList(appInfoListEntry *ApplicationList, appInfoList ApplicationList) bool {

	if appInfoListEntry == nil {
		fmt.Println("appInfoListEntry == nil")
		return false
	}

	if len(appInfoListEntry.AppList) != len(appInfoList.AppList) {
		fmt.Println("len(appInfoListEntry.AppList) != len(appInfoList.AppList)")
		return false
	}

	for i, appList := range appInfoListEntry.AppList {

		if appList.VendorSpecificExt != appInfoList.AppList[i].VendorSpecificExt {
			fmt.Println("appList.VendorSpecificExt != appInfoList.AppList.VendorSpecificExt")
			return false
		}

		if appList.AppInfo != nil && appInfoList.AppList[i].AppInfo != nil {
			if appList.AppInfo.AppDId != appInfoList.AppList[i].AppInfo.AppDId {
				fmt.Println("appList.AppInfo.AppDId != appInfoList.AppDId")
				return false
			}
			if appList.AppInfo.AppName != appInfoList.AppList[i].AppInfo.AppName {
				fmt.Println("appList.AppInfo.AppName != appInfoList.AppName")
				return false
			}
			if appList.AppInfo.AppProvider != appInfoList.AppList[i].AppInfo.AppProvider {
				fmt.Println("appList.AppInfo.AppProvider != appInfoList.AppProvider")
				return false
			}
			if appList.AppInfo.AppSoftVersion != appInfoList.AppList[i].AppInfo.AppSoftVersion {
				fmt.Println("appList.AppInfo.AppSoftVersion != appInfoList.AppSoftVersion")
				return false
			}
			if appList.AppInfo.AppDVersion != appInfoList.AppList[i].AppInfo.AppDVersion {
				fmt.Println("appList.AppInfo.AppDVersion != appInfoList.AppDVersion")
				return false
			}
			if appList.AppInfo.AppDescription != appInfoList.AppList[i].AppInfo.AppDescription {
				fmt.Println("appList.AppInfo.AppDescription != appInfoList.AppDescription")
				return false
			}
			if appList.AppInfo.AppCharcs != nil && appInfoList.AppList[i].AppInfo.AppCharcs != nil {
				if appList.AppInfo.AppCharcs.Memory != appInfoList.AppList[i].AppInfo.AppCharcs.Memory {
					fmt.Println("len(appList.AppInfo.AppCharcs.Memory) != len(appInfoList.AppCharcs.Memory)")
					return false
				}
				if appList.AppInfo.AppCharcs.Storage != appInfoList.AppList[i].AppInfo.AppCharcs.Storage {
					fmt.Println("len(appList.AppInfo.AppCharcs.Storage) != len(appInfoList.AppCharcs.Storage)")
					return false
				}
				if appList.AppInfo.AppCharcs.Latency != appInfoList.AppList[i].AppInfo.AppCharcs.Latency {
					fmt.Println("len(appList.AppInfo.AppCharcs.Latency) != len(appInfoList.AppCharcs.Latency)")
					return false
				}
				if appList.AppInfo.AppCharcs.Bandwidth != appInfoList.AppList[i].AppInfo.AppCharcs.Bandwidth {
					fmt.Println("len(appList.AppInfo.AppCharcs.Bandwidth) != len(appInfoList.AppCharcs.Bandwidth)")
					return false
				}
				if appList.AppInfo.AppCharcs.ServiceCont != appInfoList.AppList[i].AppInfo.AppCharcs.ServiceCont {
					fmt.Println("len(appList.AppInfo.AppCharcs.ServiceCont) != len(appInfoList.AppCharcs.ServiceCont)")
					return false
				}
			} else if (appList.AppInfo.AppCharcs == nil) != (appInfoList.AppList[i].AppInfo.AppCharcs == nil) {
				fmt.Println("appList.AppInfo.AppCharcs != appInfoList.AppList.AppInfo.AppCharcs")
				return false
			}
			fmt.Println("===> len(appList.AppInfo.AppLocation): ", len(appList.AppInfo.AppLocation))
			fmt.Println("===> len(appInfoList.AppList[i].AppInfo.AppLocation): ", len(appInfoList.AppList[i].AppInfo.AppLocation))
			if len(appList.AppInfo.AppLocation) != len(appInfoList.AppList[i].AppInfo.AppLocation) {
				fmt.Println("len(appList.AppInfo.AppLocation) != len(appInfoList.AppList[i].AppInfo.AppLocation)")
				return false
			}
			fmt.Println("validateApplicationList: appList.AppInfo.AppLocation:                ", appList.AppInfo.AppLocation)
			fmt.Println("validateApplicationList: appInfoList.AppList[i].AppInfo.AppLocation: ", appInfoList.AppList[i].AppInfo.AppLocation)
			for j, appLocation := range appList.AppInfo.AppLocation {
				if appLocation.Area != nil && appInfoList.AppList[i].AppInfo.AppLocation[j].Area != nil {
					if len(appLocation.Area.Coordinates) != len(appInfoList.AppList[i].AppInfo.AppLocation[j].Area.Coordinates) {
						fmt.Println("len(appLocation.Area.Coordinates) != len(appInfoList.AppList.AppInfo.AppLocation.Area.Coordinates)")
						return false
					}
					// TODO Compare content
				} else if (appLocation.Area == nil) != (appInfoList.AppList[i].AppInfo.AppLocation[j].Area == nil) {
					fmt.Println("appLocation.Area != appInfoList.AppList.AppInfo.AppLocation.Area")
					return false
				}

				if len(appLocation.CivicAddressElement) != len(appInfoList.AppList[i].AppInfo.AppLocation[j].CivicAddressElement) {
					fmt.Println("len(appLocation.CivicAddressElement) != len(appInfoList.AppList.AppInfo.AppLocation.CivicAddressElement")
					return false
				}
				for k, cv := range appLocation.CivicAddressElement {
					if cv.CaType != appInfoList.AppList[i].AppInfo.AppLocation[j].CivicAddressElement[k].CaType || cv.CaValue != appInfoList.AppList[i].AppInfo.AppLocation[j].CivicAddressElement[k].CaValue {
						fmt.Println("cv.CivicAddressElement != appContext.AppInfo.UserAppInstanceInfo[i].AppLocation[j].CivicAddressElement")
						return false
					}
				} // End of 'for' statement

				if appLocation.CountryCode != appInfoList.AppList[i].AppInfo.AppLocation[j].CountryCode {
					fmt.Println("appLocation.CountryCode != appInfoList.AppList.AppInfo.AppLocation.CountryCode")
					return false
				}
			} // End of 'for' statement
		} else if (appList.AppInfo == nil) != (appInfoList.AppList[i].AppInfo == nil) {
			fmt.Println("appList.AppInfo != appInfoList.AppList.AppInfo.AppInfo")
			return false
		}

	} // End of 'for' statement

	return true
}

func validateAppContext(appContextEntry *AppContext, appContext AppContext) bool {
	log.Debug(">>> validateAppContext: appContextEntry: ", appContextEntry)
	log.Debug(">>> validateAppContext: appContext: ", appContext)

	if appContextEntry == nil {
		fmt.Println("appContextEntry == nil")
		return false
	}
	if appContextEntry.ContextId == "" || appContextEntry.ContextId == appContext.ContextId {
		fmt.Println("appContextEntry.ContextId != ContextId")
		return false
	}
	if appContextEntry.AssociateDevAppId != appContext.AssociateDevAppId {
		fmt.Println("appContextEntry.AssociateDevAppId != AssociateDevAppId")
		return false
	}
	if appContextEntry.CallbackReference != appContext.CallbackReference {
		fmt.Println("appContextEntry.CallbackReference != CallbackReference")
		return false
	}
	if appContextEntry.AppLocationUpdates != appContext.AppLocationUpdates {
		fmt.Println("appContextEntry.AppLocationUpdates != AppLocationUpdates")
		return false
	}
	if appContextEntry.AppAutoInstantiation != appContext.AppAutoInstantiation {
		fmt.Println("appContextEntry.AppAutoInstantiation != AppAutoInstantiation")
		return false
	}

	if appContextEntry.AppInfo.AppDId != appContext.AppInfo.AppDId {
		fmt.Println("appContextEntry.AppInfo.AppDId != AppInfo.AppDId")
		return false
	}
	if appContextEntry.AppInfo.AppName != appContext.AppInfo.AppName {
		fmt.Println("appContextEntry.AppInfo.AppName != AppInfo.AppName")
		return false
	}
	if appContextEntry.AppInfo.AppProvider != appContext.AppInfo.AppProvider {
		fmt.Println("appContextEntry.AppInfo.AppProvider != AppInfo.AppProvider")
		return false
	}
	if appContextEntry.AppInfo.AppSoftVersion != appContext.AppInfo.AppSoftVersion {
		fmt.Println("appContextEntry.AppInfo.AppSoftVersion != AppInfo.AppSoftVersion")
		return false
	}
	if appContextEntry.AppInfo.AppDVersion != appContext.AppInfo.AppDVersion {
		fmt.Println("appContextEntry.AppInfo.AppDVersion != AppInfo.AppDVersion")
		return false
	}
	if appContextEntry.AppInfo.AppDescription != appContext.AppInfo.AppDescription {
		fmt.Println("appContextEntry.AppInfo.AppDescription != AppInfo.AppDescription")
		return false
	}
	if appContextEntry.AppInfo.AppPackageSource != appContext.AppInfo.AppPackageSource {
		fmt.Println("appContextEntry.AppInfo.AppPackageSource != AppInfo.AppPackageSource")
		return false
	}
	if appContextEntry.AppInfo.UserAppInstanceInfo == nil || appContext.AppInfo.UserAppInstanceInfo == nil {
		fmt.Println("appContextEntry.AppInfo.UserAppInstanceInfo != AppInfo.UserAppInstanceInfo")
		return false
	}
	if len(appContextEntry.AppInfo.UserAppInstanceInfo) == 0 || len(appContext.AppInfo.UserAppInstanceInfo) == 0 {
		fmt.Println("appContextEntry.AppInfo.UserAppInstanceInfo len shall be at leat one")
		return false
	} else if len(appContextEntry.AppInfo.UserAppInstanceInfo) != len(appContext.AppInfo.UserAppInstanceInfo) {
		fmt.Println("len(appContextEntry.AppInfo.UserAppInstanceInfo) != len(AppInfo.UserAppInstanceInfo)")
		return false
	}
	for i, item := range appContextEntry.AppInfo.UserAppInstanceInfo {
		if item.AppInstanceId == "" {
			fmt.Println("item.AppInstanceId empty")
			return false
		}
		if item.ReferenceURI == "" {
			fmt.Println("item.ReferenceURI empty")
			return false
		}
		if item.AppLocation != nil && appContext.AppInfo.UserAppInstanceInfo[i].AppLocation != nil {
			if item.AppLocation.Area != nil && appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.Area != nil {
				if len(item.AppLocation.Area.Coordinates) != len(appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.Area.Coordinates) {
					fmt.Println("len(item.AppLocation.Area.Coordinates) != len(appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.Area.Coordinates)")
					return false
				}
				// TODO Compare content
			} else if (item.AppLocation.Area == nil) != (appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.Area == nil) {
				fmt.Println("item.AppLocation.Area != appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.Area")
				return false
			}

			if len(item.AppLocation.CivicAddressElement) != len(appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.CivicAddressElement) {
				fmt.Println("len(item.AppLocation.CivicAddressElement) != len(appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.CivicAddressElement")
				return false
			}
			appContextCivicAddressElements := appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.CivicAddressElement
			for k, cv := range item.AppLocation.CivicAddressElement {
				fmt.Println("validateAppContexts: Process item k#", k)
				fmt.Println("validateAppContexts: cv: ", cv)
				fmt.Println("validateAppContexts: civicAddressElements[k]: ", appContextCivicAddressElements)
				if cv != appContextCivicAddressElements[k] {
					fmt.Println("cv.CivicAddressElement != appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.CivicAddressElement")
					return false
				}
			} // End of 'for' statement

			if item.AppLocation.CountryCode != appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.CountryCode {
				fmt.Println("item.AppLocation.CountryCode != appContext.AppInfo.UserAppInstanceInfo[i].AppLocation.CountryCode")
				return false
			}
		}

	} // End of 'for' statement

	return true
}
