/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

#include "headers.p4"
#include "settings.p4"

#define USE_NICO_DELTA_CHECKSUM 1

/*************************************************************************
 * Parser
*/
parser MyParser(packet_in packet,
    out       headers hdr,
    inout     metadata meta,
    inout     standard_metadata_t standard_metadata) {

    #include "parsers.p4"
}

control MyDeparser(packet_out packet, in headers hdr) {
    #include "deparser.p4"
}

// OK-UNTIL-HERE

/*************************************************************************
**************  I N G R E S S   P R O C E S S I N G   *******************
*************************************************************************/

control MyIngress(inout headers hdr,
    inout metadata meta,
    inout standard_metadata_t standard_metadata) {

    #include "actions_nat64_generic.p4"
    #include "actions_egress.p4"
    #include "actions_icmp6_ndp_icmp.p4"
    #include "actions_arp.p4"
    #include "actions_delta_checksum.p4"    /* only used if enabled at compile time */

	/********************** APPLYING TABLES / MAIN LOGIC **************************/
    apply {
        if(hdr.ipv6.isValid()) {
            if(nat64.apply().hit) { /* generic / static nat64 done */
                if(hdr.icmp6.isValid()) {
                    nat64_icmp6_generic();

                    if(hdr.icmp6.type == ICMP6_ECHO_REPLY) {
                        hdr.icmp.type = ICMP_ECHO_REPLY;
                        hdr.icmp.code = 0;
                    }
                    if(hdr.icmp6.type == ICMP6_ECHO_REQUEST) {
                        hdr.icmp.type = ICMP_ECHO_REQUEST;
                        hdr.icmp.code = 0;
                    }
                }
                if(hdr.udp.isValid()) {
                    meta.chk_udp_v4 = 1;
                }
                if(hdr.tcp.isValid()) {
                    meta.chk_tcp_v4 = 1;
                }

                v4_networks.apply();       /* apply egress for IPv4 */
                exit;                      /* no further v6 processing */
            }

            icmp6.apply();                 /* icmp6 echo, icmp6 ndp */

            v6_networks.apply(); /* regular egress / routing */
        } else if(hdr.ipv4.isValid()) {
            if(icmp.apply().hit) {
                v4_networks.apply();
                exit;
            } else if(nat46.apply().hit) {
                if(hdr.icmp.isValid()) {
                    nat46_icmp_generic();

                    if(hdr.icmp.type == ICMP_ECHO_REPLY) {
                        hdr.icmp6.type = ICMP6_ECHO_REPLY;
                    }
                    if(hdr.icmp.type == ICMP_ECHO_REQUEST) {
                        hdr.icmp6.type = ICMP6_ECHO_REQUEST;
                    }
                }
                if(hdr.udp.isValid()) {
                    #ifdef USE_NICO_DELTA_CHECKSUM
                    delta_udp_from_v4_to_v6();
                    #else
                    meta.chk_udp_v6 = 1;
                    #endif
                }
                if(hdr.tcp.isValid()) {
                    #ifdef USE_NICO_DELTA_CHECKSUM
                    delta_tcp_from_v4_to_v6();
                    #else

                    meta.chk_tcp_v6 = 1;
                    #endif
                }

                v6_networks.apply();
                exit;
            }
            v4_networks.apply();             /* regular routing, egress */
        } else if(hdr.arp.isValid()) {
            if(v4_arp.apply().hit) {
                v4_arp_egress.apply();
            }
        }
    }
}


/*************************************************************************
****************  E G R E S S   P R O C E S S I N G   *******************
*************************************************************************/

control MyEgress(inout headers hdr,
                 inout metadata meta,
                 inout standard_metadata_t standard_metadata) {
	apply {
        // ingress clone
        if (standard_metadata.instance_type == 1){
            hdr.cpu.setValid();
            hdr.cpu.task = meta.task;
            hdr.cpu.ethertype = hdr.ethernet.ethertype;
            hdr.cpu.ingress_port = (bit<16>) meta.ingress_port;
            hdr.cpu.table_id = meta.table_id;

            hdr.ethernet.ethertype = TYPE_CPU;
        }
	}
}

/*************************************************************************
 * Checksums
*/
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
    apply {}
}

control MyComputeChecksum(inout headers  hdr, inout metadata meta) {
    apply {
        #ifndef _SUME_SWITCH_P4_
        #include "checksum_bmv2.p4"
        #endif
    }
}

/*************************************************************************
***********************  S W I T C H  *******************************
*************************************************************************/


V1Switch(
    MyParser(),
    MyVerifyChecksum(),
    MyIngress(),
    MyEgress(),
    MyComputeChecksum(),
    MyDeparser()
) main;