/*
 * Copyright (c) 2022  InterDigital Communications, Inc
 *
 * 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.
 *
 * AdvantEDGE Bandwidth Management API
 *
 * Bandwidth Management Sercice is AdvantEDGE's implementation of [ETSI MEC ISG MEC015 Traffic Management APIs](https://www.etsi.org/deliver/etsi_gs/MEC/001_099/015/02.02.01_60/gs_MEC015v020201p.pdf) <p>[Copyright (c) ETSI 2017](https://forge.etsi.org/etsi-forge-copyright-notice.txt) <p>**Micro-service**<br>[meep-tm](https://github.com/InterDigitalInc/AdvantEDGE/tree/master/go-apps/meep-tm/server/bwm) <p>**Type & Usage**<br>Edge Service used by edge applications that want to get information about BWM Info and Session(s) in the network <p>**Note**<br>AdvantEDGE supports all Bandwidth Management API endpoints.
 *
 * API version: 2.2.1
 * Contact: AdvantEDGE@InterDigital.com
 * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
 */

package server

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	asc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-app-support-client"
	dkm "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-data-key-mgr"
	dataModel "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-data-model"
	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
	mod "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-model"
	redis "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-redis"
	scc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sandbox-ctrl-client"
	smc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-service-mgmt-client"
	sam "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-swagger-api-mgr"
	"github.com/gorilla/mux"
)

const moduleName = "meep-tm"
const bwmBasePath = "bwm/v1/"
const globalMepName = "global"

const defaultMepName = "global"
const defaultScopeOfLocality = "MEC_SYSTEM"
const defaultConsumedLocalOnly = true
const appTerminationPath = "notifications/mec011/appTermination"

const serviceCategory = "BWM"

var sbxCtrlUrl string = "http://meep-sandbox-ctrl"

var instanceId string
var instanceName string

var appEnablementUrl string
var appEnablementEnabled bool

// LinkBuff to initialize uplink/downlink buffer values
var LinkBuff = make(map[string]uint64)

//i.e. 100 Gbits/s equal to 100 * 1024 * 1024 * 1024 b/s shows maximum Bandwidth Resorce
var maxBuff = uint64(107374182400)
var nextBwAllocIdAvailable uint32 = 1

type sessionFilterListCheck struct {
	sessionBool bool
	SessionList []BwInfoSessionFilter
}

type BwAllocInfoResp struct {
	AppInstanceId []string
	SessionId     []string
	AppName       []string
	SessionList   []BwInfo
}
type SessionFilterPostResp struct {
	SessionFilterList []BwInfoSessionFilter
	SessionMatch      bool
}

type SessionFilterResp struct {
	SessionFilterList []BwInfoSessionFilter
	SessionMatch      bool
	AllocationId      string
	AllocationIdMatch bool
}

var localityEnabled bool
var mapLocality map[string]bool

// App Ins ID fields
const fieldState = "state"
const APP_STATE_READY = "READY"
const appEnablementKey = "app-enablement"

var activeModel *mod.Model
var apiMgr *sam.SwaggerApiMgr
var appSupportClient *asc.APIClient
var svcMgmtClient *smc.APIClient
var sbxCtrlClient *scc.APIClient
var appEnablementServiceId string
var appTermSubId string
var sendAppTerminationWhenDone bool = false
var serviceAppInstanceId string

var registrationTicker *time.Ticker
var rc *redis.Connector
var hostUrl *url.URL
var sandboxName string
var mepName string = defaultMepName
var scopeOfLocality string = defaultScopeOfLocality
var consumedLocalOnly bool = defaultConsumedLocalOnly
var locality []string
var basePath string
var baseKey string

const serviceAppVersion = "2.2.1"

type InitCfg struct {
	SandboxName  string
	MepName      string
	InstanceId   string
	InstanceName string
	BaseKey      string
	HostUrl      *url.URL
	RedisConn    *redis.Connector
	Model        *mod.Model
}

func getAppInstanceId() (id string, err error) {
	var appInfo scc.ApplicationInfo
	appInfo.Id = instanceId
	appInfo.Name = serviceCategory
	appInfo.Type_ = "SYSTEM"
	appInfo.NodeName = mepName
	if mepName == defaultMepName {
		appInfo.Persist = true
	} else {
		appInfo.Persist = false
	}
	response, _, err := sbxCtrlClient.ApplicationsApi.ApplicationsPOST(context.TODO(), appInfo)
	if err != nil {
		log.Error("Failed to get App Instance ID with error: ", err)
		return "", err
	}
	return response.Id, nil
}

func registerService(appInstanceId string) error {
	// Build Service Info
	state := smc.ACTIVE_ServiceState
	serializer := smc.JSON_SerializerType
	transportType := smc.REST_HTTP_TransportType
	localityType := smc.LocalityType(scopeOfLocality)
	srvInfo := smc.ServiceInfoPost{
		SerName:           instanceName,
		Version:           serviceAppVersion,
		State:             &state,
		Serializer:        &serializer,
		ScopeOfLocality:   &localityType,
		ConsumedLocalOnly: consumedLocalOnly,
		TransportInfo: &smc.TransportInfo{
			Id:       "sandboxTransport",
			Name:     "REST",
			Type_:    &transportType,
			Protocol: "HTTP",
			Version:  "2.0",
			Endpoint: &smc.OneOfTransportInfoEndpoint{},
		},
		SerCategory: &smc.CategoryRef{
			Href:    "catalogueHref",
			Id:      "bwmId",
			Name:    serviceCategory,
			Version: "v1",
		},
	}
	srvInfo.TransportInfo.Endpoint.Uris = append(srvInfo.TransportInfo.Endpoint.Uris, hostUrl.String()+basePath)

	appServicesPostResponse, _, err := svcMgmtClient.MecServiceMgmtApi.AppServicesPOST(context.TODO(), srvInfo, appInstanceId)
	if err != nil {
		log.Error("Failed to register the service to app enablement registry: ", err)
		return err
	}
	log.Info("Application Enablement Service instance Id: ", appServicesPostResponse.SerInstanceId)
	appEnablementServiceId = appServicesPostResponse.SerInstanceId
	return nil
}

func sendReadyConfirmation(appInstanceId string) error {
	var appReady asc.AppReadyConfirmation
	appReady.Indication = "READY"
	_, err := appSupportClient.MecAppSupportApi.ApplicationsConfirmReadyPOST(context.TODO(), appReady, appInstanceId)
	if err != nil {
		log.Error("Failed to send a ready confirm acknowlegement: ", err)
		return err
	}
	return nil
}

func subscribeAppTermination(appInstanceId string) error {
	var sub asc.AppTerminationNotificationSubscription
	sub.SubscriptionType = "AppTerminationNotificationSubscription"
	sub.AppInstanceId = appInstanceId
	if mepName == defaultMepName {
		sub.CallbackReference = "http://" + moduleName + "/" + bwmBasePath + appTerminationPath
	} else {
		sub.CallbackReference = "http://" + mepName + "-" + moduleName + "/" + bwmBasePath + appTerminationPath
	}
	subscription, _, err := appSupportClient.MecAppSupportApi.ApplicationsSubscriptionsPOST(context.TODO(), sub, appInstanceId)
	if err != nil {
		log.Error("Failed to register to App Support subscription: ", err)
		return err
	}
	appTermSubLink := subscription.Links.Self.Href
	appTermSubId = appTermSubLink[strings.LastIndex(appTermSubLink, "/")+1:]
	return nil
}

