/*
 * Copyright 2014 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.net.intent.impl.installer;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Link;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleOperation;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.IntentInstaller;
import org.onosproject.net.intent.OpticalPathIntent;
import org.onosproject.net.resource.DefaultLinkResourceRequest;
import org.onosproject.net.resource.Lambda;
import org.onosproject.net.resource.LambdaResourceAllocation;
import org.onosproject.net.resource.LinkResourceAllocations;
import org.onosproject.net.resource.LinkResourceRequest;
import org.onosproject.net.resource.LinkResourceService;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceType;
import org.onosproject.net.topology.TopologyService;
import org.slf4j.Logger;

import java.util.Collection;
import java.util.List;

import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * Installer for {@link org.onosproject.net.intent.OpticalPathIntent optical path connectivity intents}.
 */
@Component(immediate = true)
public class OpticalPathIntentInstaller implements IntentInstaller<OpticalPathIntent> {
    private final Logger log = getLogger(getClass());

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected IntentExtensionService intentManager;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected FlowRuleService flowRuleService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected CoreService coreService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected TopologyService topologyService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected LinkResourceService resourceService;

    private ApplicationId appId;

    static final short SIGNAL_TYPE = (short) 1;

    @Activate
    public void activate() {
        appId = coreService.registerApplication("org.onosproject.net.intent");
        intentManager.registerInstaller(OpticalPathIntent.class, this);
    }

    @Deactivate
    public void deactivate() {
        intentManager.unregisterInstaller(OpticalPathIntent.class);
    }

    @Override
    public List<Collection<FlowRuleOperation>> install(OpticalPathIntent intent) {
        LinkResourceAllocations allocations = assignWavelength(intent);
        return generateRules(intent, allocations, FlowRuleOperation.Type.ADD);
    }

    @Override
    public List<Collection<FlowRuleOperation>> uninstall(OpticalPathIntent intent) {
        LinkResourceAllocations allocations = resourceService.getAllocations(intent.id());
        List<Collection<FlowRuleOperation>> rules = generateRules(intent, allocations, FlowRuleOperation.Type.REMOVE);
        log.info("uninstall rules: {}", rules);
        return rules;
    }

    @Override
    public List<Collection<FlowRuleOperation>> replace(OpticalPathIntent oldIntent,
                                                OpticalPathIntent newIntent) {
        // FIXME: implement this
        List<Collection<FlowRuleOperation>> batches = Lists.newArrayList();
        batches.addAll(uninstall(oldIntent));
        batches.addAll(install(newIntent));
        return batches;
    }

    private LinkResourceAllocations assignWavelength(OpticalPathIntent intent) {
        LinkResourceRequest.Builder request = DefaultLinkResourceRequest.builder(intent.id(),
                                                                                 intent.path().links())
                .addLambdaRequest();
        LinkResourceAllocations retLambda = resourceService.requestResources(request.build());
        return retLambda;
    }

    private List<Collection<FlowRuleOperation>> generateRules(OpticalPathIntent intent,
                                                        LinkResourceAllocations allocations,
                                                        FlowRuleOperation.Type operation) {
        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
        selectorBuilder.matchInPort(intent.src().port());

        List<FlowRuleOperation> rules = Lists.newLinkedList();
        ConnectPoint prev = intent.src();

        //FIXME check for null allocations
        //TODO throw exception if the lambda was not assigned successfully
        for (Link link : intent.path().links()) {
            Lambda la = null;
            for (ResourceAllocation allocation : allocations.getResourceAllocation(link)) {
                if (allocation.type() == ResourceType.LAMBDA) {
                    la = ((LambdaResourceAllocation) allocation).lambda();
                    break;
                }
            }

            if (la == null) {
                log.info("Lambda was not assigned successfully");
                return null;
            }

            TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
            treatmentBuilder.setOutput(link.src().port());
            treatmentBuilder.setLambda((short) la.toInt());

            FlowRule rule = new DefaultFlowRule(prev.deviceId(),
                                                selectorBuilder.build(),
                                                treatmentBuilder.build(),
                                                100,
                                                appId,
                                                100,
                                                true);

            rules.add(new FlowRuleOperation(rule, operation));

            prev = link.dst();
            selectorBuilder.matchInPort(link.dst().port());
            selectorBuilder.matchOpticalSignalType(SIGNAL_TYPE); //todo
            selectorBuilder.matchLambda((short) la.toInt());

        }

        // build the last T port rule
        TrafficTreatment.Builder treatmentLast = builder();
        treatmentLast.setOutput(intent.dst().port());
        FlowRule rule = new DefaultFlowRule(intent.dst().deviceId(),
                                            selectorBuilder.build(),
                                            treatmentLast.build(),
                                            100,
                                            appId,
                                            100,
                                            true);
        rules.add(new FlowRuleOperation(rule, operation));

        //FIXME change to new api
        return Lists.newArrayList(ImmutableSet.of(rules));
    }
}

@@ -6473,29 +6473,30 @@
 .set
-Output(link.src().por
+Lambda((short) la.toIn
 t())
@@ -6529,38 +6529,37 @@
 lder.set
-Lambda((short) la.toIn
+Output(link.src().por
 t());%0A%0A 

Set output as last action