package org.onlab.onos.of.controller;

import java.util.Collections;

import org.onlab.packet.Ethernet;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;

public final class DefaultPacketContext implements PacketContext {

    private boolean free = true;
    private boolean isBuilt = false;
    private final OpenFlowSwitch sw;
    private final OFPacketIn pktin;
    private OFPacketOut pktout = null;

    private DefaultPacketContext(OpenFlowSwitch s, OFPacketIn pkt) {
        this.sw = s;
        this.pktin = pkt;
    }

    @Override
    public void block() {
        free = false;
    }

    @Override
    public void send() {
        if (free && isBuilt) {
            sw.sendMsg(pktout);
        }

    }

    @Override
    public void build(OFPort outPort) {
        isBuilt = true;

    }

    @Override
    public void build(Ethernet ethFrame, OFPort outPort) {
        if (isBuilt) {
            return;
        }
        OFPacketOut.Builder builder = sw.factory().buildPacketOut();
        OFAction act = sw.factory().actions()
                .buildOutput()
                .setPort(OFPort.of(outPort.getPortNumber()))
                .build();
        pktout = builder.setXid(pktin.getXid())
                .setBufferId(OFBufferId.NO_BUFFER)
                .setActions(Collections.singletonList(act))
                .setData(ethFrame.serialize())
                .build();
        isBuilt = true;
    }

    @Override
    public Ethernet parsed() {
        Ethernet eth = new Ethernet();
        eth.deserialize(pktin.getData(), 0, pktin.getTotalLen());
        return eth;
    }

    @Override
    public Dpid dpid() {
        return new Dpid(sw.getId());
    }

    public static PacketContext packetContextFromPacketIn(OpenFlowSwitch s, OFPacketIn pkt) {
        return new DefaultPacketContext(s, pkt);
    }

    @Override
    public Integer inPort() {
        return pktin.getInPort().getPortNumber();
    }

    @Override
    public byte[] unparsed() {
        return pktin.getData().clone();
    }

}

@@ -280,16 +280,86 @@
 Action;%0A
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;%0A
 import o
@@ -406,16 +406,16 @@
 fferId;%0A
-
 import o
@@ -1039,32 +1039,45 @@
 ride%0A    public 
+synchronized 
 void build(OFPor
@@ -1081,32 +1081,399 @@
 Port outPort) %7B%0A
+        if (isBuilt) %7B%0A            return;%0A        %7D%0A        OFPacketOut.Builder builder = sw.factory().buildPacketOut();%0A        OFAction act = buildOutput(outPort.getPortNumber());%0A        pktout = builder.setXid(pktin.getXid())%0A                .setBufferId(pktin.getBufferId())%0A                .setActions(Collections.singletonList(act))%0A                .build();%0A
         isBuilt 
@@ -1480,17 +1480,16 @@
 = true;%0A
-%0A
     %7D%0A%0A 
@@ -1508,24 +1508,37 @@
 %0A    public 
+synchronized 
 void build(E
@@ -1722,146 +1722,43 @@
 t = 
-sw.factory().actions()%0A                .buildOutput()%0A                .setPort(OFPort.of(outPort.getPortNumber()))%0A                .build(
+buildOutput(outPort.getPortNumber()
 );%0A 
@@ -2567,32 +2567,32 @@
 %5B%5D unparsed() %7B%0A
-
         return p
@@ -2622,10 +2622,243 @@
 %0A    %7D%0A%0A
+    private OFActionOutput buildOutput(Integer port) %7B%0A        OFActionOutput act = sw.factory().actions()%0A                .buildOutput()%0A                .setPort(OFPort.of(port))%0A                .build();%0A        return act;%0A    %7D%0A%0A
 %7D%0A

clean packet context