func Init(bwmCfg InitCfg) (err error) {
	sandboxName = bwmCfg.SandboxName
	hostUrl = bwmCfg.HostUrl
	mepName = bwmCfg.MepName
	instanceId = bwmCfg.InstanceId
	instanceName = bwmCfg.InstanceName
	baseKey = bwmCfg.BaseKey
	rc = bwmCfg.RedisConn
	activeModel = bwmCfg.Model

	// Get App Enablement URL
	appEnablementEnabled = false
	appEnablementEnv := strings.TrimSpace(os.Getenv("MEEP_APP_ENABLEMENT"))
	if appEnablementEnv != "" {
		appEnablementUrl = "http://" + appEnablementEnv
		appEnablementEnabled = true
	}
	log.Info("MEEP_APP_ENABLEMENT: ", appEnablementUrl)

	// Get scope of locality
	scopeOfLocalityEnv := strings.TrimSpace(os.Getenv("MEEP_SCOPE_OF_LOCALITY"))
	if scopeOfLocalityEnv != "" {
		scopeOfLocality = scopeOfLocalityEnv
	}
	log.Info("MEEP_SCOPE_OF_LOCALITY: ", scopeOfLocality)

	// Get local consumption
	consumedLocalOnlyEnv := strings.TrimSpace(os.Getenv("MEEP_CONSUMED_LOCAL_ONLY"))
	if consumedLocalOnlyEnv != "" {
		value, err := strconv.ParseBool(consumedLocalOnlyEnv)
		if err == nil {
			consumedLocalOnly = value
		}
	}
	log.Info("MEEP_CONSUMED_LOCAL_ONLY: ", consumedLocalOnly)

	// Get locality
	localityEnv := strings.TrimSpace(os.Getenv("MEEP_LOCALITY"))
	if localityEnv != "" {
		locality = strings.Split(localityEnv, ":")
	}
	log.Info("MEEP_LOCALITY: ", locality)

	// Set base path
	if mepName == globalMepName {
		basePath = "/" + sandboxName + "/" + bwmBasePath
	} else {
		basePath = "/" + sandboxName + "/" + mepName + "/" + bwmBasePath
	}

	// Create App Enablement REST clients
	if appEnablementEnabled {
		// Create Sandbox Controller client
		sbxCtrlClientCfg := scc.NewConfiguration()
		sbxCtrlClientCfg.BasePath = sbxCtrlUrl + "/sandbox-ctrl/v1"
		sbxCtrlClient = scc.NewAPIClient(sbxCtrlClientCfg)
		if sbxCtrlClient == nil {
			return errors.New("Failed to create Sandbox Controller REST API client")
		}
		log.Info("Create Sandbox Controller REST API client")

		// Create App Support client
		appSupportClientCfg := asc.NewConfiguration()
		appSupportClientCfg.BasePath = appEnablementUrl + "/mec_app_support/v1"
		appSupportClient = asc.NewAPIClient(appSupportClientCfg)
		if appSupportClient == nil {
			return errors.New("Failed to create App Enablement App Support REST API client")
		}
		log.Info("Create App Enablement App Support REST API client")

		// Create App Info client
		srvMgmtClientCfg := smc.NewConfiguration()
		srvMgmtClientCfg.BasePath = appEnablementUrl + "/mec_service_mgmt/v1"
		svcMgmtClient = smc.NewAPIClient(srvMgmtClientCfg)
		if svcMgmtClient == nil {
			return errors.New("Failed to create App Enablement Service Management REST API client")
		}
		log.Info("Create App Enablement Service Management REST API client")
	}

	log.Info("BWM successfully initialized")

	err = IntializeBwBuffer()
	if err != nil {
		log.Error("Failed to initialize Bandwidth Buffer: ", err)
		return err
	}

	return nil
}

// Run - Starts Bandwidth Management
func Run() (err error) {

	// Start MEC Service registration ticker
	if appEnablementEnabled {
		startRegistrationTicker()
	}

	return nil
}

// Stop - Stops Bandwidth Management
func Stop() (err error) {
	// Stop MEC Service registration ticker
	if appEnablementEnabled {
		stopRegistrationTicker()
	}

	if apiMgr != nil {
		// Remove APIs
		err = apiMgr.RemoveApis()
		if err != nil {
			log.Error("Failed to remove APIs with err: ", err.Error())
			return err
		}
	}

	return nil
}

func startRegistrationTicker() {
	// Make sure ticker is not running
	if registrationTicker != nil {
		log.Warn("Registration ticker already running")
		return
	}

	// Wait a few seconds to allow App Enablement Service to start.
	// This is done to avoid the default 20 second TCP socket connect timeout
	// if the App Enablement Service is not yet running.
	log.Info("Waiting for App Enablement Service to start")
	time.Sleep(5 * time.Second)

	// Start registration ticker
	registrationTicker = time.NewTicker(5 * time.Second)
	go func() {
		mecAppReadySent := false
		registrationSent := false
		subscriptionSent := false
		for range registrationTicker.C {
			// Get Application instance ID
			if serviceAppInstanceId == "" {
				// If a sandbox service, request an app instance ID from Sandbox Controller
				// Otherwise use the scenario-provisioned instance ID
				if mepName == defaultMepName {
					var err error
					serviceAppInstanceId, err = getAppInstanceId()
					if err != nil || serviceAppInstanceId == "" {
						continue
					}
				} else {
					serviceAppInstanceId = instanceId
				}
			}

			// Send App Ready message
			if !mecAppReadySent {
				err := sendReadyConfirmation(serviceAppInstanceId)
				if err != nil {
					log.Error("Failure when sending the MecAppReady message. Error: ", err)
					continue
				}
				mecAppReadySent = true
			}

			// Register service instance
			if !registrationSent {
				err := registerService(serviceAppInstanceId)
				if err != nil {
					log.Error("Failed to register to appEnablement DB, keep trying. Error: ", err)
					continue
				}
				registrationSent = true
			}

			// Register for graceful termination
			if !subscriptionSent {
				err := subscribeAppTermination(serviceAppInstanceId)
				if err != nil {
					log.Error("Failed to subscribe to graceful termination. Error: ", err)
					continue
				}
				sendAppTerminationWhenDone = true
				subscriptionSent = true
			}

			if mecAppReadySent && registrationSent && subscriptionSent {

				// Registration complete
				log.Info("Successfully registered with App Enablement Service")
				stopRegistrationTicker()
				return
			}
		}
	}()
}

func stopRegistrationTicker() {
	if registrationTicker != nil {
		log.Info("Stopping App Enablement registration ticker")
		registrationTicker.Stop()
		registrationTicker = nil
	}
}

/**
* IntializeBwBuffer initializes bandwidth buffer with maximum value and stores bandwidth buffer in redis
* @return {String} error It returns error message if bandwidth buffer is not stored properly in redis
 */
func IntializeBwBuffer() error {

	//Convert value type to interface{} before storing bandwidth buffer information

	LinkBuff["up"] = maxBuff
	LinkBuff["down"] = maxBuff

	entry := make(map[string]interface{}, len(LinkBuff))
	for k, v := range LinkBuff {
		entry[k] = v
	}

	key := baseKey + "linkbuf:"
	err := rc.SetEntry(key, entry)
	if err != nil {
		return err
	}

	return nil
}

/*
* updateBuffer stores the updated values of bandwidth buffer
* @param map[string]string buff	Bandwidth buffer
* @return {String} error It returns error message if bandwidth buffer is not stored properly in redis
 */
func updateBuffer(buff map[string]string) error {
	entry := make(map[string]interface{}, len(buff))
	for k, v := range buff {
		entry[k] = v
	}

	key := baseKey + "linkbuf:"
	err := rc.SetEntry(key, entry)
	if err != nil {
		return err
	}

	return nil
}

func mec011AppTerminationPost(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
}

// bandwidthAllocationDelete removes a specific bandwidthAllocation at /bw_allocations/{allocationId} endpoint
func bandwidthAllocationDelete(w http.ResponseWriter, r *http.Request) {
	log.Info("Delete bwInfo by allocationId")

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	vars := mux.Vars(r)
	bwallocIdStr := vars["allocationId"]

	// Information of bandwidth allocation of specific allocationId is fetched
	keyName := baseKey + "bw_alloc:" + bwallocIdStr
	jsonBwInfo, err := rc.JSONGetEntry(keyName, ".")

	if err != nil {
		err = errors.New("bwInfo not found against the provided allocationId")
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusNotFound)
		return
	}

	var bwInfo BwInfo
	err = json.Unmarshal([]byte(jsonBwInfo), &bwInfo)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// adding/updating the same Bandwidth Resource in Buffer
	switch bwInfo.AllocationDirection {

	// downlink
	case "00":

		bufferInfo, valBuff, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return
		}
		var valFixedBuff uint64
		valFixedBuff, _ = strconv.ParseUint(bwInfo.FixedAllocation, 10, 64)
		bufferInfo["down"] = strconv.FormatUint(valBuff+valFixedBuff, 10)

		// updateBuffer function takes updated buffer Information and store it in redis
		// returns error if unable to store allocation buffer in redis
		err = updateBuffer(bufferInfo)
		if err != nil {
			log.Error(err.Error())
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return
		} else {
			log.Info("Allocation downlink buffer is updated")
		}

	// uplink
	case "01":

		bufferInfo, valBuff, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return
		}
		var valFixedBuff uint64
		valFixedBuff, _ = strconv.ParseUint(bwInfo.FixedAllocation, 10, 64)
		bufferInfo["up"] = strconv.FormatUint(valBuff+valFixedBuff, 10)

		// updateBuffer function takes updated buffer Information and store it in redis
		// returns error if unable to store allocation buffer in redis
		err = updateBuffer(bufferInfo)
		if err != nil {
			log.Error(err.Error())
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return
		} else {
			log.Info("Allocation uplink buffer is updated")
		}

	// symmetrical
	case "10":

		_, valBuffup, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return
		}

		bufferInfo, valBuffdown, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return
		}

		var valFixedBuff uint64
		valFixedBuff, _ = strconv.ParseUint(bwInfo.FixedAllocation, 10, 64)
		bufferInfo["down"] = strconv.FormatUint(valBuffdown+(valFixedBuff/2), 10)
		bufferInfo["up"] = strconv.FormatUint(valBuffup+(valFixedBuff/2), 10)

		// updateBuffer function takes updated buffer Information and store it in redis
		// returns error if unable to store allocation buffer in redis
		err = updateBuffer(bufferInfo)
		if err != nil {
			log.Error(err.Error())
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return
		} else {
			log.Info("Allocation buffer is updated")
		}
	}

	// Information of bandwidth allocation of specific allocationId is deleted from redis
	err = rc.JSONDelEntry(keyName, ".")
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusNotFound)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// bandwidthAllocationGet retrieves information about a specific bandwidthAllocation at /bw_allocations/{allocationId} endpoint
func bandwidthAllocationGet(w http.ResponseWriter, r *http.Request) {
	log.Info("bandwidthAllocationGet")

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	vars := mux.Vars(r)
	bwallocIdStr := vars["allocationId"]

	// Information of bandwidth allocation of specific allocationId is fetched
	keyName := baseKey + "bw_alloc:" + bwallocIdStr
	jsonBwInfo, err := rc.JSONGetEntry(keyName, ".")

	if err != nil {
		err = errors.New("bwInfo not found against the provided the allocationId")
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusNotFound)
		return
	}

	var bwInfo BwInfo
	err = json.Unmarshal([]byte(jsonBwInfo), &bwInfo)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
		return
	}

	jsonResponse := convertBandwidthInfoToJson(&bwInfo)

	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, jsonResponse)
}

