package org.sdnplatform.sync.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.sdnplatform.sync.IStoreClient;
import org.sdnplatform.sync.ISyncService;
import org.sdnplatform.sync.Versioned;
import org.sdnplatform.sync.ISyncService.Scope;
import org.sdnplatform.sync.error.SyncException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.debugcounter.IDebugCounterService;

/**
 * A floodlight module that will start up and start doing horrible,
 * horrible things to the sync service.
 * @author readams
 */
public class SyncTorture implements IFloodlightModule {
    protected static final Logger logger =
            LoggerFactory.getLogger(SyncTorture.class);
    
    ISyncService syncService;
    IDebugCounterService debugCounter;

    int numWorkers = 2;
    int keysPerWorker = 1024*1024;
    int iterations = 0;
    int delay = 0;

    @Override
    public Collection<Class<? extends IFloodlightService>>
            getModuleServices() {
        return null;
    }

    @Override
    public Map<Class<? extends IFloodlightService>, IFloodlightService>
            getServiceImpls() {
        return null;
    }

    @Override
    public Collection<Class<? extends IFloodlightService>>
            getModuleDependencies() {
        Collection<Class<? extends IFloodlightService>> l = 
                new ArrayList<Class<? extends IFloodlightService>>();
        l.add(ISyncService.class);
        l.add(IDebugCounterService.class);

        return l;
    }

    @Override
    public void init(FloodlightModuleContext context)
            throws FloodlightModuleException {
        syncService = context.getServiceImpl(ISyncService.class);
        debugCounter = context.getServiceImpl(IDebugCounterService.class);

        try {
            syncService.registerStore("torture", Scope.GLOBAL);
        } catch (SyncException e) {
            throw new FloodlightModuleException(e);
        }
        
        Map<String,String> config = context.getConfigParams(this);
        if (config.containsKey("numWorkers")) {
            numWorkers = Integer.parseInt(config.get("numWorkers"));
        }
        if (config.containsKey("keysPerWorker")) {
            keysPerWorker = Integer.parseInt(config.get("keysPerWorker"));
        }
        if (config.containsKey("iterations")) {
            iterations = Integer.parseInt(config.get("iterations"));
        }
        if (config.containsKey("delay")) {
            delay = Integer.parseInt(config.get("delay"));
        }
    }

    @Override
    public void startUp(FloodlightModuleContext context)
            throws FloodlightModuleException {
        try {
            final IStoreClient<String, TortureValue> storeClient = 
                    syncService.getStoreClient("torture", 
                                               String.class, 
                                               TortureValue.class);
            for (int i = 0; i < numWorkers; i++) {
                Thread thread = new Thread(new TortureWorker(storeClient, i),
                                           "Torture-" + i);
                thread.setPriority(Thread.MIN_PRIORITY);
                thread.start();
            }
        } catch (Exception e) {
            throw new FloodlightModuleException(e);
        }
    }

    protected static class TortureValue {
        private String string;
        private int integer;
        private boolean bool;

        public TortureValue() {
            super();
        }

        public TortureValue(String string, int integer, boolean bool) {
            super();
            this.string = string;
            this.integer = integer;
            this.bool = bool;
        }
        
        public String getString() {
            return string;
        }
        public void setString(String string) {
            this.string = string;
        }
        public int getInteger() {
            return integer;
        }
        public void setInteger(int integer) {
            this.integer = integer;
        }
        public boolean isBool() {
            return bool;
        }
        public void setBool(boolean bool) {
            this.bool = bool;
        }
    }
    
    protected class TortureWorker implements Runnable {
        final IStoreClient<String, TortureValue> storeClient;
        final int workerId;
        final List<TortureValue> values;

        public TortureWorker(IStoreClient<String, TortureValue> storeClient,
                             int workerId) {
            super();
            this.storeClient = storeClient;
            this.workerId = workerId;
            values = new ArrayList<TortureValue>();
            for (int i = 0; i < keysPerWorker; i++) {
                values.add(new TortureValue(workerId+":"+i, 0, true));
            }
        }

        @Override
        public void run() {
            if (delay > 0) {
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException e) { }
            }
            int i = 0;
            while (iterations == 0 || i++ < iterations) {
                long start = System.currentTimeMillis();
                try {
                    for (TortureValue v : values) {
                        Versioned<TortureValue> vv =
                                storeClient.get(v.getString());
                        v.setInteger(v.getInteger() + 1);
                        v.setBool(!v.isBool());
                        vv.setValue(v);
                        storeClient.put(v.getString(), vv);
                    }
                } catch (Exception e) {
                    logger.error("Error in worker: ", e);
                }
                long iterend = System.currentTimeMillis();
                debugCounter.flushCounters();
                logger.info("Completed iteration of {} values in {}ms" + 
                            " ({}/s)", 
                            new Object[]{values.size(), (iterend-start),
                            1000*values.size()/(iterend-start)});
            }
            
        }
    }
}

@@ -1035,24 +1035,138 @@
 ture.class);
+%0A%0A    private static final String SYNC_STORE_NAME =%0A            SyncTorture.class.getCanonicalName() + %22.torture%22;
 %0A    %0A    IS
@@ -2268,25 +2268,31 @@
 erStore(
-%22torture%22
+SYNC_STORE_NAME
 , Scope.
@@ -3234,17 +3234,23 @@
 ent(
-%22torture%22
+SYNC_STORE_NAME
 , %0A 

Add package qualifiers to store name