/*
 * Copyright 2015 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.vtn.table.impl;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;

import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.flowobjective.Objective.Operation;
import org.onosproject.vtn.table.ClassifierService;
import org.onosproject.vtnrsc.SegmentationId;
import org.slf4j.Logger;

import com.google.common.collect.Sets;

/**
 * Provides implementation of ClassifierService.
 */
public class ClassifierServiceImpl implements ClassifierService {
    private final Logger log = getLogger(getClass());

    private static final int L2_CLAFFIFIER_PRIORITY = 50000;

    private final FlowObjectiveService flowObjectiveService;
    private final ApplicationId appId;

    /**
     * Constructor.
     *
     * @param appId the application id of vtn
     */
    public ClassifierServiceImpl(ApplicationId appId) {
        this.appId = checkNotNull(appId, "ApplicationId can not be null");
        ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
        this.flowObjectiveService = serviceDirectory.get(FlowObjectiveService.class);
    }

    @Override
    public void programLocalIn(DeviceId deviceId,
                               SegmentationId segmentationId, PortNumber inPort,
                               MacAddress srcMac, ApplicationId appid,
                               Objective.Operation type) {
        TrafficSelector selector = DefaultTrafficSelector.builder()
                .matchInPort(inPort).matchEthSrc(srcMac).build();
        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
        treatment.add(Instructions
                .modTunnelId(Long.parseLong(segmentationId.toString())));
        ForwardingObjective.Builder objective = DefaultForwardingObjective
                .builder().withTreatment(treatment.build())
                .withSelector(selector).fromApp(appId).makePermanent()
                .withFlag(Flag.SPECIFIC).withPriority(L2_CLAFFIFIER_PRIORITY);
        if (type.equals(Objective.Operation.ADD)) {
            log.debug("programLocalIn-->ADD");
            flowObjectiveService.forward(deviceId, objective.add());
        } else {
            log.debug("programLocalIn-->REMOVE");
            flowObjectiveService.forward(deviceId, objective.remove());
        }
    }

    @Override
    public void programTunnelIn(DeviceId deviceId,
                                SegmentationId segmentationId,
                                Iterable<PortNumber> localTunnelPorts,
                                Objective.Operation type) {
        if (localTunnelPorts == null) {
            log.info("No tunnel port in device");
            return;
        }
        Sets.newHashSet(localTunnelPorts).stream().forEach(tp -> {
            TrafficSelector selector = DefaultTrafficSelector.builder()
                    .matchInPort(tp).add(Criteria.matchTunnelId(Long
                            .parseLong(segmentationId.toString())))
                    .build();

            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                    .build();
            ForwardingObjective.Builder objective = DefaultForwardingObjective
                    .builder().withTreatment(treatment).withSelector(selector)
                    .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC)
                    .withPriority(L2_CLAFFIFIER_PRIORITY);
            if (type.equals(Objective.Operation.ADD)) {
                log.debug("programTunnelIn-->ADD");
                flowObjectiveService.forward(deviceId, objective.add());
            } else {
                log.debug("programTunnelIn-->REMOVE");
                flowObjectiveService.forward(deviceId, objective.remove());
            }
        });
    }

    @Override
    public void programL3ExPortClassifierRules(DeviceId deviceId,
                                               PortNumber exPort,
                                               IpAddress fIp, Operation type) {
        // TODO Auto-generated method stub
    }

    @Override
    public void programL3InPortClassifierRules(DeviceId deviceId,
                                               PortNumber inPort,
                                               MacAddress srcMac,
                                               MacAddress dstVmGwMac,
                                               SegmentationId l3Vni,
                                               Operation type) {
        // TODO Auto-generated method stub
    }

    @Override
    public void programArpClassifierRules(DeviceId deviceId, IpAddress srcGwIp,
                                          SegmentationId srcVni, Operation type) {
        // TODO Auto-generated method stub
    }

}

@@ -870,25 +870,93 @@
 .packet.
-IpAddress
+Ethernet;%0Aimport org.onlab.packet.IpAddress;%0Aimport org.onlab.packet.IpPrefix
 ;%0Aimport
@@ -2139,24 +2139,86 @@
 tClass());%0A%0A
+    private static final int L3_CLAFFIFIER_PRIORITY = 0xffff;%0A
     private 
@@ -5484,32 +5484,51 @@
 viceId deviceId,
+ PortNumber inPort,
 %0A               
@@ -5563,149 +5563,979 @@
     
-PortNumber exPort,%0A                                               IpAddress fIp, Operation type) %7B%0A        // TODO Auto-generated method stub
+IpAddress dstIp,%0A                                               Objective.Operation type) %7B%0A        TrafficSelector selector = DefaultTrafficSelector.builder()%0A                .matchEthType(Ethernet.TYPE_IPV4).matchInPort(inPort)%0A                .matchIPDst(IpPrefix.valueOf(dstIp, 32)).build();%0A        TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();%0A        ForwardingObjective.Builder objective = DefaultForwardingObjective%0A                .builder().withTreatment(treatment).withSelector(selector)%0A                .fromApp(appId).withFlag(Flag.SPECIFIC)%0A                .withPriority(L3_CLAFFIFIER_PRIORITY);%0A        if (type.equals(Objective.Operation.ADD)) %7B%0A            log.debug(%22L3ExToInClassifierRules--%3EADD%22);%0A            flowObjectiveService.forward(deviceId, objective.add());%0A        %7D else %7B%0A            log.debug(%22L3ExToInClassifierRules--%3EREMOVE%22);%0A            flowObjectiveService.forward(deviceId, objective.remove());%0A        %7D
 %0A   

Add the implementation of method programL3ExPortClassifierRules which assemble the L3 Classifier table rules sended from external port.