/*
 * 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.feed.function;

import static com.google.common.base.Preconditions.checkNotNull;
import groovy.lang.Closure;

import java.util.concurrent.Callable;

import com.google.common.base.Supplier;

import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.core.feed.FeedConfig;
import org.apache.brooklyn.core.feed.PollConfig;
import org.apache.brooklyn.util.groovy.GroovyJavaMethods;
import org.apache.brooklyn.util.guava.Functionals;
import org.apache.brooklyn.util.javalang.JavaClassNames;

public class FunctionPollConfig<S, T> extends PollConfig<S, T, FunctionPollConfig<S, T>> {

    private Callable<?> callable;
    
    public static <T> FunctionPollConfig<?, T> forSensor(AttributeSensor<T> sensor) {
        return new FunctionPollConfig<Object, T>(sensor);
    }

    public static FunctionPollConfig<?, Void> forMultiple() {
        return new FunctionPollConfig<Object, Void>(PollConfig.NO_SENSOR);
    }
    
    public FunctionPollConfig(AttributeSensor<T> sensor) {
        super(sensor);
    }

    public FunctionPollConfig(FunctionPollConfig<S, T> other) {
        super(other);
        callable = other.callable;
    }
    
    public Callable<? extends Object> getCallable() {
        return callable;
    }
    
    /**
     * The {@link Callable} to be invoked on each poll.
     * <p>
     * Note this <em>must</em> use generics, otherwise the return type of subsequent chained
     * calls will (e.g. to {@link FeedConfig#onException(com.google.common.base.Function)} will
     * return the wrong type.
     */
    @SuppressWarnings("unchecked")
    public <newS> FunctionPollConfig<newS, T> callable(Callable<? extends newS> val) {
        this.callable = checkNotNull(val, "callable");
        return (FunctionPollConfig<newS, T>) this;
    }
    
    /**
     * Supplies the value to be returned by each poll.
     * <p>
     * Note this <em>must</em> use generics, otherwise the return type of subsequent chained
     * calls will (e.g. to {@link FeedConfig#onException(com.google.common.base.Function)} will
     * return the wrong type.
     */
    @SuppressWarnings("unchecked")
    public <newS> FunctionPollConfig<newS, T> supplier(final Supplier<? extends newS> val) {
        this.callable = Functionals.callable( checkNotNull(val, "supplier") );
        return (FunctionPollConfig<newS, T>) this;
    }
    
    /** @deprecated since 0.7.0, kept for legacy compatibility when deserializing */
    @Deprecated
    @SuppressWarnings({ "unchecked", "unused" })
    private <newS> FunctionPollConfig<newS, T> supplierLegacy(final Supplier<? extends newS> val) {
        checkNotNull(val, "supplier");
        this.callable = new Callable<newS>() {
            @Override
            public newS call() throws Exception {
                return val.get();
            }
        };
        return (FunctionPollConfig<newS, T>) this;
    }

    /**
     * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted.
     */
    @Deprecated
    public FunctionPollConfig<S, T> closure(Closure<?> val) {
        this.callable = GroovyJavaMethods.callableFromClosure(checkNotNull(val, "closure"));
        return this;
    }

    @Override protected String toStringBaseName() { return "fn"; }
    @Override protected String toStringPollSource() {
        if (callable==null) return null;
        String cs = callable.toString();
        if (!cs.contains( ""+Integer.toHexString(callable.hashCode()) )) {
            return cs;
        }
        // if hashcode is in callable it's probably a custom internal; return class name
        return JavaClassNames.simpleClassName(callable);
    }

}
