// See the file "COPYING" in the main distribution directory for copyright.

#include "zeek/packet_analysis/protocol/tcp/Stats.h"

#include "zeek/File.h"

namespace zeek::packet_analysis::TCP {

TCPStateStats::TCPStateStats() {
    for ( int i = 0; i < analyzer::tcp::TCP_ENDPOINT_RESET + 1; ++i ) // NOLINT
        for ( int j = 0; j < analyzer::tcp::TCP_ENDPOINT_RESET + 1; ++j )
            state_cnt[i][j] = 0;
}

void TCPStateStats::ChangeState(analyzer::tcp::EndpointState o_prev, analyzer::tcp::EndpointState o_now,
                                analyzer::tcp::EndpointState r_prev, analyzer::tcp::EndpointState r_now) {
    --state_cnt[o_prev][r_prev];
    ++state_cnt[o_now][r_now];
}

void TCPStateStats::FlipState(analyzer::tcp::EndpointState orig, analyzer::tcp::EndpointState resp) {
    --state_cnt[orig][resp];
    ++state_cnt[resp][orig];
}

unsigned int TCPStateStats::NumStatePartial() const {
    unsigned int sum = 0;
    for ( int i = 0; i < analyzer::tcp::TCP_ENDPOINT_RESET + 1; ++i ) {
        sum += state_cnt[analyzer::tcp::TCP_ENDPOINT_PARTIAL][i];
        sum += state_cnt[i][analyzer::tcp::TCP_ENDPOINT_PARTIAL];
    }

    return sum;
}

void TCPStateStats::PrintStats(File* file, const char* prefix) {
    file->Write(prefix);
    file->Write("        Inact.  Syn.    SA      Part.   Est.    Fin.    Rst.\n");

    for ( int i = 0; i < analyzer::tcp::TCP_ENDPOINT_RESET + 1; ++i ) {
        file->Write(prefix);

        switch ( i ) {
// This macro really doesn't save us much typing, if that was the intention
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define STATE_STRING(state, str)                                                                                       \
    case state: file->Write(str); break;

            STATE_STRING(analyzer::tcp::TCP_ENDPOINT_INACTIVE, "Inact.");
            STATE_STRING(analyzer::tcp::TCP_ENDPOINT_SYN_SENT, "Syn.  ");
            STATE_STRING(analyzer::tcp::TCP_ENDPOINT_SYN_ACK_SENT, "SA    ");
            STATE_STRING(analyzer::tcp::TCP_ENDPOINT_PARTIAL, "Part. ");
            STATE_STRING(analyzer::tcp::TCP_ENDPOINT_ESTABLISHED, "Est.  ");
            STATE_STRING(analyzer::tcp::TCP_ENDPOINT_CLOSED, "Fin.  ");
            STATE_STRING(analyzer::tcp::TCP_ENDPOINT_RESET, "Rst.  ");
            default: break;
        }

        file->Write("  ");

        for ( int j = 0; j < analyzer::tcp::TCP_ENDPOINT_RESET + 1; ++j ) {
            unsigned int n = state_cnt[i][j];
            if ( n > 0 ) {
                char buf[32];
                snprintf(buf, sizeof(buf), "%-8d", state_cnt[i][j]);
                file->Write(buf);
            }
            else
                file->Write("        ");
        }

        file->Write("\n");
    }
}

} // namespace zeek::packet_analysis::TCP