// bandwidthAllocationListGet retrieves information about a list of bandwidthAllocation resources at /bw_allocations endpoint
func bandwidthAllocationListGet(w http.ResponseWriter, r *http.Request) {
	log.Info("bandwidthAllocationListGet")
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	u, _ := url.Parse(r.URL.String())
	q := u.Query()

	appInstanceId := q["app_instance_id"]
	appName := q["app_name"]
	sessionId := q["session_id"]

	validQueryParams := []string{"app_instance_id", "app_name", "session_id"}
	err := validateQueryParams(q, validQueryParams)
	if err != nil {
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
		return
	}

	bwInfoList := &BwAllocInfoResp{
		AppInstanceId: appInstanceId,
		AppName:       appName,
		SessionId:     sessionId,
		SessionList:   make([]BwInfo, 0),
	}

	// Make sure only 1 or none of the following are present: appInstanceId, appName, allocationId
	err = validateBwInfoQueryParams(appInstanceId, appName, sessionId)
	if err != nil {
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Get all Bandwidth Allocation Info from DB
	keyName := baseKey + "bw_alloc:*"
	err = rc.ForEachJSONEntry(keyName, populateBwInfo, bwInfoList)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Prepare & send response
	jsonResponse, err := json.Marshal(bwInfoList.SessionList)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(jsonResponse))
}

