// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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.

// Binary stats-client is an example VPP management application that exercises the
// govpp API for interface counters together with asynchronous connection to VPP.
package main

// Generates Go bindings for all VPP APIs located in the json directory.
//go:generate binapi-generator --input-dir=bin_api --output-dir=bin_api

import (
	"fmt"
	"os"
	"os/signal"

	"git.fd.io/govpp.git"
	"git.fd.io/govpp.git/api"
	"git.fd.io/govpp.git/api/ifcounters"
	"git.fd.io/govpp.git/core"
	"git.fd.io/govpp.git/core/bin_api/vpe"
	"git.fd.io/govpp.git/examples/bin_api/interfaces"
)

func main() {
	fmt.Println("Starting stats VPP client...")

	// async connect to VPP
	conn, statCh, err := govpp.AsyncConnect()
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(1)
	}
	defer conn.Disconnect()

	// create an API channel that will be used in the examples
	ch, err := conn.NewAPIChannel()
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(1)
	}
	defer ch.Close()

	// create channel for Interrupt signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)

	var subs *api.NotifSubscription
	var notifChan chan api.Message

	// loop until Interrupt signal is received
loop:
	for {
		select {

		case connEvent := <-statCh:
			// VPP connection state change
			switch connEvent.State {
			case core.Connected:
				fmt.Println("VPP connected.")
				if subs == nil {
					subs, notifChan = subscribeNotification(ch)
				}
				requestStatistics(ch)

			case core.Disconnected:
				fmt.Println("VPP disconnected.")
			}

		case notifMsg := <-notifChan:
			// counter notification received
			processCounters(notifMsg.(*interfaces.VnetInterfaceCounters))

		case <-sigChan:
			// interrupt received
			fmt.Println("Interrupt received, exiting.")
			break loop
		}
	}

	ch.UnsubscribeNotification(subs)
}

// subscribeNotification subscribes for interface counters notifications.
func subscribeNotification(ch *api.Channel) (*api.NotifSubscription, chan api.Message) {

	notifChan := make(chan api.Message, 100)
	subs, _ := ch.SubscribeNotification(notifChan, interfaces.NewVnetInterfaceCounters)

	return subs, notifChan
}

// requestStatistics requests interface counters notifications from VPP.
func requestStatistics(ch *api.Channel) {
	ch.SendRequest(&vpe.WantStats{
		Pid:           uint32(os.Getpid()),
		EnableDisable: 1,
	}).ReceiveReply(&vpe.WantStatsReply{})
}

// processCounters processes a counter message received from VPP.
func processCounters(msg *interfaces.VnetInterfaceCounters) {
	fmt.Printf("%+v\n", msg)

	if msg.IsCombined == 0 {
		// simple counter
		counters, err := ifcounters.DecodeCounters(ifcounters.VnetInterfaceCounters(*msg))
		if err != nil {
			fmt.Println("Error:", err)
		} else {
			fmt.Printf("%+v\n", counters)
		}
	} else {
		// combined counter
		counters, err := ifcounters.DecodeCombinedCounters(ifcounters.VnetInterfaceCounters(*msg))
		if err != nil {
			fmt.Println("Error:", err)
		} else {
			fmt.Printf("%+v\n", counters)
		}
	}
}