//go:build !windows

// Copyright (c) 2022 Tigera, Inc. All rights reserved.
//
// 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 bpfmap

import (
	"fmt"
	"os"

	"github.com/projectcalico/calico/felix/bpf/arp"
	"github.com/projectcalico/calico/felix/bpf/conntrack"
	"github.com/projectcalico/calico/felix/bpf/counters"
	"github.com/projectcalico/calico/felix/bpf/failsafes"
	"github.com/projectcalico/calico/felix/bpf/ifstate"
	"github.com/projectcalico/calico/felix/bpf/ipsets"
	"github.com/projectcalico/calico/felix/bpf/maps"
	"github.com/projectcalico/calico/felix/bpf/nat"
	"github.com/projectcalico/calico/felix/bpf/routes"
	"github.com/projectcalico/calico/felix/bpf/state"
)

type Maps struct {
	IpsetsMap       maps.Map
	StateMap        maps.Map
	ArpMap          maps.Map
	FailsafesMap    maps.Map
	FrontendMap     maps.Map
	BackendMap      maps.Map
	AffinityMap     maps.Map
	RouteMap        maps.Map
	CtMap           maps.Map
	SrMsgMap        maps.Map
	CtNatsMap       maps.Map
	IfStateMap      maps.Map
	RuleCountersMap maps.Map
	CountersMap     maps.Map
}

func (m *Maps) Destroy() {
	mps := []maps.Map{
		m.IpsetsMap,
		m.StateMap,
		m.ArpMap,
		m.FailsafesMap,
		m.FrontendMap,
		m.BackendMap,
		m.AffinityMap,
		m.RouteMap,
		m.CtMap,
		m.SrMsgMap,
		m.CtNatsMap,
	}

	for _, m := range mps {
		os.Remove(m.(*maps.PinnedMap).Path())
		m.(*maps.PinnedMap).Close()
	}
}

func CreateBPFMaps() (*Maps, error) {
	mps := []maps.Map{}
	ret := new(Maps)

	ret.IpsetsMap = ipsets.Map()
	mps = append(mps, ret.IpsetsMap)

	ret.StateMap = state.Map()
	mps = append(mps, ret.StateMap)

	ret.ArpMap = arp.Map()
	mps = append(mps, ret.ArpMap)

	ret.FailsafesMap = failsafes.Map()
	mps = append(mps, ret.FailsafesMap)

	ret.FrontendMap = nat.FrontendMap()
	mps = append(mps, ret.FrontendMap)

	ret.BackendMap = nat.BackendMap()
	mps = append(mps, ret.BackendMap)

	ret.AffinityMap = nat.AffinityMap()
	mps = append(mps, ret.AffinityMap)

	ret.RouteMap = routes.Map()
	mps = append(mps, ret.RouteMap)

	ret.CtMap = conntrack.Map()
	mps = append(mps, ret.CtMap)

	ret.SrMsgMap = nat.SendRecvMsgMap()
	mps = append(mps, ret.SrMsgMap)

	ret.CtNatsMap = nat.AllNATsMsgMap()
	mps = append(mps, ret.CtNatsMap)

	ret.IfStateMap = ifstate.Map()
	mps = append(mps, ret.IfStateMap)

	ret.RuleCountersMap = counters.PolicyMap()
	mps = append(mps, ret.RuleCountersMap)

	ret.CountersMap = counters.Map()
	mps = append(mps, ret.CountersMap)

	for i, bpfMap := range mps {
		err := bpfMap.EnsureExists()
		if err != nil {

			for j := 0; j < i; j++ {
				m := mps[j]
				os.Remove(m.(*maps.PinnedMap).Path())
				m.(*maps.PinnedMap).Close()
			}

			return nil, fmt.Errorf("failed to create %s map, err=%w", bpfMap.GetName(), err)
		}
	}

	return ret, nil
}