// bandwidthAllocationPatch modifies the information about a specific existing bandwidthAllocation
// by sending updates on the data structure at /bw_allocations/{allocationId} endpoint
func bandwidthAllocationPatch(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	log.Info("bandwidthAllocationPut")
	vars := mux.Vars(r)
	bwallocIdStr := vars["allocationId"]

	//Request body is of BwInfoDeltas Type
	var bwInfoDeltaInput BwInfoDeltas
	// Read JSON input stream provided in the Request, and stores it in the buffer of a Decoder object
	decoder := json.NewDecoder(r.Body)
	// Decode function return strings containing the text provided in the request body
	err := decoder.Decode(&bwInfoDeltaInput)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Information of bandwidth allocation of specific allocationId is fetched
	keyName := baseKey + "bw_alloc:" + bwallocIdStr
	jsonBwInfo, _ := rc.JSONGetEntry(keyName, ".")

	if jsonBwInfo == "" {
		log.Error("BW Allocation Info not found against the provided allocationId")
		errHandlerProblemDetails(w, "BW Allocation Info not found against the provided allocationId", http.StatusNotFound)
		return
	}

	// Bandwidth Information stored for specific allocation
	var bwInfoStored BwInfo
	err = json.Unmarshal([]byte(jsonBwInfo), &bwInfoStored)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// for copying response from request body of type BwInfoDeltas to BwInfo
	var newBwInfo BwInfo

	if bwInfoDeltaInput.AllocationId == "" {
		log.Error("Mandatory attribute allocationId is missing")
		errHandlerProblemDetails(w, "Mandatory attribute allocationId is missing", http.StatusBadRequest)
		return
	}

	if bwInfoDeltaInput.AppInsId == "" {
		log.Error("Mandatory attribute appInsId is missing")
		errHandlerProblemDetails(w, "Mandatory attribute appInsId is missing", http.StatusBadRequest)
		return
	}

	if bwInfoDeltaInput.RequestType == nil {
		log.Error("Mandatory attribute requestType is Missing")
		errHandlerProblemDetails(w, "Mandatory attribute requestType is Missing", http.StatusBadRequest)
		return
	}

	if (*bwInfoDeltaInput.RequestType != 0) && (*bwInfoDeltaInput.RequestType != 1) {
		log.Error("Invalid Mandatory attribute")
		errHandlerProblemDetails(w, "Invalid Mandatory attribute", http.StatusBadRequest)
		return
	}

	//Validating Mandatory attribute
	if bwInfoDeltaInput.AllocationId != "" {
		if bwInfoDeltaInput.AllocationId != bwallocIdStr {
			log.Error("AllocationId in endpoint and in body not matching")
			errHandlerProblemDetails(w, "AllocationId in endpoint and in body not matching", http.StatusBadRequest)
			return
		} else {
			// copying response from type BwInfoDeltas to BwInfo
			newBwInfo.AllocationId = bwInfoDeltaInput.AllocationId
		}
	}

	//Validating Mandatory attribute
	if bwInfoDeltaInput.AppInsId != "" {
		if bwInfoDeltaInput.AppInsId != bwInfoStored.AppInsId {
			// Get App instance
			appInfo, err := getAppInfo(bwInfoDeltaInput.AppInsId)
			if err != nil {
				errHandlerProblemDetails(w, err.Error(), http.StatusNotFound)
				return
			}

			// Validate App info
			code, problemDetails, err := validateAppInfo(appInfo)
			if err != nil {
				log.Error(err.Error())
				if problemDetails != "" {
					w.WriteHeader(code)
					fmt.Fprint(w, problemDetails)
				} else {
					errHandlerProblemDetails(w, err.Error(), code)
				}
				return
			}
			// copying response from type BwInfoDeltas to BwInfo
			newBwInfo.AppInsId = bwInfoDeltaInput.AppInsId
		} else if bwInfoDeltaInput.AppInsId == bwInfoStored.AppInsId {
			// copying response from type BwInfoDeltas to BwInfo
			newBwInfo.AppInsId = bwInfoDeltaInput.AppInsId
		}
	}

	// Patch method does not allow to change appName
	newBwInfo.AppName = bwInfoStored.AppName

	// validate the requested changes in Mandatory attribute requestType (session/application specific)
	if (*bwInfoDeltaInput.RequestType == 0) || (*bwInfoDeltaInput.RequestType == 1) {
		// copying data to response body of BwInfo type
		newBwInfo.RequestType = bwInfoDeltaInput.RequestType
		newSessionFilter := make([]BwInfoSessionFilter, 0)
		for _, filter := range bwInfoDeltaInput.SessionFilter {
			newSessionFilter = append(newSessionFilter, BwInfoSessionFilter(filter))
		}
		newBwInfo.SessionFilter = newSessionFilter

		if *bwInfoDeltaInput.RequestType == 1 {
			if len(bwInfoDeltaInput.SessionFilter) >= 0 {
				sessionSlice := make([]BwInfoSessionFilter, 0)
				for index, singleSessionFilter := range newBwInfo.SessionFilter {
					if index == 0 {
						sessionSlice = append(sessionSlice, singleSessionFilter)
					} else {
						sessionSlice, err = sessionContains(sessionSlice, singleSessionFilter)
						if err != nil {
							errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
							return
						}
					}
				}
			}
		}

		err = validateUpdateRequestType(&newBwInfo, &bwInfoStored, w)
		if err != nil {
			log.Error(err.Error())
			return
		}
	}

	seconds := time.Now().Unix()
	nanoseconds := time.Now().UnixNano()
	newBwInfo.TimeStamp = &BwInfoTimeStamp{
		NanoSeconds: int32(nanoseconds),
		Seconds:     int32(seconds),
	}

	if (bwInfoDeltaInput.AllocationDirection != "") || (bwInfoDeltaInput.FixedAllocation != "") {
		// validate and update the request changes in Allocation direction or Fixed Allocation (bps)
		if bwInfoDeltaInput.AllocationDirection != "" {
			if (bwInfoDeltaInput.AllocationDirection == "00") || (bwInfoDeltaInput.AllocationDirection == "01") || (bwInfoDeltaInput.AllocationDirection == "10") {
				newBwInfo.AllocationDirection = bwInfoDeltaInput.AllocationDirection
			}
		} else {
			newBwInfo.AllocationDirection = bwInfoStored.AllocationDirection
		}
		if bwInfoDeltaInput.FixedAllocation != "" {
			newBwInfo.FixedAllocation = bwInfoDeltaInput.FixedAllocation
		} else {
			newBwInfo.FixedAllocation = bwInfoStored.FixedAllocation
		}
		err = UpdateAllocationDirection(&newBwInfo, &bwInfoStored, w)
		if err != nil {
			return
		}
	}

	// setBwInfo function takes input of new BW allocation information and
	//	store it in json format in redis with new allocation ID.
	var jsonResponse []byte
	jsonResponse, err = setBwInfo(newBwInfo)
	if err != nil {
		log.Error("Unable to store new Allocation in redis")
		errHandlerProblemDetails(w, "Unable to store new Allocation in redis", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(jsonResponse))
	//w.WriteHeader(http.StatusOK)
}

// bandwidthAllocationPost creates a bandwidthAllocation resource at /bw_allocations endpoint
func bandwidthAllocationPost(w http.ResponseWriter, r *http.Request) {
	log.Info("bandwidthAllocationPOST")

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	//	Read request body
	var bwInfo BwInfo
	// Read JSON input stream provided in the Request, and stores it in the buffer of a Decoder object
	decoder := json.NewDecoder(r.Body)
	// Decode function return strings containing the text provided in the request body
	err := decoder.Decode(&bwInfo)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Validating mandatory parameters provided in the request body

	if bwInfo.AppInsId == "" {
		log.Error("Mandatory appInsId parameter should be present")
		errHandlerProblemDetails(w, "Mandatory attribute appInsId is missing in the request body.", http.StatusBadRequest)
		return
	}

	// verify RequestType attribute is either 1 or 0
	if bwInfo.RequestType != nil {
		if (*bwInfo.RequestType == 0) || (*bwInfo.RequestType == 1) {
			if (bwInfo.AllocationDirection == "00") || (bwInfo.AllocationDirection == "01") || (bwInfo.AllocationDirection == "10") {
				log.Debug("Valid Mandatory attribute allocationDirection")
			} else {
				log.Error("Valid allocationDirection value should be present")
				errHandlerProblemDetails(w, "Valid allocationDirection value should be present in the request body", http.StatusBadRequest)
				return
			}
		} else {
			log.Error("Valid requestType value should be present")
			errHandlerProblemDetails(w, "Valid requestType value should be present in the request body", http.StatusBadRequest)
			return
		}
	} else {
		log.Error("Mandatory attribute requestType should be present")
		errHandlerProblemDetails(w, "Mandatory attribute requestType is missing in the request body", http.StatusBadRequest)
		return
	}

	// Validate Mandatory attribute "FixedAllocation"
	if bwInfo.FixedAllocation != "" {
		valFixedBuff, err := strconv.ParseUint(bwInfo.FixedAllocation, 10, 64)
		if err == nil {
			_, valUpBuff, _ := getUplinkBuff()
			_, valDownBuff, _ := getDownlinkBuff()

			if valFixedBuff == 0 {
				log.Error("Value of FixedAllocation should be non-zero")
				errHandlerProblemDetails(w, "Value of FixedAllocation should be non-zero", http.StatusBadRequest)
				return
			}

			// validate if the requested Allocation is within range of Available Bandwidth Resorce
			if (bwInfo.AllocationDirection == "01" && valFixedBuff > valUpBuff) || (bwInfo.AllocationDirection == "00" && valFixedBuff > valDownBuff) {
				log.Error("Requested Allocation exceeds Resource buffer")
				errHandlerProblemDetails(w, "Requested Allocation exceeds Resource buffer.", http.StatusBadRequest)
				return
			} else {
				log.Info("Fixed Allocation is in Allocation Buffer range")
			}
		} else {
			log.Error("Failed to convert value of Fixed allocation: ", err.Error())
			errHandlerProblemDetails(w, "Failed to convert value of Fixed allocation", http.StatusInternalServerError)
			return
		}
	} else {
		log.Error("Mandatory fixedAllocation parameter should be present")
		errHandlerProblemDetails(w, "Mandatory attribute fixedAllocation is missing in the request body", http.StatusBadRequest)
		return
	}

	// verify if the RequestType attribute is 1 for session specific allocation
	// then SessionFilter should also provided in requested body
	if *bwInfo.RequestType == 1 && len(bwInfo.SessionFilter) != 0 {
		for _, flowFilterVal := range bwInfo.SessionFilter {
			if flowFilterVal.SourceIp == "" && flowFilterVal.SourcePort == "" && flowFilterVal.DstAddress == "" && flowFilterVal.DstPort == "" && flowFilterVal.Protocol == "" {
				log.Error("At least one of sessionFilter subfields shall be included")
				errHandlerProblemDetails(w, "At least one of sessionFilter subfields shall be included in the request body.", http.StatusBadRequest)
				return
			}
		}
	} else if *bwInfo.RequestType == 1 && len(bwInfo.SessionFilter) == 0 {
		log.Error("sessionFilter attribute is Empty.")
		errHandlerProblemDetails(w, "sessionFilter attribute is Empty..", http.StatusBadRequest)
		return
	}

	if *bwInfo.RequestType == 0 {
		bwInfo.SessionFilter = nil
	}

	if *bwInfo.RequestType == 1 && bwInfo.SessionFilter != nil {
		sessionSlice := make([]BwInfoSessionFilter, 0)
		for index, singleSessionFilter := range bwInfo.SessionFilter {
			if index == 0 {
				sessionSlice = append(sessionSlice, singleSessionFilter)
			} else {
				sessionSlice, err = sessionContains(sessionSlice, singleSessionFilter)
				if err != nil {
					errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
					return
				}
			}
		}
	}

	// Get App instance
	appInfo, err := getAppInfo(bwInfo.AppInsId)
	if err != nil {
		errHandlerProblemDetails(w, err.Error(), http.StatusNotFound)
		return
	}

	// Validate App info
	code, problemDetails, err := validateAppInfo(appInfo)
	if err != nil {
		log.Error(err.Error())
		if problemDetails != "" {
			w.WriteHeader(code)
			fmt.Fprint(w, problemDetails)
		} else {
			errHandlerProblemDetails(w, err.Error(), code)
		}
		return
	}

	//var newBwInfo BwInfo
	var jsonResponse []byte

	sessionFilterList := &sessionFilterListCheck{
		sessionBool: false,
		SessionList: bwInfo.SessionFilter,
	}

	keyName := baseKey + "bw_alloc:*"

	if *bwInfo.RequestType == 1 {
		// Retrieve bwInfo sessions from redis DB one by one and store in the sessionFilterList
		// to check if a bwInfo already exists in DB with same sessionFilter
		err = rc.ForEachJSONEntry(keyName, compareSessionFilters, sessionFilterList)
		if err != nil {
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return
		}
		//check bool here
		if sessionFilterList.sessionBool {
			errHandlerProblemDetails(w, "Provide sessionFilter matches an already existing session", http.StatusBadRequest)
			return
		}
	}

	// Validate IP Address of UE (dstAddress in downlink, sourceIp in uplink and both is symmetrical
	// options) with existing UE IP(s), if the request type is session specific

	if *bwInfo.RequestType == 1 {
		switch bwInfo.AllocationDirection {
		case "00":
			// if the provided destination IP range matches with the existing UE IP(s)
			err = checkDstIP(&bwInfo)
			if err != nil {
				errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
				return
			}

		case "01":
			// if the provided source IP range matches with the existing UE IP(s)
			err = checkSrcIP(&bwInfo)
			if err != nil {
				errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
				return
			}

		case "10":
			// if the provided source IP range matches with the existing UE IP(s)
			err = checkSrcIP(&bwInfo)
			if err != nil {
				errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
				return
			}
			// if the provided destination IP range matches with the existing UE IP(s)
			err = checkDstIP(&bwInfo)
			if err != nil {
				errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
				return
			}
		}

	}

	seconds := time.Now().Unix()
	nanoseconds := time.Now().UnixNano()
	bwInfo.TimeStamp = &BwInfoTimeStamp{
		NanoSeconds: int32(nanoseconds),
		Seconds:     int32(seconds),
	}

	// In APPLICATION_SPECIFIC_BW_ALLOCATION OR SESSION_SPECIFIC_BW_ALLOCATION
	// Uplink, Downlink and Symmetrical bandwidth allocation is performed
	if bwInfo.AllocationDirection != "" {
		err = bandwidthResourceAllocation(&bwInfo, w)
		if err != nil {
			log.Error(err.Error())
			return
		}
	}
	// setBwInfo function takes input of new BW allocation information and
	// store it in json format in redis with new allocation ID.
	jsonResponse, err = setBwInfo(bwInfo)
	if err != nil {
		log.Error("Unable to store new Allocation in redis")
		errHandlerProblemDetails(w, "Unable to store new Allocation in redis", http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusCreated)
	fmt.Fprint(w, string(jsonResponse))
}

// bandwidthAllocationPut updates the information about a specific bandwidthAllocation at /bw_allocations/{allocationId} endpoint
func bandwidthAllocationPut(w http.ResponseWriter, r *http.Request) {

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	log.Info("bandwidthAllocationPut")
	vars := mux.Vars(r)
	bwallocIdStr := vars["allocationId"]

	var bwInfoInput BwInfo

	// Read JSON input stream provided in the Request, and stores it in the buffer of a Decoder object
	decoder := json.NewDecoder(r.Body)
	// Decode function return strings containing the text provided in the request body
	err := decoder.Decode(&bwInfoInput)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
		return
	}

	//Body content not matching parameters
	if bwInfoInput.AllocationId != "" {
		if bwInfoInput.AllocationId != bwallocIdStr {
			log.Error("AllocationId in the endpoint and Request body does not matched")
			errHandlerProblemDetails(w, "AllocationId in the endpoint and Request body does not matched", http.StatusBadRequest)
			return
		}
	}

	// Information of bandwidth allocation of specific allocationId is fetched
	keyName := baseKey + "bw_alloc:" + bwallocIdStr
	jsonBwInfo, _ := rc.JSONGetEntry(keyName, ".")

	if jsonBwInfo == "" {
		log.Error("BW Allocation Information not found against the provided allocationId")
		errHandlerProblemDetails(w, "BW Allocation Information not found against the provided allocationId", http.StatusNotFound)
		return
	}

	if bwInfoInput.RequestType == nil {
		log.Error("Mandatory attribute requestType is Missing")
		errHandlerProblemDetails(w, "Mandatory attribute requestType is Missing", http.StatusBadRequest)
		return
	}

	if bwInfoInput.AppInsId == "" {
		log.Error("Mandatory attribute appInsId is Missing")
		errHandlerProblemDetails(w, "Mandatory attribute appInsId is Missing", http.StatusBadRequest)
		return
	}

	if bwInfoInput.RequestType != nil {
		if (*bwInfoInput.RequestType != 0) && (*bwInfoInput.RequestType != 1) {
			log.Error("Invalid Mandatory attribute requestType")
			errHandlerProblemDetails(w, "Invalid Mandatory attribute requestType", http.StatusBadRequest)
			return
		}
	}

	if (bwInfoInput.AllocationDirection == "") || (bwInfoInput.FixedAllocation == "") {
		log.Error("Mandatory attribute allocationDirection or fixedAllocation is Missing")
		errHandlerProblemDetails(w, "Mandatory attribute allocationDirection or fixedAllocation is Missing", http.StatusBadRequest)
		return
	}

	var bwInfoStored BwInfo
	err = json.Unmarshal([]byte(jsonBwInfo), &bwInfoStored)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// validate the requested changes in application Instance Id
	if bwInfoInput.AppInsId != "" {
		if bwInfoInput.AppInsId != bwInfoStored.AppInsId {
			// Get App instance
			appInfo, err := getAppInfo(bwInfoInput.AppInsId)
			if err != nil {
				errHandlerProblemDetails(w, err.Error(), http.StatusNotFound)
				return
			}

			// Validate App info
			code, problemDetails, err := validateAppInfo(appInfo)
			if err != nil {
				log.Error(err.Error())
				if problemDetails != "" {
					w.WriteHeader(code)
					fmt.Fprint(w, problemDetails)
				} else {
					errHandlerProblemDetails(w, err.Error(), code)
				}
				return
			}
		}
	}

	// validate the requested changes in Request Type (session/application specific)
	if bwInfoInput.RequestType != nil {
		if (*bwInfoInput.RequestType == 0) || (*bwInfoInput.RequestType == 1) {

			if *bwInfoInput.RequestType == 1 {
				if len(bwInfoInput.SessionFilter) > 0 {
					sessionSlice := make([]BwInfoSessionFilter, 0)
					for index, singleSessionFilter := range bwInfoInput.SessionFilter {
						if index == 0 {
							sessionSlice = append(sessionSlice, singleSessionFilter)
						} else {
							sessionSlice, err = sessionContains(sessionSlice, singleSessionFilter)
							if err != nil {
								errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
								return
							}
						}
					}
				}
			}

			err = validateUpdateRequestType(&bwInfoInput, &bwInfoStored, w)
			if err != nil {
				log.Error(err.Error())
				//errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
	}

	seconds := time.Now().Unix()
	nanoseconds := time.Now().UnixNano()
	bwInfoInput.TimeStamp = &BwInfoTimeStamp{
		NanoSeconds: int32(nanoseconds),
		Seconds:     int32(seconds),
	}
	bwInfoInput.AllocationId = bwInfoStored.AllocationId

	// validate and update the request changes in Allocation direction or Fixed Allocation (bps)
	if (bwInfoInput.AllocationDirection != "") && (bwInfoInput.FixedAllocation != "") {
		if (bwInfoInput.AllocationDirection == "00") || (bwInfoInput.AllocationDirection == "01") || (bwInfoInput.AllocationDirection == "10") {
			err = UpdateAllocationDirection(&bwInfoInput, &bwInfoStored, w)
			if err != nil {
				return
			}
		} else {
			log.Error("Invalid Mandatory attribute allocationDirection")
			errHandlerProblemDetails(w, "Invalid Mandatory attribute allocationDirection", http.StatusBadRequest)
			return
		}
	}

	// setBwInfo function takes input of new BW allocation information and
	//	store it in json format in redis with new allocation ID.
	var jsonResponse []byte
	jsonResponse, err = setBwInfo(bwInfoInput)
	if err != nil {
		log.Error("Unable to store new Allocation in redis")
		errHandlerProblemDetails(w, "Unable to store new Allocation in redis", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(jsonResponse))
}

/*
* populateSessionFilter compares the requested session filters  against session filters of the allocated resource allocations stored in redis
* @param {string} key	Bandwidth allocation stored with this key
* @param {string} jsonInfo Bandwidth allocation information
* @param {interface{}} response Bandwidth allocation information
* @return {String} error error message
 */
func populateSessionFilter(key string, jsonInfo string, response interface{}) error {
	resp := response.(*SessionFilterResp)
	if resp == nil {
		return errors.New("Response not defined")
	}
	var bwInfo BwInfo
	err := json.Unmarshal([]byte(jsonInfo), &bwInfo)
	if err != nil {
		return err
	}

	if len(bwInfo.SessionFilter) > 0 {
		for _, singleSessionInput := range resp.SessionFilterList {
			for _, singleSessionStored := range bwInfo.SessionFilter {
				if reflect.DeepEqual(singleSessionInput, singleSessionStored) {
					resp.SessionMatch = true
					if resp.AllocationId == bwInfo.AllocationId {
						resp.AllocationIdMatch = true
					}
				}
			}
		}
	}
	return nil
}

/*
* Function that list down all the bandwidth allocations based on query parameters
* @param {string} key	Bandwidth allocation stored with this key
* @param {string} jsonInfo Bandwidth allocation information
* @param {*BwAllocInfoResp} response Bandwidth allocation information
* @return {String} error error message
 */
func populateBwInfo(key string, jsonInfo string, bwInfoList interface{}) error {
	resp := bwInfoList.(*BwAllocInfoResp)
	if resp == nil {
		return errors.New("Response not defined")
	}
	var bwInfo BwInfo
	err := json.Unmarshal([]byte(jsonInfo), &bwInfo)
	if err != nil {
		return err
	}

	paramFound := false

	if len(resp.AppInstanceId) > 0 {
		paramFound = false
		for _, QueryAppInstanceId := range resp.AppInstanceId {
			if bwInfo.AppInsId == QueryAppInstanceId {
				paramFound = true
			}
		}
		if !paramFound {
			return nil
		}
	}

	if len(resp.AppName) > 0 {
		paramFound = false
		for _, QueryAppName := range resp.AppName {
			if bwInfo.AppName == QueryAppName {
				paramFound = true
			}
		}
		if !paramFound {
			return nil
		}
	}

	if len(resp.SessionId) > 0 {
		paramFound = false
		for _, QueryAllocationId := range resp.SessionId {
			if bwInfo.AllocationId == QueryAllocationId {
				paramFound = true
			}
		}
		if !paramFound {
			return nil
		}
	}

	resp.SessionList = append(resp.SessionList, bwInfo)
	return nil
}

func errHandlerProblemDetails(w http.ResponseWriter, error string, code int) {
	var pd ProblemDetails
	pd.Detail = error
	pd.Status = int32(code)
	jsonResponse := convertProblemDetailsToJson(&pd)
	w.WriteHeader(code)
	fmt.Fprint(w, jsonResponse)
}

/*
* setBwInfo sets/updates updated bandwidth allocation information with allocationId as a key to stored in the redis
* @param {BwInfo} bwInfo updated bandwidth allocation resource information
*	@return {uint8} []byte encoded bandwidth allocation resource information in json string
* @return {error} error It returns error while encoding to json string throughs error
 */
func setBwInfo(bwInfo BwInfo) ([]byte, error) {
	var jsonResponse []byte
	bwallocIdStr := bwInfo.AllocationId
	keyName := baseKey + "bw_alloc:" + bwallocIdStr
	_ = rc.JSONSetEntry(keyName, ".", convertBandwidthInfoToJson(&bwInfo))
	jsonResponse, err := json.Marshal(bwInfo)

	return jsonResponse, err
}

/*
* getAppInfo gets application information using its application Instance Id stored in redis
* @param {string} appId application Instance Id used to retrive its information from redis
*	@return {map[string]string} appInfo application information
* @return {error} err It returns error if there is no information associated with this appId
 */
func getAppInfo(appId string) (map[string]string, error) {

	var appInfo map[string]string
	// Get app instance from local MEP only
	baseKeyAppEn := dkm.GetKeyRoot(sandboxName) + appEnablementKey + ":mep:" + mepName + ":"
	key := baseKeyAppEn + "app:" + appId + ":info"
	appInfo, err := rc.GetEntry(key)
	if err != nil || len(appInfo) == 0 {
		return nil, errors.New("appInsId not found")
	}
	return appInfo, nil
}

/*
* validateAppInfo validates the status information of application to be in ready state
* @param {map[string]string} appInfo Information associated with application
* @return {int} int It returns the http status for validation
* @return {error} err It returns the details of the problem if any
* @return {error} err It returns the error message for error if any otherwise nil
 */
func validateAppInfo(appInfo map[string]string) (int, string, error) {
	// Make sure App is in ready state
	if appInfo[fieldState] != APP_STATE_READY {
		var problemDetails ProblemDetails
		problemDetails.Status = http.StatusForbidden
		problemDetails.Detail = "App Instance not ready. Waiting for AppReadyConfirmation."
		return http.StatusForbidden, convertProblemDetailsToJson(&problemDetails), errors.New("App Instance not ready")
	}
	return http.StatusOK, "", nil
}

/*
* checkSrcIP validate source ip range passed in CIDR notation with existing UE ip
* @param {*BwInfo} bw request body of POST method
* @return {error} err It returns the status of UE present in the active model zone
 */
func checkSrcIP(bw *BwInfo) (err error) {
	ueNameList := activeModel.GetNodeNames("UE")
	// if the provided source IP range matches with the existing UE IP(s)
	if len(ueNameList) > 0 {
		for _, ip := range bw.SessionFilter {
			ipFound := true
			if ip.SourceIp != "" {
				ipName := net.ParseIP(ip.SourceIp)
				if ipName != nil {
					for _, name := range ueNameList {
						// Ignore disconnected UEs
						if !isUeConnected(name) || !isInLocality(name) {
							continue
						}
						if ipName.String() == name {
							log.Info("Provided source IP range is valid against the existing UE IPs")
							ipFound = true
							break
						} else {
							ipFound = false
						}
					}
				}
				// Return error if IP not in ueNameList
				if !ipFound {
					log.Error("Provided source IP range is not valid against the existing UE IPs")
					err = errors.New("Provided source IP range is not valid against the existing UE IPs in the request body")
					return err
				}
			}
		}
	}
	return nil
}

/*
* checkDstIP validate destination ip range passed in CIDR notation with existing UE ip
* @param {*BwInfo} bw request body of POST method
* @return {error} err It returns the status of UE present in the active model zone
 */
func checkDstIP(bw *BwInfo) (err error) {
	ueNameList := activeModel.GetNodeNames("UE")
	// if the provided destination IP range matches with the existing UE IP(s)
	if len(ueNameList) > 0 {
		for _, ip := range bw.SessionFilter {
			ipFound := true
			if ip.DstAddress != "" {
				ipName := net.ParseIP(ip.DstAddress)
				if ipName != nil {
					for _, name := range ueNameList {
						// Ignore disconnected UEs
						if !isUeConnected(name) || !isInLocality(name) {
							continue
						}
						if ipName.String() == name {
							log.Info("Provided destination IP range is valid against the existing UE IPs")
							ipFound = true
							break
						} else {
							ipFound = false
						}
					}
				}
				// Return error if IP not in ueNameList
				if !ipFound {
					log.Error("Provided destination IP range is not valid against the existing UE IPs")
					err = errors.New("Provided destination IP range is not valid against the existing UE IPs in the request body")
					return err
				}
			}
		}
	}
	return nil
}

/*
* isInLocality validate UE ip only when they are in locality of current active model
* @param {string} name	name of a UE
* @return {bool} bool It returns the status of UE present in the active model zone
 */
func isInLocality(name string) bool {
	if localityEnabled {
		ctx := activeModel.GetNodeContext(name)
		if ctx == nil {
			log.Error("Error getting context for: " + name)
			return false
		}
		if _, found := mapLocality[ctx.Parents[mod.Zone]]; !found {
			return false
		}
	}
	return true
}

/*
* isUeConnected validate UE ip with connected UE(s) in the current active model
* @param {string} name	name of a UE
* @return {bool} bool It returns the status of network connectivity of UE
 */
func isUeConnected(name string) bool {
	node := activeModel.GetNode(name)
	if node != nil {
		pl := node.(*dataModel.PhysicalLocation)
		return pl.Connected
	}
	return false
}

/*
* getUplinkBuff fetches uplink buffer information stored in redis
* @return {map[string]string} bufferInfo	Bandwidth buffer information (uplink/downlink)
* @return {uint64} valBuff Value of uplink buffer
* @return {String} error error message
 */
func getUplinkBuff() (bufferInfo map[string]string, valBuff uint64, err error) {
	// fetch bandwidth buffer information from redis
	key := baseKey + "linkbuf:"
	bufferInfo, err = rc.GetEntry(key)
	if err != nil {
		log.Error("Unable to fetch allocation buffer from redis")
		return nil, 0, err
	} else {
		log.Info("Allocation buffer is successfully fetched from redis")
		var valBuff uint64
		// conversion of downlink buffer value from string to uint64
		valBuff, err = strconv.ParseUint(bufferInfo["up"], 10, 64)
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			return nil, 0, err
		} else {
			return bufferInfo, valBuff, nil
		}
	}
}

/*
* getDownlinkBuff fetches downlink buffer information stored in redis
* @return {map[string]string} bufferInfo	Bandwidth buffer information (uplink/downlink)
* @return {uint64} valBuff Value of downlink buffer
* @return {String} error error message
 */
func getDownlinkBuff() (bufferInfo map[string]string, valBuff uint64, err error) {
	// fetch bandwidth buffer information from redis
	key := baseKey + "linkbuf:"
	bufferInfo, err = rc.GetEntry(key)
	if err != nil {
		log.Error("Unable to fetch allocation buffer from redis")
		return nil, 0, err
	} else {
		log.Info("Allocation buffer is successfully fetched from redis")
		var valBuff uint64
		// conversion of downlink buffer value from string to uint64
		valBuff, err = strconv.ParseUint(bufferInfo["down"], 10, 64)
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			return nil, 0, err
		} else {
			return bufferInfo, valBuff, nil
		}
	}
}

/*
* validateUpdateRequestType validates the type (session/application specific) for allocation and in case of session specific makes check on UE range using sessionFilter array
* @param {*BwInfo} bwInfoInput	bandwidth Allocation information to be updated
* @param{*BwInfo} bwInfoStored bandwidth Allocation information to be replaced
* @param {http.ResponseWriter} w writes HTTP reply
 */
func validateUpdateRequestType(bwInfoInput *BwInfo, bwInfoStored *BwInfo, w http.ResponseWriter) error {

	// if the RequestType attribute is the same for session specific allocation or changed to session specific allocation
	// then SessionFilter should also provided in requested body and verified
	if (*bwInfoInput.RequestType == 1 && bwInfoInput.RequestType == bwInfoStored.RequestType) || (*bwInfoInput.RequestType == 1 && bwInfoInput.RequestType != bwInfoStored.RequestType) {
		if len(bwInfoInput.SessionFilter) <= 0 {
			err := errors.New("sessions are not provided for filtering criteria")
			errHandlerProblemDetails(w, "sessions are not provided for filtering criteria.", http.StatusBadRequest)
			return err
			// validate the requested changes within session specific allocation
		} else {
			log.Info("Request type is now change to session specific allocation")

			response := &SessionFilterResp{
				SessionMatch:      false,
				SessionFilterList: make([]BwInfoSessionFilter, len(bwInfoInput.SessionFilter)),
				AllocationId:      "",
				AllocationIdMatch: false,
			}

			response.SessionFilterList = bwInfoInput.SessionFilter
			response.AllocationId = bwInfoInput.AllocationId

			// Get all Bandwidth Allocation Info from DB
			keyMatchStr := baseKey + "bw_alloc:*"
			err := rc.ForEachJSONEntry(keyMatchStr, populateSessionFilter, response)
			if err != nil {
				log.Error("Unable to fetch allocation resource from redis")
				errHandlerProblemDetails(w, "Unable to fetch allocation resource from redis", http.StatusInternalServerError)
				return err
			}

			if response.SessionMatch {
				if response.AllocationIdMatch {
					log.Info("Same sessionFilter requested to update")
					return nil
				} else {
					err = errors.New("multiple sessions match sessionFilter")
					//log.Error("multiple sessions match sessionFilter")
					errHandlerProblemDetails(w, "multiple sessions match sessionFilter", http.StatusBadRequest)
					return err
				}
			}

			switch bwInfoInput.AllocationDirection {

			//downlink
			case "00":

				// if the provided destination IP range matches with the existing UE IP(s)
				err := checkDstIP(bwInfoInput)
				if err != nil {
					errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
					return err
				}

			//uplink
			case "01":

				// if the provided source IP range matches with the existing UE IP(s)
				err := checkSrcIP(bwInfoInput)
				if err != nil {
					errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
					return err
				}

			//symmetrical
			case "10":
				// if the provided source IP range matches with the existing UE IP(s)
				err := checkSrcIP(bwInfoInput)
				if err != nil {
					errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
					return err
				}
				// if the provided destination IP range matches with the existing UE IP(s)
				err = checkDstIP(bwInfoInput)
				if err != nil {
					errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
					return err
				}
			}
			return nil
		}
	} else if *bwInfoInput.RequestType == 0 {
		// The attribute for session filter should be removed if requested is application specific allocation
		if *bwInfoInput.RequestType == 0 {
			log.Info("Request Types is SessionFilter")
			bwInfoInput.SessionFilter = nil
		}
		log.Info("Request type is now change to application specific allocation")
		return nil
	}
	return nil
}

/*
* UpdateAllocationDirection validates and update the resource allocation according to the direction of allocation (downlink, uplink and symmetrical) and fixedALloaction value
* @param {*BwInfo} bwInfoInput	bandwidth Allocation information to be updated
* @param{*BwInfo} bwInfoStored bandwidth Allocation information to be replaced
* @param {http.ResponseWriter} w writes HTTP reply
 */
func UpdateAllocationDirection(bwInfoInput *BwInfo, bwInfoStored *BwInfo, w http.ResponseWriter) error {
	if (bwInfoInput.AllocationDirection != bwInfoStored.AllocationDirection) || (bwInfoInput.FixedAllocation != bwInfoStored.FixedAllocation) {

		// Before updating resource allocation validate that value of FixedAllocation should be non-zero
		var valFixedBuff uint64
		valFixedBuff, err := strconv.ParseUint(bwInfoInput.FixedAllocation, 10, 64)
		if err != nil {
			log.Error(err.Error())
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return err
		} else if valFixedBuff == 0 {
			err = errors.New("Value of FixedAllocation should be non-zero")
			log.Error(err.Error())
			errHandlerProblemDetails(w, "Value of FixedAllocation should be non-zero", http.StatusBadRequest)
			return err
		}

		// Release bandwidth resources in bandwidth buffer for previous allocation based on fixed allocation and allocation direction
		err = freeBwBufferResource(bwInfoStored, w)
		if err != nil {
			return err
		}
		// update the newly requested bandwidth resources in bandwidth buffer based on fixed allocation and allocation direction
		err = addBwBufferResource(bwInfoInput, w)
		if err != nil {
			return err
		}
	}
	return nil
}

/*
* freeBwBufferResource releases bandwidth buffer resources uplink/downlink based on Allocation direction (downlink, uplink and symmetrical) and fixedALloaction value
* @param{*BwInfo} bwInfoStored bandwidth Allocation information to be replaced
* @param {http.ResponseWriter} w writes HTTP reply
 */
func freeBwBufferResource(bwInfoStored *BwInfo, w http.ResponseWriter) error {
	// adding/updating the Bandwidth Resource in Buffer
	switch bwInfoStored.AllocationDirection {

	// downlink
	case "00":

		bufferInfo, valBuff, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}
		var valFixedBuff uint64
		valFixedBuff, _ = strconv.ParseUint(bwInfoStored.FixedAllocation, 10, 64)
		bufferInfo["down"] = strconv.FormatUint(valBuff+valFixedBuff, 10)

		// updateBuffer function takes updated buffer Information and store it in redis
		// returns error if unable to store allocation buffer in redis
		err = updateBuffer(bufferInfo)
		if err != nil {
			log.Error(err.Error())
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return err
		} else {
			log.Info("Allocation downlink buffer is updated")
			return nil
		}

	// uplink
	case "01":

		bufferInfo, valBuff, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}
		var valFixedBuff uint64
		valFixedBuff, _ = strconv.ParseUint(bwInfoStored.FixedAllocation, 10, 64)
		bufferInfo["up"] = strconv.FormatUint(valBuff+valFixedBuff, 10)

		// updateBuffer function takes updated buffer Information and store it in redis
		// returns error if unable to store allocation buffer in redis
		err = updateBuffer(bufferInfo)
		if err != nil {
			log.Error(err.Error())
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return err
		} else {
			log.Info("Allocation uplink buffer is updated")
			return nil
		}

	// symmetrical
	case "10":

		_, valBuffup, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		bufferInfo, valBuffdown, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		var valFixedBuff uint64
		valFixedBuff, _ = strconv.ParseUint(bwInfoStored.FixedAllocation, 10, 64)
		bufferInfo["down"] = strconv.FormatUint(valBuffdown+(valFixedBuff/2), 10)
		bufferInfo["up"] = strconv.FormatUint(valBuffup+(valFixedBuff/2), 10)

		// updateBuffer function takes updated buffer Information and store it in redis
		// returns error if unable to store allocation buffer in redis
		err = updateBuffer(bufferInfo)
		if err != nil {
			log.Error(err.Error())
			errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
			return err
		} else {
			log.Info("Allocation buffer is updated")
			return nil
		}
	}
	return nil
}

