/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * 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 com.android.networkstack.metrics;

import android.net.apf.ApfCounterTracker.Counter;
import android.stats.connectivity.CounterName;

import androidx.annotation.VisibleForTesting;

/**
 * Class to record the network stack ApfSessionInfo metrics into statsd.
 *
 * This class is not thread-safe, and should always be accessed from the same thread.
 *
 * @hide
 */
public class ApfSessionInfoMetrics {
    // Define the maximum size of the counter list
    public static final int MAX_NUM_OF_COUNTERS = Counter.class.getEnumConstants().length - 1;
    private final ApfSessionInfoReported.Builder mStatsBuilder =
            ApfSessionInfoReported.newBuilder();
    private final ApfCounterList.Builder mApfCounterListBuilder = ApfCounterList.newBuilder();

    /**
     * Write the version to mStatsBuilder.
     */
    public void setVersion(final int version) {
        mStatsBuilder.setVersion(version);
    }

    /**
     * Write the memory size to mStatsBuilder.
     */
    public void setMemorySize(final int memorySize) {
        mStatsBuilder.setMemorySize(memorySize);
    }

    /**
     * Add an APF counter to the metrics builder.
     */
    public void addApfCounter(final Counter counter, final long value) {
        if (mApfCounterListBuilder.getApfCounterCount() >= MAX_NUM_OF_COUNTERS) return;
        final ApfCounter.Builder apfCounterBuilder = ApfCounter.newBuilder()
                .setCounterName(apfFilterCounterToEnum(counter))
                .setCounterValue(value);

        mApfCounterListBuilder.addApfCounter(apfCounterBuilder);
    }

    /**
     * Write the session duration to mStatsBuilder.
     */
    public void setApfSessionDurationSeconds(final int durationSeconds) {
        mStatsBuilder.setApfSessionDurationSeconds(durationSeconds);
    }

    /**
     * Write the number of times APF program updated to mStatsBuilder.
     */
    public void setNumOfTimesApfProgramUpdated(final int updatedTimes) {
        mStatsBuilder.setNumOfTimesApfProgramUpdated(updatedTimes);
    }

    /**
     * Write the maximum program size to mStatsBuilder.
     */
    public void setMaxProgramSize(final int programSize) {
        mStatsBuilder.setMaxProgramSize(programSize);
    }

    /**
     * Write the ApfSessionInfoReported proto into statsd.
     */
    public ApfSessionInfoReported statsWrite() {
        mStatsBuilder.setApfCounterList(mApfCounterListBuilder);
        final ApfSessionInfoReported stats = mStatsBuilder.build();
        final byte[] apfCounterList = stats.getApfCounterList().toByteArray();
        NetworkStackStatsLog.write(NetworkStackStatsLog.APF_SESSION_INFO_REPORTED,
                stats.getVersion(),
                stats.getMemorySize(),
                apfCounterList,
                stats.getApfSessionDurationSeconds(),
                stats.getNumOfTimesApfProgramUpdated(),
                stats.getMaxProgramSize());
        return stats;
    }

