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

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentInstallationContext;
import org.onosproject.net.intent.IntentOperationContext;
import org.onosproject.net.intent.IntentInstaller;
import org.onosproject.net.intent.IntentStore;
import org.slf4j.Logger;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import static org.onosproject.net.intent.IntentState.*;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * Implementation of IntentInstallCoordinator.
 */
public class InstallCoordinator {
    private static final String INSTALLER_NOT_FOUND = "Intent installer not found, Intent: {}";
    private final Logger log = getLogger(IntentManager.class);

    private InstallerRegistry installerRegistry;
    private IntentStore intentStore;

    /**
     * Creates an InstallCoordinator.
     *
     * @param installerRegistry the installer registry
     * @param intentStore the Intent store
     */
    public InstallCoordinator(InstallerRegistry installerRegistry,
                              IntentStore intentStore) {
        this.installerRegistry = installerRegistry;
        this.intentStore = intentStore;
    }

    /**
     * Applies Intent data to be uninstalled and to be installed.
     *
     * @param toUninstall Intent data to be uninstalled
     * @param toInstall Intent data to be installed
     */
    public void installIntents(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
        // If no any Intents to be uninstalled or installed, ignore it.
        if (!toUninstall.isPresent() && !toInstall.isPresent()) {
            return;
        }

        // Classify installable Intents to different installers.
        ArrayListMultimap<IntentInstaller, Intent> uninstallInstallers;
        ArrayListMultimap<IntentInstaller, Intent> installInstallers;
        Set<IntentInstaller> allInstallers = Sets.newHashSet();

        if (toUninstall.isPresent()) {
            uninstallInstallers = getInstallers(toUninstall.get());
            allInstallers.addAll(uninstallInstallers.keySet());
        } else {
            uninstallInstallers = ArrayListMultimap.create();
        }

        if (toInstall.isPresent()) {
            installInstallers = getInstallers(toInstall.get());
            allInstallers.addAll(installInstallers.keySet());
        } else {
            installInstallers = ArrayListMultimap.create();
        }

        // Generates an installation context for the high level Intent.
        IntentInstallationContext installationContext =
                new IntentInstallationContext(toUninstall.orElse(null), toInstall.orElse(null));

        //Generates different operation context for different installable Intents.
        Map<IntentInstaller, IntentOperationContext> contexts = Maps.newHashMap();
        allInstallers.forEach(installer -> {
            List<Intent> intentsToUninstall = uninstallInstallers.get(installer);
            List<Intent> intentsToInstall = installInstallers.get(installer);

            // Connect context to high level installation context
            IntentOperationContext context =
                    new IntentOperationContext(intentsToUninstall, intentsToInstall,
                                               installationContext);
            installationContext.addPendingContext(context);
            contexts.put(installer, context);
        });

        // Apply contexts to installers
        contexts.forEach((installer, context) -> {
            installer.apply(context);
        });
    }

    /**
     * Generates a mapping for installable Intents to installers.
     *
     * @param intentData the Intent data which contains installable Intents
     * @return the mapping for installable Intents to installers
     */
    private ArrayListMultimap<IntentInstaller, Intent> getInstallers(IntentData intentData) {
        ArrayListMultimap<IntentInstaller, Intent> intentInstallers = ArrayListMultimap.create();
        intentData.installables().forEach(intent -> {
            IntentInstaller installer = installerRegistry.getInstaller(intent.getClass());
            if (installer != null) {
                intentInstallers.put(installer, intent);
            } else {
                log.warn(INSTALLER_NOT_FOUND, intent);
            }
        });
        return intentInstallers;
    }

    /**
     * Handles success operation context.
     *
     * @param context the operation context
     */
    public void success(IntentOperationContext context) {
        IntentInstallationContext intentInstallationContext =
                context.intentInstallationContext();
        intentInstallationContext.removePendingContext(context);

        if (intentInstallationContext.isPendingContextsEmpty()) {
            finish(intentInstallationContext);
        }
    }

    /**
     * Handles failed operation context.
     *
     * @param context the operation context
     */
    public void failed(IntentOperationContext context) {
        IntentInstallationContext intentInstallationContext =
                context.intentInstallationContext();
        intentInstallationContext.addErrorContext(context);
        intentInstallationContext.removePendingContext(context);

        if (intentInstallationContext.isPendingContextsEmpty()) {
            finish(intentInstallationContext);
        }
    }

    /**
     * Completed the installation context and update the Intent store.
     *
     * @param intentInstallationContext the installation context
     */
    private void finish(IntentInstallationContext intentInstallationContext) {
        Set<IntentOperationContext> errCtxs = intentInstallationContext.errorContexts();
        Optional<IntentData> toUninstall = intentInstallationContext.toUninstall();
        Optional<IntentData> toInstall = intentInstallationContext.toInstall();

        // Intent install success
        if (errCtxs == null || errCtxs.isEmpty()) {
            if (toInstall.isPresent()) {
                IntentData installData = toInstall.get();
                log.debug("Completed installing: {}", installData.key());
                installData = new IntentData(installData, installData.installables());
                installData.setState(INSTALLED);
                intentStore.write(installData);
            } else if (toUninstall.isPresent()) {
                IntentData uninstallData = toUninstall.get();
                uninstallData = new IntentData(uninstallData, Collections.emptyList());
                log.debug("Completed withdrawing: {}", uninstallData.key());
                switch (uninstallData.request()) {
                    case INSTALL_REQ:
                        log.warn("{} was requested to withdraw during installation?",
                                 uninstallData.intent());
                        uninstallData.setState(FAILED);
                        break;
                    case WITHDRAW_REQ:
                    default: //TODO "default" case should not happen
                        uninstallData.setState(WITHDRAWN);
                        break;
                }
                // Intent has been withdrawn; we can clear the installables
                intentStore.write(uninstallData);
            }
        } else {
            // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
            if (toInstall.isPresent()) {
                IntentData installData = toInstall.get();
                installData.setState(CORRUPT);
                installData.incrementErrorCount();
                intentStore.write(installData);
            }
            // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
            if (toUninstall.isPresent()) {
                IntentData uninstallData = toUninstall.get();
                uninstallData.setState(CORRUPT);
                uninstallData.incrementErrorCount();
                intentStore.write(uninstallData);
            }
        }
    }
}

@@ -1579,18 +1579,23 @@
 r(In
-tentManage
+stallCoordinato
 r.cl

Fix wrong logger