/*
 * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.netvirt.ipv6service;

import java.math.BigInteger;
import java.util.List;
import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
import org.opendaylight.genius.mdsalutil.MDSALUtil;
import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Ipv6ServiceInterfaceEventListener
        extends AsyncDataTreeChangeListenerBase<Interface, Ipv6ServiceInterfaceEventListener>
        implements ClusteredDataTreeChangeListener<Interface>, AutoCloseable {
    private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceInterfaceEventListener.class);
    private final DataBroker dataBroker;
    private final IfMgr ifMgr;

    /**
     * Intialize the member variables.
     * @param broker the data broker instance.
     */
    public Ipv6ServiceInterfaceEventListener(DataBroker broker) {
        super(Interface.class, Ipv6ServiceInterfaceEventListener.class);
        this.dataBroker = broker;
        ifMgr = IfMgr.getIfMgrInstance();
    }

    public void start() {
        LOG.info("{} start", getClass().getSimpleName());
        registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
    }

    @Override
    protected InstanceIdentifier<Interface> getWildCardPath() {
        return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
    }

    @Override
    protected void remove(InstanceIdentifier<Interface> key, Interface del) {
        LOG.debug("Port removed {}, {}", key, del);
    }

    @Override
    protected void update(InstanceIdentifier<Interface> key, Interface dataObjectModificationBefore,
                          Interface dataObjectModificationAfter) {
        // TODO Auto-generated method stub
        LOG.debug("Port updated...");
    }

    @Override
    protected void add(InstanceIdentifier<Interface> key, Interface add) {
        LOG.debug("Port added {}, {}", key, add);
        List<String> ofportIds = add.getLowerLayerIf();
        // When a port is created, we receive two notifications.
        // 1. where the interface name is dpnid:tapinterfaceName (f.e., 238412509713739:tapf662f5bf-9d)
        // 2. neutron interface with name as UUID (f.e., f662f5bf-9d54-4dd7-8bcd-7a0a3a0bae4a)
        // In ipv6service, we are interested only in notification-2, so we skip notification-1.
        if (ofportIds == null || ofportIds.isEmpty() || add.getName().contains(":")) {
            return;
        }

        if (add.getType() != null && add.getType().equals(Tunnel.class)) {
            LOG.info("iface {} is a tunnel interface, skipping.", add);
            return;
        }

        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface iface;
        iface = Ipv6ServiceUtils.getInterface(dataBroker, add.getName());
        if (null != iface) {
            LOG.debug("Port added {}", iface);
            NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
            BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));

            if (!dpId.equals(Ipv6Constants.INVALID_DPID)) {
                Uuid portId = new Uuid(iface.getName());
                VirtualPort port = ifMgr.obtainV6Interface(portId);
                if (port == null) {
                    LOG.info("Port {} not found, skipping.", port);
                    return;
                }

                Long ofPort = MDSALUtil.getOfPortNumberFromPortName(nodeConnectorId);
                ifMgr.updateInterface(portId, dpId, ofPort);

                VirtualPort routerPort = ifMgr.getRouterV6InterfaceForNetwork(port.getNetworkID());
                if (routerPort == null) {
                    LOG.info("Port {} is not associated to a Router, skipping.", routerPort);
                    return;
                }
                // Check and program icmpv6 punt flows on the dpnID if its the first VM on the host.
                ifMgr.programIcmpv6PuntFlowsIfNecessary(portId, dpId, routerPort);
            }
        }
    }

    @Override
    protected Ipv6ServiceInterfaceEventListener getDataTreeChangeListener() {
        return Ipv6ServiceInterfaceEventListener.this;
    }
}

@@ -2992,32 +2992,321 @@
 ed...%22);%0A    %7D%0A%0A
+    private boolean isNeutronPort(String name) %7B%0A        try %7B%0A            Uuid portId = new Uuid(name);%0A            return true;%0A        %7D catch (IllegalArgumentException e) %7B%0A            LOG.debug(%22Port %7B%7D is not a Neutron Port, skipping.%22, name);%0A        %7D%0A        return false;%0A    %7D%0A%0A
     @Override%0A  
@@ -3530,19 +3530,24 @@
 receive 
-two
+multiple
  notific
@@ -3558,207 +3558,8 @@
 ns.%0A
-        // 1. where the interface name is dpnid:tapinterfaceName (f.e., 238412509713739:tapf662f5bf-9d)%0A        // 2. neutron interface with name as UUID (f.e., f662f5bf-9d54-4dd7-8bcd-7a0a3a0bae4a)%0A
     
@@ -3588,16 +3588,21 @@
  we are 
+only 
 interest
@@ -3604,23 +3604,22 @@
 erested 
-only in
+in the
  notific
@@ -3627,10 +3627,24 @@
 tion
--2
+ for NeutronPort
 , so
@@ -3652,16 +3652,22 @@
 we skip 
+other 
 notifica
@@ -3674,11 +3674,9 @@
 tion
--1.
+s
 %0A   
@@ -3728,16 +3728,31 @@
 ty() %7C%7C 
+!isNeutronPort(
 add.getN
@@ -3760,21 +3760,8 @@
 me()
-.contains(%22:%22
 )) %7B

Fix exception in IPv6Service for non-neutron ports