/*
 * Copyright 2016-present Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.onosproject.segmentrouting;

import com.google.common.collect.ImmutableSet;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.incubator.net.routing.ResolvedRoute;
import org.onosproject.incubator.net.routing.RouteEvent;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Handles RouteEvent and manages routing entries.
 */
public class RouteHandler {
    private static final Logger log = LoggerFactory.getLogger(RouteHandler.class);
    private final SegmentRoutingManager srManager;

    public RouteHandler(SegmentRoutingManager srManager) {
        this.srManager = srManager;
    }

    protected void init(DeviceId deviceId) {
        srManager.routeService.getNextHops().forEach(nextHop -> {
            if (nextHop.location().deviceId().equals(deviceId)) {
                srManager.routeService.getRoutesForNextHop(nextHop.ip()).forEach(route -> {
                    ResolvedRoute resolvedRoute =
                            new ResolvedRoute(route, nextHop.mac(), nextHop.location());
                    processRouteAddedInternal(resolvedRoute);
                });
            }
        });
    }

    protected void processRouteAdded(RouteEvent event) {
        log.info("processRouteAdded {}", event);
        processRouteAddedInternal(event.subject());
    }

    private void processRouteAddedInternal(ResolvedRoute route) {
        IpPrefix prefix = route.prefix();
        MacAddress nextHopMac = route.nextHopMac();
        // TODO ResolvedRoute does not contain VLAN information.
        //      Therefore we only support untagged nexthop for now.
        VlanId nextHopVlan = VlanId.NONE;
        ConnectPoint location = route.location();

        srManager.deviceConfiguration.addSubnet(location, prefix);
        srManager.defaultRoutingHandler.populateSubnet(location, ImmutableSet.of(prefix));
        srManager.routingRulePopulator.populateRoute(location.deviceId(), prefix,
                nextHopMac, nextHopVlan, location.port());
    }

    protected void processRouteUpdated(RouteEvent event) {
        log.info("processRouteUpdated {}", event);
        processRouteRemovedInternal(event.prevSubject());
        processRouteAddedInternal(event.subject());
    }

    protected void processRouteRemoved(RouteEvent event) {
        log.info("processRouteRemoved {}", event);
        processRouteRemovedInternal(event.subject());
    }

    private void processRouteRemovedInternal(ResolvedRoute route) {
        IpPrefix prefix = route.prefix();
        MacAddress nextHopMac = route.nextHopMac();
        // TODO ResolvedRoute does not contain VLAN information.
        //      Therefore we only support untagged nexthop for now.
        VlanId nextHopVlan = VlanId.NONE;
        ConnectPoint location = route.location();

        srManager.deviceConfiguration.removeSubnet(location, prefix);
        srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(prefix));
        srManager.routingRulePopulator.revokeRoute(
                location.deviceId(), prefix, nextHopMac, nextHopVlan, location.port());
    }
}

@@ -1458,23 +1458,26 @@
 vice.get
-NextHop
+RouteTable
 s().forE
@@ -1484,91 +1484,26 @@
 ach(
-nextHop -%3E %7B%0A            if (nextHop.location().deviceId().equals(deviceId)) %7B%0A    
+routeTableId -%3E %7B%0A
     
@@ -1546,31 +1546,21 @@
 utes
-ForNextHop(nextHop.ip()
+(routeTableId
 ).fo
@@ -1570,16 +1570,20 @@
 ch(route
+Info
  -%3E %7B%0A  
@@ -1600,13 +1600,72 @@
     
-    R
+routeInfo.allRoutes().stream()%0A                        .filter(r
 esol
@@ -1672,16 +1672,19 @@
 vedRoute
+ -%3E
  resolve
@@ -1689,18 +1689,38 @@
 vedRoute
- =
+.location() != null &&
 %0A       
@@ -1744,13 +1744,13 @@
     
-new R
+    r
 esol
@@ -1761,51 +1761,48 @@
 oute
-(route, nextHop.mac(), nextHop.location(
+.location().deviceId().equals(deviceId
 ))
-;
 %0A   
@@ -1810,32 +1810,51 @@
                 
+    .forEach(this::
 processRouteAdde
@@ -1866,29 +1866,11 @@
 rnal
-(resolvedRoute);%0A    
+);%0A
     
@@ -1885,22 +1885,8 @@
 %7D);%0A
-            %7D%0A
     
@@ -2237,173 +2237,48 @@
     
-// TODO ResolvedRoute does not contain VLAN information.%0A        //      Therefore we only support untagged nexthop for now.%0A        VlanId nextHopVlan = VlanId.NONE
+VlanId nextHopVlan = route.nextHopVlan()
 ;%0A  
@@ -3204,173 +3204,48 @@
     
-// TODO ResolvedRoute does not contain VLAN information.%0A        //      Therefore we only support untagged nexthop for now.%0A        VlanId nextHopVlan = VlanId.NONE
+VlanId nextHopVlan = route.nextHopVlan()
 ;%0A  

Support tagged next hop in Segment Routing