/*
 * 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.brooklyn.core.mgmt.internal;

import static org.apache.brooklyn.util.JavaGroovyEquivalents.mapOf;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.Group;
import org.apache.brooklyn.api.mgmt.SubscriptionContext;
import org.apache.brooklyn.api.mgmt.SubscriptionHandle;
import org.apache.brooklyn.api.mgmt.SubscriptionManager;
import org.apache.brooklyn.api.sensor.Sensor;
import org.apache.brooklyn.api.sensor.SensorEvent;
import org.apache.brooklyn.api.sensor.SensorEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

import groovy.lang.Closure;

/**
 * A {@link SubscriptionContext} for an entity or other user of a {@link SubscriptionManager}.
 */
public class BasicSubscriptionContext implements SubscriptionContext {
    
    private static final Logger LOG = LoggerFactory.getLogger(BasicSubscriptionContext.class);

    private final SubscriptionManager manager;
    private final Object subscriber;
    private final Map<String,Object> flags;

    public BasicSubscriptionContext(SubscriptionManager manager, Object subscriber) {
        this(ImmutableMap.<String, Object>of(), manager, subscriber);
    }
    
    public BasicSubscriptionContext(Map<String, ?> flags, SubscriptionManager manager, Object subscriber) {
        this.manager = manager;
        this.subscriber = subscriber;
        this.flags = mapOf("subscriber", subscriber);
        if (flags!=null) this.flags.putAll(flags);
    }

    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    @SuppressWarnings("rawtypes")
    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, Closure c) {
        LOG.warn("Use of groovy.lang.Closure is deprecated in BasicSubscriptionContext.subscribe()");
        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, c);
    }
    
    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    @SuppressWarnings("rawtypes")
    public <T> SubscriptionHandle subscribe(Map<String, ?> newFlags, Entity producer, Sensor<T> sensor, Closure c) {
        LOG.warn("Use of groovy.lang.Closure is deprecated in BasicSubscriptionContext.subscribe()");
        return subscribe(newFlags, producer, sensor, toSensorEventListener(c));       
    }

    @Override
    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, listener);
    }
    
    @Override
    public <T> SubscriptionHandle subscribe(Map<String, ?> newFlags, Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
        if (newFlags != null) subscriptionFlags.putAll(newFlags);
        return manager.subscribe(subscriptionFlags, producer, sensor, listener);
    }

    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    @SuppressWarnings("rawtypes")
    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, Closure c) {
        LOG.warn("Use of groovy.lang.Closure is deprecated in BasicSubscriptionContext.subscribeToChildren()");
        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, c);
    }
    
    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    @SuppressWarnings("rawtypes")
    public <T> SubscriptionHandle subscribeToChildren(Map<String, Object> newFlags, Entity parent, Sensor<T> sensor, Closure c) {
        LOG.warn("Use of groovy.lang.Closure is deprecated in BasicSubscriptionContext.subscribeToChildren()");
        return subscribeToChildren(newFlags, parent, sensor, toSensorEventListener(c));
    }

    @Override
    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, listener);
    }
    
    @Override
    public <T> SubscriptionHandle subscribeToChildren(Map<String, Object> newFlags, Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
        if (newFlags != null) subscriptionFlags.putAll(newFlags);
        return manager.subscribeToChildren(subscriptionFlags, parent, sensor, listener);
    }

    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    @SuppressWarnings("rawtypes")
    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, Closure c) {
        LOG.warn("Use of groovy.lang.Closure is deprecated in BasicSubscriptionContext.subscribeToMembers()");
        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, c);
    }

    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    @SuppressWarnings("rawtypes")
    public <T> SubscriptionHandle subscribeToMembers(Map<String, Object> newFlags, Group parent, Sensor<T> sensor, Closure c) {
        LOG.warn("Use of groovy.lang.Closure is deprecated in BasicSubscriptionContext.subscribeToMembers()");
        return subscribeToMembers(newFlags, parent, sensor, toSensorEventListener(c));
    }
    
    @Override
    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, listener);
    }
    
    @Override
    public <T> SubscriptionHandle subscribeToMembers(Map<String, Object> newFlags, Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
        if (newFlags != null) subscriptionFlags.putAll(newFlags);
        return manager.subscribeToMembers(subscriptionFlags, parent, sensor, listener);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public boolean unsubscribe(SubscriptionHandle subscriptionId) {
        Preconditions.checkNotNull(subscriptionId, "subscriptionId must not be null");
        Preconditions.checkArgument(Objects.equal(subscriber, ((Subscription) subscriptionId).subscriber), "The subscriptionId is for a different "+subscriber+"; expected "+((Subscription) subscriptionId).subscriber);
        return manager.unsubscribe(subscriptionId);
    }

    /** @see SubscriptionManager#publish(SensorEvent) */
    @Override
    public <T> void publish(SensorEvent<T> event) {
        manager.publish(event);
    }

    /** Return the subscriptions associated with this context */
    @Override
    public Set<SubscriptionHandle> getSubscriptions() {
        return manager.getSubscriptionsForSubscriber(subscriber);
    }

    @Override
    public int unsubscribeAll() {
        int count = 0;
        
        // To avoid ConcurrentModificationException when copying subscriptions, need to synchronize on it
        Set<SubscriptionHandle> subscriptions = getSubscriptions();
        Collection<SubscriptionHandle> subscriptionsCopy;
        synchronized (subscriptions) {
            subscriptionsCopy = ImmutableList.copyOf(subscriptions);
        }
        
        for (SubscriptionHandle s : subscriptionsCopy) {
            count++; 
            boolean result = unsubscribe(s); 
            if (!result) LOG.warn("When unsubscribing from all of {}, unsubscribe of {} returned false", subscriber, s);
        }
        return count;
    }
    
    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    @SuppressWarnings("rawtypes")
    private <T> SensorEventListener<T> toSensorEventListener(final Closure c) {
        return new SensorEventListener<T>() {
            @Override public void onEvent(SensorEvent<T> event) {
                c.call(event);
            }
        };
    }
}
