package net.beaconcontroller.devicemanager.internal;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import net.beaconcontroller.core.IOFSwitch;
import net.beaconcontroller.core.test.MockBeaconProvider;
import net.beaconcontroller.devicemanager.Device;
import net.beaconcontroller.packet.Data;
import net.beaconcontroller.packet.Ethernet;
import net.beaconcontroller.packet.IPacket;
import net.beaconcontroller.packet.IPv4;
import net.beaconcontroller.packet.UDP;
import net.beaconcontroller.test.BeaconTestCase;
import net.beaconcontroller.topology.ITopology;
import net.beaconcontroller.topology.SwitchPortTuple;

import org.junit.Before;
import org.junit.Test;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketIn.OFPacketInReason;

/**
 *
 * @author David Erickson (daviderickson@cs.stanford.edu)
 */
public class DeviceManagerImplTest extends BeaconTestCase {
    protected OFPacketIn packetIn;
    protected IPacket testPacket;
    protected byte[] testPacketSerialized;

    @Before
    public void setUp() throws Exception {
        super.setUp();

        // Build our test packet
        this.testPacket = new Ethernet()
            .setDestinationMACAddress("00:11:22:33:44:55")
            .setSourceMACAddress("00:44:33:22:11:00")
            .setEtherType(Ethernet.TYPE_IPv4)
            .setPayload(
                new IPv4()
                .setTtl((byte) 128)
                .setSourceAddress("192.168.1.1")
                .setDestinationAddress("192.168.1.2")
                .setPayload(new UDP()
                            .setSourcePort((short) 5000)
                            .setDestinationPort((short) 5001)
                            .setPayload(new Data(new byte[] {0x01}))));
        this.testPacketSerialized = testPacket.serialize();

        // Build the PacketIn
        this.packetIn = new OFPacketIn()
            .setBufferId(-1)
            .setInPort((short) 1)
            .setPacketData(this.testPacketSerialized)
            .setReason(OFPacketInReason.NO_MATCH)
            .setTotalLength((short) this.testPacketSerialized.length);
    }

    protected DeviceManagerImpl getDeviceManager() {
        return (DeviceManagerImpl) getApplicationContext().getBean("deviceManager");
    }

    protected MockBeaconProvider getMockBeaconProvider() {
        return (MockBeaconProvider) getApplicationContext().getBean("mockBeaconProvider");
    }

    @Test
    public void testDeviceDiscover() throws Exception {
        DeviceManagerImpl deviceManager = getDeviceManager();
        MockBeaconProvider mockBeaconProvider = getMockBeaconProvider();
        byte[] dataLayerSource = ((Ethernet)this.testPacket).getSourceMACAddress();

        // Mock up our expected behavior
        IOFSwitch mockSwitch = createMock(IOFSwitch.class);
        expect(mockSwitch.getId()).andReturn(1L).atLeastOnce();
        ITopology mockTopology = createMock(ITopology.class);
        expect(mockTopology.isInternal(new SwitchPortTuple(mockSwitch, 1))).andReturn(false);
        deviceManager.setTopology(mockTopology);

        // build our expected Device
        Device device = new Device();
        device.setDataLayerAddress(dataLayerSource);
        device.getSwPorts().add(new SwitchPortTuple(mockSwitch, (short)1));
        device.getNetworkAddresses().add(IPv4.toIPv4Address("192.168.1.1"));


        // Start recording the replay on the mocks
        replay(mockSwitch, mockTopology);
        // Get the listener and trigger the packet in
        mockBeaconProvider.dispatchMessage(mockSwitch, this.packetIn);

        // Verify the replay matched our expectations
        verify(mockSwitch, mockTopology);

        // Verify the device
        assertEquals(device, deviceManager.getDeviceByDataLayerAddress(dataLayerSource));

        // move the port on this device
        device.getSwPorts().add(new SwitchPortTuple(mockSwitch, (short)2));

        reset(mockSwitch, mockTopology);
        expect(mockSwitch.getId()).andReturn(2L).atLeastOnce();
        expect(mockTopology.isInternal(new SwitchPortTuple(mockSwitch, 2))).andReturn(false);

        // Start recording the replay on the mocks
        replay(mockSwitch, mockTopology);
        // Get the listener and trigger the packet in
        mockBeaconProvider.dispatchMessage(mockSwitch, this.packetIn.setInPort((short)2));

        // Verify the replay matched our expectations
        verify(mockSwitch, mockTopology);

        // Verify the device
        assertEquals(device, deviceManager.getDeviceByDataLayerAddress(dataLayerSource));
    }
}

@@ -502,20 +502,19 @@
 .packet.
-Data
+ARP
 ;%0Aimport
@@ -641,48 +641,8 @@
 v4;%0A
-import net.beaconcontroller.packet.UDP;%0A
 impo
@@ -1348,27 +1348,22 @@
     .set
-Destination
+Source
 MACAddre
@@ -1373,22 +1373,22 @@
 %2200:
-11:22:33:44:55
+44:33:22:11:00
 %22)%0A 
@@ -1394,38 +1394,43 @@
             .set
-Source
+Destination
 MACAddress(%2200:4
@@ -1424,38 +1424,38 @@
 Address(%2200:
-44:33:22:11:00
+11:22:33:44:55
 %22)%0A         
@@ -1485,20 +1485,19 @@
 et.TYPE_
-IPv4
+ARP
 )%0A      
@@ -1535,19 +1535,26 @@
     
+   
+ 
 new 
-IPv4()%0A
+ARP()%0A    
     
@@ -1573,125 +1573,153 @@
 .set
-Ttl((byte) 128)%0A                .setSourceAddress(%22192.168.1.1%22)%0A                .setDestinationAddress(%22192.168.1.2%22
+HardwareType(ARP.HW_TYPE_ETHERNET)%0A                    .setProtocolType(ARP.PROTO_TYPE_IP)%0A                    .setHardwareAddressLength((byte) 6
 )%0A  
@@ -1736,39 +1736,93 @@
     
+    
 .setP
-ayload(new UDP()%0A         
+rotocolAddressLength((byte) 4)%0A                    .setOpCode(ARP.OP_REPLY)%0A
     
@@ -1840,166 +1840,338 @@
     
+ 
 .setS
-ourcePort((short) 5000)%0A                            .setDestinationPort((short) 5001)%0A                            .setPayload(new Data(new byte%5B%5D %7B0x01%7D)
+enderHardwareAddress(Ethernet.toMACAddress(%2200:44:33:22:11:00%22))%0A                    .setSenderProtocolAddress(IPv4.toIPv4AddressBytes(%22192.168.1.1%22))%0A                    .setTargetHardwareAddress(Ethernet.toMACAddress(%2200:11:22:33:44:55%22))%0A                    .setTargetProtocolAddress(IPv4.toIPv4AddressBytes(%22192.168.1.2%22
 )));

Update DeviceManager unit test to test device discovery using an ARP reply rather than a UDP datagram