/*
* addBwBufferResource allocate bandwidth buffer resources uplink/downlink based on new requested Allocation direction (downlink, uplink and symmetrical) and fixedALloaction value
* @param {*BwInfo} bwInfoInput	bandwidth Allocation information to be updated
* @param {http.ResponseWriter} w writes HTTP reply
 */
func addBwBufferResource(bwInfoInput *BwInfo, w http.ResponseWriter) error {
	switch bwInfoInput.AllocationDirection {

	// downlink case
	case "00":

		// getting downlink buffer value from redis to update
		bufferInfo, valBuff, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		// To start allocating resource, buffer value should be non-zero
		if valBuff > 0 {
			var valFixedBuff uint64
			valFixedBuff, err = strconv.ParseUint(bwInfoInput.FixedAllocation, 10, 64)
			// The value of resource needed to be allocate should also be no-zero
			if (valFixedBuff > 0) && (err == nil) {
				bufferInfo["down"] = strconv.FormatUint(valBuff-valFixedBuff, 10)
				// updateBuffer function takes updated buffer Information and store it in redis
				// returns error if unable to store allocation buffer in redis
				err = updateBuffer(bufferInfo)
				if err != nil {
					log.Error(err.Error())
					errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
					return err
				} else {
					log.Info("Allocation downlink buffer is updated")
					return nil
				}
			}
		} else {
			log.Debug("Resources are insufficient for this allocation")
			return nil
		}

	// uplink case
	case "01":

		// getting uplink buffer value from redis to update
		bufferInfo, valBuff, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		// To start allocating resource, buffer value should be non-zero
		if valBuff > 0 {
			var valFixedBuff uint64
			valFixedBuff, err = strconv.ParseUint(bwInfoInput.FixedAllocation, 10, 64)
			// The value of resource needed to be allocate should also be no-zero
			if (valFixedBuff > 0) && (err == nil) {
				bufferInfo["up"] = strconv.FormatUint(valBuff-valFixedBuff, 10)
				// updateBuffer function takes updated buffer Information and store it in redis
				// returns error if unable to store allocation buffer in redis
				err = updateBuffer(bufferInfo)
				if err != nil {
					log.Error(err.Error())
					errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
					return err
				} else {
					log.Info("Allocation uplink buffer is updated")
					return nil
				}
			}
		} else {

			log.Debug("Resources are insufficient for this allocation")
			return nil
		}
	// symmetrical case
	case "10":

		// getting downlink/uplink buffer value from redis to update
		_, valBuffup, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}
		bufferInfo, valBuffdown, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		// To start allocating resource, buffer values should be non-zero
		if (valBuffup > 0) && (valBuffdown > 0) {
			var valFixedBuff uint64
			valFixedBuff, err = strconv.ParseUint(bwInfoInput.FixedAllocation, 10, 64)
			// The value of resource needed to be allocate should also be no-zero
			if (valFixedBuff > 0) && (err == nil) {
				bufferInfo["up"] = strconv.FormatUint(valBuffup-(valFixedBuff/2), 10)
				bufferInfo["down"] = strconv.FormatUint(valBuffdown-(valFixedBuff/2), 10)
				// updateBuffer function takes updated buffer Information and store it in redis
				// returns error if unable to store allocation buffer in redis
				err = updateBuffer(bufferInfo)
				if err != nil {
					log.Error(err.Error())
					errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
					return err
				} else {
					log.Info("Allocation downlink buffer is updated")
					return nil
				}
			}
		} else {

			log.Debug("Resources are insufficient for this allocation")
			return nil
		}

	}
	return nil
}

