/*
 * Copyright 2019-2024 Open Networking Foundation (ONF) and the ONF Contributors
 *
 * 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 meters

import (
	"context"
	"fmt"

	"github.com/opencord/voltha-lib-go/v7/pkg/log"
	ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
	tp_pb "github.com/opencord/voltha-protos/v5/go/tech_profile"
)

// GetTrafficShapingInfo returns CIR,PIR and GIR values
func GetTrafficShapingInfo(ctx context.Context, meterConfig *ofp.OfpMeterConfig) (*tp_pb.TrafficShapingInfo, error) {
	meterBandSize := len(meterConfig.Bands)
	switch meterBandSize {
	case 1:
		band := meterConfig.Bands[0]
		if band.BurstSize == 0 { // GIR = PIR, Burst Size = 0, tcont type 1
			return &tp_pb.TrafficShapingInfo{Pir: band.Rate, Gir: band.Rate}, nil
		}
		return &tp_pb.TrafficShapingInfo{Pir: band.Rate, Pbs: band.BurstSize}, nil // PIR, tcont type 4
	case 2:
		firstBand, secondBand := meterConfig.Bands[0], meterConfig.Bands[1]
		if firstBand.BurstSize == 0 && secondBand.BurstSize == 0 &&
			firstBand.Rate == secondBand.Rate { // PIR = GIR, tcont type 1
			return &tp_pb.TrafficShapingInfo{Pir: firstBand.Rate, Gir: secondBand.Rate}, nil
		}
		if firstBand.BurstSize > 0 && secondBand.BurstSize > 0 { // PIR, CIR, tcont type 2 or 3
			if firstBand.Rate > secondBand.Rate { // always PIR >= CIR
				return &tp_pb.TrafficShapingInfo{Pir: firstBand.Rate, Pbs: firstBand.BurstSize, Cir: secondBand.Rate, Cbs: secondBand.BurstSize}, nil
			}
			return &tp_pb.TrafficShapingInfo{Pir: secondBand.Rate, Pbs: secondBand.BurstSize, Cir: firstBand.Rate, Cbs: firstBand.BurstSize}, nil
		}
	case 3: // PIR,CIR,GIR, tcont type 5
		var count, girIndex int
		for i, band := range meterConfig.Bands {
			if band.BurstSize == 0 { // find GIR
				count = count + 1
				girIndex = i
			}
		}
		if count == 1 {
			bands := make([]*ofp.OfpMeterBandHeader, len(meterConfig.Bands))
			copy(bands, meterConfig.Bands)
			pirCirBands := append(bands[:girIndex], bands[girIndex+1:]...)
			firstBand, secondBand := pirCirBands[0], pirCirBands[1]
			if firstBand.Rate > secondBand.Rate {
				return &tp_pb.TrafficShapingInfo{Pir: firstBand.Rate, Pbs: firstBand.BurstSize, Cir: secondBand.Rate, Cbs: secondBand.BurstSize, Gir: meterConfig.Bands[girIndex].Rate}, nil
			}
			return &tp_pb.TrafficShapingInfo{Pir: secondBand.Rate, Pbs: secondBand.BurstSize, Cir: firstBand.Rate, Cbs: firstBand.BurstSize, Gir: meterConfig.Bands[girIndex].Rate}, nil
		}
	default:
		logger.Errorw(ctx, "invalid-meter-config", log.Fields{"meter-config": meterConfig})
		return nil, fmt.Errorf("invalid-meter-config: %v", meterConfig)
	}
	return nil, fmt.Errorf("invalid-meter-config: %v", meterConfig)
}
