/*
 * 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.camp.brooklyn;

import org.apache.brooklyn.api.effector.Effector;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.core.effector.Effectors;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityAsserts;
import org.apache.brooklyn.core.entity.EntityPredicates;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.net.Networking;
import org.apache.brooklyn.util.text.StringPredicates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;

@Test
public class VanillaBashNetcatYamlTest extends AbstractYamlTest {

    private static final Logger log = LoggerFactory.getLogger(VanillaBashNetcatYamlTest.class);

    private static final AttributeSensor<String> SENSOR_OUTPUT_ALL = Sensors.newStringSensor("output.all");
    final static Effector<String> EFFECTOR_SAY_HI = Effectors.effector(String.class, "sayHiNetcat").buildAbstract();
    
    // OSX specific test because of netcat arguments
    @Test(groups={"Integration","Broken"})
    public void testInvocationSensorAndEnricher() throws Exception {
        Preconditions.checkArgument(Networking.isPortAvailable(4321), "port 4321 must not be in use (no leaked nc instances) for this test to succeed!");
        
        Entity app = createAndStartApplication(loadYaml("vanilla-bash-netcat-w-client.yaml"));
        waitForApplicationTasks(app);
        
        log.info("App started:");
        Entities.dumpInfo(app);
        
        Assert.assertEquals(app.getDisplayName(), "Simple Netcat with Client");
        
        // comparing by plan ID is one common way
        Iterable<Entity> netcatI = Iterables.filter(app.getChildren(), EntityPredicates.configEqualTo(BrooklynCampConstants.PLAN_ID, "netcat-server"));
        Assert.assertTrue(netcatI.iterator().hasNext(), "no 'netcat-server' child of app: "+app.getChildren());
        Entity netcat = Iterables.getOnlyElement(netcatI);
        
        // make sure netcat is running
        EntityAsserts.assertAttributeEventually(netcat, Attributes.SERVICE_STATE_ACTUAL, Predicates.equalTo(Lifecycle.RUNNING));
        
        // find the pinger, now comparing by name
        Iterable<Entity> pingerI = Iterables.filter(app.getChildren(), EntityPredicates.displayNameEqualTo("Simple Pinger"));
        Assert.assertTrue(pingerI.iterator().hasNext(), "no 'Simple Pinger' child of app: "+app.getChildren());
        Entity pinger = Iterables.getOnlyElement(pingerI);

        // invoke effector
        Task<String> ping;
        ping = pinger.invoke(EFFECTOR_SAY_HI, MutableMap.<String,Object>of());
        Assert.assertEquals(ping.get().trim(), "hello");
        // and check we get the right result 
        EntityAsserts.assertAttributeEventually(netcat, SENSOR_OUTPUT_ALL, StringPredicates.containsLiteral("hi netcat"));
        log.info("invoked ping from "+pinger+" to "+netcat+", 'all' sensor shows:\n"+
                netcat.getAttribute(SENSOR_OUTPUT_ALL));

        // netcat should now fail and restart
        EntityAsserts.assertAttributeEventually(netcat, Attributes.SERVICE_STATE_ACTUAL, Predicates.not(Predicates.equalTo(Lifecycle.RUNNING)));
        log.info("detected failure, state is: "+netcat.getAttribute(Attributes.SERVICE_STATE_ACTUAL));
        EntityAsserts.assertAttributeEventually(netcat, Attributes.SERVICE_STATE_ACTUAL, Predicates.equalTo(Lifecycle.RUNNING));
        log.info("detected recovery, state is: "+netcat.getAttribute(Attributes.SERVICE_STATE_ACTUAL));

        // invoke effector again, now with a parameter
        ping = pinger.invoke(EFFECTOR_SAY_HI, MutableMap.<String,Object>of("message", "yo yo yo"));
        Assert.assertEquals(ping.get().trim(), "hello");
        // checking right result
        EntityAsserts.assertAttributeEventually(netcat, SENSOR_OUTPUT_ALL, StringPredicates.containsLiteral("yo yo yo"));
        log.info("invoked ping again from "+pinger+" to "+netcat+", 'all' sensor shows:\n"+
                netcat.getAttribute(SENSOR_OUTPUT_ALL));
        
        // and it's propagated to the app
        EntityAsserts.assertAttributeEventually(app, Sensors.newStringSensor("output.last"), StringPredicates.containsLiteral("yo yo yo"));
        
        log.info("after all is said and done, app is:");
        Entities.dumpInfo(app);
    }
    
    @Override
    protected Logger getLogger() {
        return log;
    }
    
}