/*
* sessionContains compares the slice of unique sessions with the passed single session of the requested allocation
* @param {[]BwInfoSessionFilter} sessionSlice	slice contains the unique sessions
* @param {BwInfoSessionFilter} singleSessionFilter	single session to be compared
* @return {[]BwInfoSessionFilter} returns the updated slice of sessionFilters
* @return {error} An error will be return if occurs
 */
func sessionContains(sessionSlice []BwInfoSessionFilter, singleSessionFilter BwInfoSessionFilter) ([]BwInfoSessionFilter, error) {
	for _, sessionSlice1 := range sessionSlice {
		if reflect.DeepEqual(singleSessionFilter, sessionSlice1) {
			err := errors.New("SessionFilter contains same sessions")
			return nil, err
		} else {
			sessionSlice = append(sessionSlice, singleSessionFilter)
		}
	}
	return sessionSlice, nil
}

/*
	* validateQueryParams ensures that valid query parameters should be used to retrieve one of the
		app_instance_id or app_name or allocation_id attributes from the user
	* @return {error} error An error will be return if occurs
*/
func validateQueryParams(params url.Values, validParams []string) error {
	for param := range params {
		found := false
		for _, validParam := range validParams {
			if param == validParam {
				found = true
				break
			}
		}
		if !found {
			err := errors.New("Invalid query param: " + param)
			log.Error(err.Error())
			return err
		}
	}
	return nil
}