    /**
     *  Map ApfCounterTracker.Counter to {@link CounterName}.
     */
    @VisibleForTesting
    public static CounterName apfFilterCounterToEnum(final Counter counter) {
        switch(counter) {
            case TOTAL_PACKETS:
                return CounterName.CN_TOTAL_PACKETS;
            case PASSED_ARP:
                return CounterName.CN_PASSED_ARP;
            case PASSED_DHCP:
                return CounterName.CN_PASSED_DHCP;
            case PASSED_IPV4:
                return CounterName.CN_PASSED_IPV4;
            case PASSED_IPV6_NON_ICMP:
                return CounterName.CN_PASSED_IPV6_NON_ICMP;
            case PASSED_IPV4_UNICAST:
                return CounterName.CN_PASSED_IPV4_UNICAST;
            case PASSED_IPV6_ICMP:
                return CounterName.CN_PASSED_IPV6_ICMP;
            case PASSED_IPV6_UNICAST_NON_ICMP:
                return CounterName.CN_PASSED_IPV6_UNICAST_NON_ICMP;
            // PASSED_ARP_NON_IPV4 and PASSED_ARP_UNKNOWN were deprecated in ApfFilter:
            //     PASSED_ARP_NON_IPV4 ==> DROPPED_ARP_NON_IPV4
            //     PASSED_ARP_UNKNOWN  ==> DROPPED_ARP_UNKNOWN
            // They are not supported in the metrics.
            case PASSED_ARP_NON_IPV4:
            case PASSED_ARP_UNKNOWN:
                return CounterName.CN_UNKNOWN;
            case PASSED_ARP_UNICAST_REPLY:
                return CounterName.CN_PASSED_ARP_UNICAST_REPLY;
            case PASSED_NON_IP_UNICAST:
                return CounterName.CN_PASSED_NON_IP_UNICAST;
            case PASSED_MDNS:
                return CounterName.CN_PASSED_MDNS;
            case DROPPED_ETH_BROADCAST:
                return CounterName.CN_DROPPED_ETH_BROADCAST;
            case DROPPED_RA:
                return CounterName.CN_DROPPED_RA;
            case DROPPED_GARP_REPLY:
                return CounterName.CN_DROPPED_GARP_REPLY;
            case DROPPED_ARP_OTHER_HOST:
                return CounterName.CN_DROPPED_ARP_OTHER_HOST;
            case DROPPED_IPV4_L2_BROADCAST:
                return CounterName.CN_DROPPED_IPV4_L2_BROADCAST;
            case DROPPED_IPV4_BROADCAST_ADDR:
                return CounterName.CN_DROPPED_IPV4_BROADCAST_ADDR;
            case DROPPED_IPV4_BROADCAST_NET:
                return CounterName.CN_DROPPED_IPV4_BROADCAST_NET;
            case DROPPED_IPV4_MULTICAST:
                return CounterName.CN_DROPPED_IPV4_MULTICAST;
            case DROPPED_IPV6_ROUTER_SOLICITATION:
                return CounterName.CN_DROPPED_IPV6_ROUTER_SOLICITATION;
            case DROPPED_IPV6_MULTICAST_NA:
                return CounterName.CN_DROPPED_IPV6_MULTICAST_NA;
            case DROPPED_IPV6_MULTICAST:
                return CounterName.CN_DROPPED_IPV6_MULTICAST;
            case DROPPED_IPV6_MULTICAST_PING:
                return CounterName.CN_DROPPED_IPV6_MULTICAST_PING;
            case DROPPED_IPV6_NON_ICMP_MULTICAST:
                return CounterName.CN_DROPPED_IPV6_NON_ICMP_MULTICAST;
            case DROPPED_802_3_FRAME:
                return CounterName.CN_DROPPED_802_3_FRAME;
            case DROPPED_ETHERTYPE_NOT_ALLOWED:
                return CounterName.CN_DROPPED_ETHERTYPE_DENYLISTED;
            case DROPPED_ARP_REPLY_SPA_NO_HOST:
                return CounterName.CN_DROPPED_ARP_REPLY_SPA_NO_HOST;
            case DROPPED_IPV4_KEEPALIVE_ACK:
                return CounterName.CN_DROPPED_IPV4_KEEPALIVE_ACK;
            case DROPPED_IPV6_KEEPALIVE_ACK:
                return CounterName.CN_DROPPED_IPV6_KEEPALIVE_ACK;
            case DROPPED_IPV4_NATT_KEEPALIVE:
                return CounterName.CN_DROPPED_IPV4_NATT_KEEPALIVE;
            case DROPPED_MDNS:
                return CounterName.CN_DROPPED_MDNS;
            case DROPPED_IPV4_TCP_PORT7_UNICAST:
                // TODO: Not supported yet in the metrics backend.
                return CounterName.CN_UNKNOWN;
            case DROPPED_ARP_NON_IPV4:
                return CounterName.CN_DROPPED_ARP_NON_IPV4;
            case DROPPED_ARP_UNKNOWN:
                return CounterName.CN_DROPPED_ARP_UNKNOWN;
            default:
                return CounterName.CN_UNKNOWN;
        }
    }
}
