package brooklyn.entity.nosql.mongodb;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

import brooklyn.entity.basic.EntityLocal;
import brooklyn.entity.nosql.mongodb.sharding.MongoDBRouter;
import brooklyn.entity.nosql.mongodb.sharding.MongoDBRouterCluster;
import brooklyn.entity.nosql.mongodb.sharding.MongoDBShardedDeployment;
import brooklyn.entity.trait.Startable;
import brooklyn.event.basic.DependentConfiguration;
import brooklyn.location.basic.SshMachineLocation;
import brooklyn.management.Task;
import brooklyn.util.exceptions.Exceptions;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

public class MongoDBClientSshDriver extends AbstractMongoDBSshDriver implements MongoDBClientDriver {
    
    private static final Logger LOG = LoggerFactory.getLogger(MongoDBClientSshDriver.class);

    private boolean isRunning = false;

    public MongoDBClientSshDriver(EntityLocal entity, SshMachineLocation machine) {
        super(entity, machine);
    }
    
    @Override
    public void customize() {
        String command = String.format("mkdir -p %s", getUserScriptDir());
        newScript(CUSTOMIZING)
            .updateTaskAndFailOnNonZeroResultCode()
            .body.append(command).execute();
        Map<String, String> scripts = entity.getConfig(MongoDBClient.JS_SCRIPTS);
        for (String scriptName : scripts.keySet()) {
            copyResource(scripts.get(scriptName), getUserScriptDir() + scriptName + ".js");
        }
    }

    @Override
    public void launch() {
        AbstractMongoDBServer server = getServer();
        String host = server.getAttribute(AbstractMongoDBServer.HOSTNAME);
        Integer port = server.getAttribute(AbstractMongoDBServer.PORT);
        try {
            for (String scriptName : entity.getConfig(MongoDBClient.STARTUP_JS_SCRIPTS)) {
                runScript("", scriptName, host, port);
            }
        } catch (NullPointerException e) {
            // FIXME avoid the null ptr, and do something more intelligent
            LOG.error("startupScripts not specified in MongoDBClientSshDriver launch method;", e);
            isRunning = false;
            return;
        }
        isRunning = true;
    }
    
    @Override
    public boolean isRunning() {
        return isRunning;
    }
    
    private String getUserScriptDir() {
        return getRunDir() + "/userScripts/" ;
    }
    
    public void runScript(String preStart, String scriptName) {
        AbstractMongoDBServer server = getServer();
        String host = server.getAttribute(AbstractMongoDBServer.HOSTNAME);
        Integer port = server.getAttribute(AbstractMongoDBServer.PORT);
        runScript(preStart, scriptName, host, port);
    }
    
    private void runScript(String preStart, String scriptName, String host, Integer port) {
        // TODO: escape preStart to prevent injection attack
        String command = String.format("%s/bin/mongo %s:%s --eval \"%s\" %s/%s > out.log 2> err.log < /dev/null", getExpandedInstallDir(), 
                host, port, preStart, getUserScriptDir(), scriptName + ".js");
        newScript(LAUNCHING)
            .updateTaskAndFailOnNonZeroResultCode()
            .body.append(command).execute();
    }
    
    private AbstractMongoDBServer getServer() {
        AbstractMongoDBServer server = entity.getConfig(MongoDBClient.SERVER);
        MongoDBShardedDeployment deployment = entity.getConfig(MongoDBClient.SHARDED_DEPLOYMENT);
        if (server == null) {
            Preconditions.checkNotNull(deployment, "Either server or shardedDeployment must be specified");
            Task<MongoDBRouter> task = DependentConfiguration.attributeWhenReady(deployment.getRouterCluster(),
                    MongoDBRouterCluster.ANY_ROUTER);
            try {
                server = DependentConfiguration.waitForTask(task, entity, "any available router");
            } catch (InterruptedException e) {
                throw Exceptions.propagate(e);
            }
            DependentConfiguration.waitInTaskForAttributeReady(server, MongoDBRouter.SHARD_COUNT, new Predicate<Integer>() {
                public boolean apply(Integer input) {
                    return input > 0;
                };
            });
        } else {
            if (deployment != null) {
                log.warn("Server and ShardedDeployment defined for {}; using server ({} instead of {})", 
                        new Object[] {this, server, deployment});
            }
            Task<Boolean> task = DependentConfiguration.attributeWhenReady(server, Startable.SERVICE_UP);
            try {
                DependentConfiguration.waitForTask(task, server);
            } catch (InterruptedException e) {
                throw Exceptions.propagate(e);
            }
            DependentConfiguration.waitInTaskForAttributeReady(server, Startable.SERVICE_UP, Predicates.equalTo(true));
        }
        return server;
    }
}

@@ -33,16 +33,39 @@
 ngodb;%0A%0A
+import java.util.List;%0A
 import j
@@ -141,137 +141,8 @@
 y;%0A%0A
-import com.google.common.base.Preconditions;%0Aimport com.google.common.base.Predicate;%0Aimport com.google.common.base.Predicates;%0A%0A
 impo
@@ -1829,37 +1829,32 @@
 .PORT);%0A        
-try %7B
 %0A            for
@@ -1850,36 +1850,30 @@
     
-    for (
+List%3C
 String
+%3E
  script
-Name :
+s =
  ent
@@ -1919,20 +1919,209 @@
 SCRIPTS)
-) %7B%0A
+;%0A        if (scripts!=null) %7B%0A            for (String scriptName : scripts) %7B%0A                try %7B%0A                    LOG.debug(%22Running MongoDB script %22+scriptName+%22 at %22+getEntity());%0A    
         
@@ -2178,27 +2178,21 @@
         
- %7D%0A
      
-   
 %7D catch 
@@ -2196,19 +2196,8 @@
 ch (
-NullPointer
 Exce
@@ -2223,133 +2223,271 @@
     
-// FIXME avoid the null ptr, and do something more intelligent%0A            LOG.error(%22startupScripts not specified 
+        LOG.warn(%22Error running MongoDB script %22+scriptName+%22 at %22+getEntity()+%22, throwing: %22+e);%0A                    isRunning = false;%0A                    Exceptions.propagateIfFatal(e);%0A                    throw new IllegalStateException(%22Error runn
 in
+g
  MongoDB
 Clie
@@ -2486,75 +2486,72 @@
 goDB
-ClientSshDriver launch method;%22, e);%0A            isRunning = false;
+ script %22+scriptName+%22 at %22+entity+%22: %22+e, e);%0A                %7D
 %0A   
@@ -2563,15 +2563,9 @@
     
-return;
+%7D
 %0A   
@@ -2667,25 +2667,233 @@
     
-return isRunning;
+// TODO better would be to get some confirmation%0A        return isRunning;%0A    %7D%0A    %0A    @Override%0A    public void stop() %7B%0A        try %7B%0A            super.stop();%0A        %7D finally %7B%0A            isRunning = false;%0A        %7D
 %0A   

tidy of MongoDB client NPE check (as per discussion in #1327), and clearing isRunning on stop