#ifndef NICO_DELTA_CHECKSUM
#define NICO_DELTA_CHECKSUM



#include "headers.p4"

action v4sum() {
    bit<16> tmp = 0;

    tmp = tmp + (bit<16>) hdr.ipv4.src_addr[15:0];              // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv4.src_addr[31:16];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv4.dst_addr[15:0];              // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv4.dst_addr[31:16];             // 16 bit

    tmp = tmp + (bit<16>) hdr.ipv4.totalLen -20;                // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv4.protocol;                    // 8 bit

    meta.v4sum = ~tmp;
}

action v6sum() {
    bit<16> tmp = 0;

    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[15:0];              // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[31:16];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[47:32];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[63:48];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[79:64];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[95:80];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[111:96];            // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.src_addr[127:112];           // 16 bit

    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[15:0];              // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[31:16];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[47:32];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[63:48];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[79:64];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[95:80];             // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[111:96];            // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.dst_addr[127:112];           // 16 bit

    tmp = tmp + (bit<16>) hdr.ipv6.payload_length;              // 16 bit
    tmp = tmp + (bit<16>) hdr.ipv6.next_header;                 // 8 bit

    meta.v6sum = ~tmp;
}

action delta_prepare()
{
    v4sum();
    v6sum();
}

action delta_udp_from_v4_to_v6()
{
    delta_prepare();

    bit<17> tmp = (bit<17>) hdr.udp.checksum + (bit<17>) meta.v6sum;
    if (tmp[16:16] == 1) {
        tmp = tmp + 1;
        tmp[16:16] = 0;
    }
    tmp = tmp + (bit<17>) (0xffff - meta.v4sum);
    if (tmp[16:16] == 1) {
        tmp = tmp + 1;
        tmp[16:16] = 0;
    }

    hdr.udp.checksum = (bit<16>) tmp;
}

action delta_tcp_from_v4_to_v6()
{
    delta_prepare();

    bit<17> tmp = (bit<17>) hdr.tcp.checksum + (bit<17>) meta.v6sum;
    if (tmp[16:16] == 1) {
        tmp = tmp + 1;
        tmp[16:16] = 0;
    }
    tmp = tmp + (bit<17>) (0xffff - meta.v4sum);
    if (tmp[16:16] == 1) {
        tmp = tmp + 1;
        tmp[16:16] = 0;
    }

    hdr.tcp.checksum = (bit<16>) tmp;
}

action delta_ipv4_from_v6_to_v4()
{
    bit<16> tmp = 0;


    tmp = tmp + (bit<16>) hdr.ipv4.version;          /* 4 bit */
	tmp = tmp + (bit<16>) hdr.ipv4.ihl;              /* 4 bit */
    tmp = tmp + (bit<16>) hdr.ipv4.diff_serv;        /* 6 bit */
    tmp = tmp + (bit<16>) hdr.ipv4.ecn;              /* 2 bit */
    tmp = tmp + (bit<16>) hdr.ipv4.totalLen;         /* 16 bit */
    tmp = tmp + (bit<16>) hdr.ipv4.identification;   /* 16 bit */
    tmp = tmp + (bit<16>) hdr.ipv4.flags;            /* 3 bit */
    /* we don't have ANY checksum, but tcp or udp: we can
       base on that ones for calculating the diff for IPv4

    Does NOT contain payload! -> can be done manually
    tmp = tmp + (bit<16>) hdr.ipv4.fragOffset;
    tmp = tmp + (bit<16>) hdr.ipv4.ttl,
    tmp = tmp + (bit<16>) hdr.ipv4.protocol,
    tmp = tmp + (bit<16>) hdr.ipv4.src_addr,
    tmp = tmp + (bit<16>) hdr.ipv4.dst_addr

    */
    hdr.ipv4.checksum = 0;    /* TO BE DONE! */
}


#endif