/*
	* validateBwInfoQueryParams check that either app_instance_id or app_name or allocation_id or
		none should be provided in the request
	* @return {error} error An error will be return if occurs
*/
func validateBwInfoQueryParams(appInstanceId []string, appName []string, sessionId []string) error {
	count := 0
	if len(appInstanceId) != 0 {
		count++
	}
	if len(appName) != 0 {
		count++
	}
	if len(sessionId) != 0 {
		count++
	}
	if count > 1 {
		err := errors.New("Either app_instance_id or app_name or allocation_id or none of them shall be present")
		log.Error(err.Error())
		return err
	}
	return nil
}

/*
* compareSessionFilters compares the requested allocation sessions with the existing unique sessions stored in redis of allocated resources
* @param {string} key	Bandwidth allocation stored with this key
* @param {string} jsonInfo Bandwidth allocation information
* @param {*BwAllocInfoResp} sessionFilterList Bandwidth allocation information
* @return {String} error error message
 */
func compareSessionFilters(key string, jsonInfo string, sessionFilterList interface{}) error {

	// Get query params & bwInfo
	data := sessionFilterList.(*sessionFilterListCheck)
	if data == nil {
		return errors.New("bwInfo list not found")
	}

	// Retrieve bwInfo from DB
	var sessionFilterInfo BwInfo
	err := json.Unmarshal([]byte(jsonInfo), &sessionFilterInfo)
	if err != nil {
		return err
	}

	for _, sessionFilterData := range data.SessionList {
		for _, redisSessionFilterData := range sessionFilterInfo.SessionFilter {
			if reflect.DeepEqual(sessionFilterData, redisSessionFilterData) {
				data.sessionBool = true
				return nil
			}
		}
	}
	return nil
}

