/*
 *     Licensed to the Apache Software Foundation (ASF) under one
 *     or more contributor license agreements.  See the NOTICE file
 *     distributed with this work for additional information
 *     regarding copyright ownership.  The ASF licenses this file
 *     to you 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.apache.stratos.autoscaler.monitor.application;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.stratos.autoscaler.exception.DependencyBuilderException;
import org.apache.stratos.autoscaler.exception.TopologyInConsistentException;
import org.apache.stratos.autoscaler.monitor.AbstractClusterMonitor;
import org.apache.stratos.autoscaler.monitor.Monitor;
import org.apache.stratos.autoscaler.monitor.MonitorStatusEventBuilder;
import org.apache.stratos.autoscaler.monitor.ParentComponentMonitor;
import org.apache.stratos.autoscaler.monitor.events.ApplicationStatusEvent;
import org.apache.stratos.autoscaler.monitor.events.MonitorScalingEvent;
import org.apache.stratos.autoscaler.monitor.events.MonitorStatusEvent;
import org.apache.stratos.autoscaler.monitor.events.MonitorTerminateAllEvent;
import org.apache.stratos.autoscaler.status.checker.StatusChecker;
import org.apache.stratos.messaging.domain.topology.Application;
import org.apache.stratos.messaging.domain.topology.ApplicationStatus;
import org.apache.stratos.messaging.domain.topology.ClusterStatus;
import org.apache.stratos.messaging.domain.topology.GroupStatus;
import org.apache.stratos.messaging.domain.topology.lifecycle.LifeCycleState;

import java.util.Collection;

/**
 * ApplicationMonitor is to control the child monitors
 */
public class ApplicationMonitor extends ParentComponentMonitor {
    private static final Log log = LogFactory.getLog(ApplicationMonitor.class);
    //status of the monitor whether it is running/in_maintainable/terminated
    protected ApplicationStatus status;

    public ApplicationMonitor(Application application) throws DependencyBuilderException,
            TopologyInConsistentException {
        super(application);
        //setting the appId for the application
        this.appId = application.getUniqueIdentifier();
        this.status = application.getStatus();
        //starting the first set of dependencies from its children
        startDependency();

    }

    /**
     * To find all the clusters of an application
     *
     * @param appId the application which contains the clusters
     * @return all the clusters of the application
     */
//    public List<String> findClustersOfApplication(String appId) {
//        List<String> clusters = new ArrayList<String>();
//        Set<ClusterDataHolder> clusterData;
//
//        TopologyManager.acquireReadLockForApplication(appId);
//        try {
//            clusterData = TopologyManager.getTopology().getApplication(appId).getClusterDataRecursively();
//
//        } finally {
//            TopologyManager.releaseReadLockForApplication(appId);
//        }
//
//        if (clusterData != null) {
//            for (ClusterDataHolder clusterDataHolder : clusterData) {
//                clusters.add(clusterDataHolder.getClusterId());
//            }
//        }
//
//        return clusters;
//    }

    /**
     * Find the cluster monitor by traversing recursively in the hierarchical monitors.
     *
     * @param clusterId cluster id of the monitor to be searched
     * @return the found cluster monitor
     */
    public AbstractClusterMonitor findClusterMonitorWithId(String clusterId) {
        /*return findClusterMonitor(clusterId, clusterIdToClusterMonitorsMap.values(),
                aliasToActiveMonitorsMap.values());*/
        return null;

    }


    /**
     * Find the group monitor by traversing recursively in the hierarchical monitors.
     *
     * @param groupId the unique alias of the Group
     * @return the found GroupMonitor
     */
    public Monitor findGroupMonitorWithId(String groupId) {
        Monitor monitor;
        monitor = findGroupMonitor(groupId, aliasToActiveMonitorsMap.values());
        if (monitor == null) {
            monitor = findGroupMonitor(groupId, aliasToInActiveMonitorsMap.values());
        }
        return monitor;
    }


    /**
     * Utility method to find the group monitor recursively within app monitor
     *
     * @param id       the unique alias of the Group
     * @param monitors the group monitors found in the app monitor
     * @return the found GroupMonitor
     */
    private Monitor findGroupMonitor(String id, Collection<Monitor> monitors) {
        for (Monitor monitor : monitors) {
            // check if alias is equal, if so, return
            if (monitor.getId().equals(id)) {
                return monitor;
            } else {
                // check if this Group has nested sub Groups. If so, traverse them as well
                if (monitor.getAliasToActiveMonitorsMap() != null) {
                    return findGroupMonitor(id, monitor.getAliasToActiveMonitorsMap().values());
                }
            }
        }
        return null;
    }

    /**
     * To set the status of the application monitor
     *
     * @param status the status
     */
    public void setStatus(ApplicationStatus status) {
        log.info(String.format("[ApplicationMonitor] %s " +
                "state changes from %s to %s", id, this.status, status));

        this.status = status;
        //notify the children about the state change
        MonitorStatusEventBuilder.notifyChildren(this.parent, new ApplicationStatusEvent(status, appId));
    }

    @Override
    public void onChildEvent(MonitorStatusEvent statusEvent) {
        String id = statusEvent.getId();
        LifeCycleState status1 = statusEvent.getStatus();
        //Events coming from parent are In_Active(in faulty detection), Scaling events, termination
        if (status1 == ClusterStatus.Active || status1 == GroupStatus.Active) {
            onChildActivatedEvent(id);

        } else if (status1 == ClusterStatus.Inactive || status1 == GroupStatus.Inactive) {
            onChildInActiveEvent(id);
            //TODO update the status of the Application as in_active when child becomes in_active

        /*} else if (status1 == ClusterStatus.Terminating || status1 == GroupStatus.Terminating) {
            onChildTerminatingEvent(id);
            StatusChecker.getInstance().onChildStatusChange(id, this.id, this.appId);*/

        } else if (status1 == ClusterStatus.Terminated || status1 == GroupStatus.Terminated) {
            //Check whether all dependent goes Terminated and then start them in parallel.
            this.aliasToInActiveMonitorsMap.remove(id);
            if (this.status != ApplicationStatus.Terminating) {
                onChildTerminatedEvent(id);
            } else {
                StatusChecker.getInstance().onChildStatusChange(id, this.id, this.appId);
                log.info("Executing the un-subscription request for the [monitor] " + id);
            }
        }
    }

    @Override
    public void onEvent(MonitorTerminateAllEvent terminateAllEvent) {

    }

    @Override
    public void onEvent(MonitorScalingEvent scalingEvent) {

    }
}

@@ -6094,15 +6094,8 @@
 this
-.parent
 , ne

fixing a NPE is ApplicationMonitor