/*
   Copyright 2020 the original author or authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package ofagent

import (
	"context"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	"github.com/opencord/ofagent-go/internal/pkg/openflow"
	"github.com/opencord/voltha-lib-go/v3/pkg/log"
)

func (ofa *OFAgent) synchronizeDeviceList(ctx context.Context) {
	// Refresh once to get everything started
	ofa.refreshDeviceList()

	tick := time.NewTicker(ofa.DeviceListRefreshInterval)
loop:
	for {
		select {
		case <-ctx.Done():
			break loop
		case <-tick.C:
			ofa.refreshDeviceList()
		}
	}
	tick.Stop()
}

func (ofa *OFAgent) refreshDeviceList() {
	// If we exit, assume disconnected
	if ofa.volthaClient == nil {
		logger.Error("no-voltha-connection")
		ofa.events <- ofaEventVolthaDisconnected
		return
	}
	deviceList, err := ofa.volthaClient.Get().ListLogicalDevices(context.Background(), &empty.Empty{})
	if err != nil {
		logger.Errorw("ofagent failed to query device list from voltha",
			log.Fields{"error": err})
		ofa.events <- ofaEventVolthaDisconnected
		return
	}
	devices := deviceList.GetItems()

	var toAdd []string
	var toDel []string
	var deviceIDMap = make(map[string]string)
	for i := 0; i < len(devices); i++ {
		deviceID := devices[i].GetId()
		deviceIDMap[deviceID] = deviceID
		if ofa.clientMap[deviceID] == nil {
			toAdd = append(toAdd, deviceID)
		}
	}
	for key := range ofa.clientMap {
		if deviceIDMap[key] == "" {
			toDel = append(toDel, key)
		}
	}
	logger.Debugw("GrpcClient refreshDeviceList", log.Fields{"ToAdd": toAdd, "ToDel": toDel})
	for i := 0; i < len(toAdd); i++ {
		ofa.addOFClient(toAdd[i]) // client is started in addOFClient
	}
	for i := 0; i < len(toDel); i++ {
		ofa.clientMap[toDel[i]].Stop()
		ofa.mapLock.Lock()
		delete(ofa.clientMap, toDel[i])
		ofa.mapLock.Unlock()
	}
}

func (ofa *OFAgent) addOFClient(deviceID string) *openflow.OFClient {
	logger.Debugw("GrpcClient addClient called ", log.Fields{"device-id": deviceID})
	ofa.mapLock.Lock()
	ofc := ofa.clientMap[deviceID]
	if ofc == nil {
		ofc = openflow.NewOFClient(&openflow.OFClient{
			DeviceID:             deviceID,
			OFControllerEndPoint: ofa.OFControllerEndPoint,
			VolthaClient:         ofa.volthaClient,
			PacketOutChannel:     ofa.packetOutChannel,
			ConnectionMaxRetries: ofa.ConnectionMaxRetries,
			ConnectionRetryDelay: ofa.ConnectionRetryDelay,
		})
		go ofc.Run(context.Background())
		ofa.clientMap[deviceID] = ofc
	}
	ofa.mapLock.Unlock()
	logger.Debugw("Finished with addClient", log.Fields{"deviceID": deviceID})
	return ofc
}

func (ofa *OFAgent) getOFClient(deviceID string) *openflow.OFClient {
	ofc := ofa.clientMap[deviceID]
	if ofc == nil {
		ofc = ofa.addOFClient(deviceID)
	}
	return ofc
}