/*
* bandwidthResourceAllocation allocate bandwidth buffer resources uplink/downlink based on new requested Allocation direction (downlink, uplink and
	symmetrical) and fixedALloaction value and also assigns a unique allocation id. This function is only for bandwidthAllocationPost method.
* @param {*BwInfo} bwInfoInput	bandwidth Allocation information to be updated
* @param {http.ResponseWriter} w writes HTTP reply
* @return {error} error An error will be return if occurs
*/
func bandwidthResourceAllocation(bwInfo *BwInfo, w http.ResponseWriter) error {
	switch bwInfo.AllocationDirection {
	case "00":
		// getting downlink buffer value from redis to update
		bufferInfo, valBuff, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		// To start allocating resource, buffer value should be non-zero
		if valBuff > 0 {
			var valFixedBuff uint64
			valFixedBuff, err = strconv.ParseUint(bwInfo.FixedAllocation, 10, 64)
			// The value of resource needed to be allocate should also be no-zero
			if (valFixedBuff > 0) && (err == nil) {
				newAllocId := nextBwAllocIdAvailable
				bwInfo.AllocationId = strconv.FormatUint(uint64(newAllocId), 10)
				bufferInfo["down"] = strconv.FormatUint(valBuff-valFixedBuff, 10)
				// updateBuffer function takes updated buffer Information and store it in redis
				// returns error if unable to store allocation buffer in redis
				err = updateBuffer(bufferInfo)
				if err != nil {
					log.Error(err.Error())
					errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
					return err
				} else {
					log.Info("Allocation downlink buffer is updated")
				}
				nextBwAllocIdAvailable++ // increment allocation ID
			}
		} else {
			log.Debug("Resources are insufficient for this allocation")
		}

	case "01":
		// getting uplink buffer value from redis to update
		bufferInfo, valBuff, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		// To start allocating resource, buffer value should be non-zero
		if valBuff > 0 {
			var valFixedBuff uint64
			valFixedBuff, err = strconv.ParseUint(bwInfo.FixedAllocation, 10, 64)
			// The value of resource needed to be allocate should also be no-zero
			if (valFixedBuff > 0) && (err == nil) {
				newAllocId := nextBwAllocIdAvailable
				bwInfo.AllocationId = strconv.FormatUint(uint64(newAllocId), 10)
				bufferInfo["up"] = strconv.FormatUint(valBuff-valFixedBuff, 10)
				// updateBuffer function takes updated buffer Information and store it in redis
				// returns error if unable to store allocation buffer in redis
				err = updateBuffer(bufferInfo)
				if err != nil {
					log.Error(err.Error())
					errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
					return err
				} else {
					log.Info("Allocation uplink buffer is updated")
				}
				nextBwAllocIdAvailable++
			}
		} else {
			log.Debug("Resources are insufficient for this allocation")
		}

	case "10":
		// getting downlink/uplink buffer value from redis to update
		_, valBuffup, err := getUplinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}
		bufferInfo, valBuffdown, err := getDownlinkBuff()
		if err != nil {
			log.Error("Unable to fetch allocation buffer from redis")
			errHandlerProblemDetails(w, "Unable to fetch allocation buffer from redis", http.StatusInternalServerError)
			return err
		}

		// To start allocating resource, buffer values should be non-zero
		if (valBuffup > 0) && (valBuffdown > 0) {
			var valFixedBuff uint64
			valFixedBuff, err = strconv.ParseUint(bwInfo.FixedAllocation, 10, 64)
			// The value of resource needed to be allocate should also be no-zero
			if (valFixedBuff > 0) && (err == nil) {
				newAllocId := nextBwAllocIdAvailable
				bwInfo.AllocationId = strconv.FormatUint(uint64(newAllocId), 10)
				bufferInfo["up"] = strconv.FormatUint(valBuffup-(valFixedBuff/2), 10)
				bufferInfo["down"] = strconv.FormatUint(valBuffdown-(valFixedBuff/2), 10)
				// updateBuffer function takes updated buffer Information and store it in redis
				// returns error if unable to store allocation buffer in redis
				err = updateBuffer(bufferInfo)
				if err != nil {
					log.Error(err.Error())
					errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
					return err
				} else {
					log.Info("Allocation downlink buffer is updated")
				}
				nextBwAllocIdAvailable++
			}
		} else {
			log.Debug("Resources are insufficient for this allocation")
		}
	}
	return nil
}
