package org.lsst.ccs.subsystem.metrology;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.lsst.ccs.bus.data.ConfigurationInfo;
import org.lsst.ccs.bus.data.ConfigurationParameterInfo;
import org.lsst.ccs.messaging.AgentMessagingLayer;
import org.lsst.ccs.messaging.AgentPresenceListener;
import org.lsst.ccs.Agent;

//import org.lsst.ccs.subsystem.archon.data.ArchonControllerStatus;
import org.lsst.ccs.messaging.ConcurrentMessagingUtils;
import org.lsst.ccs.messaging.StatusMessageListener;

import org.lsst.ccs.subsystem.metrology.data.MetrologyConfig;

import org.lsst.ccs.utilities.logging.Logger;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.function.Predicate;
import org.lsst.ccs.bootstrap.BootstrapResourceUtils;
import org.lsst.ccs.bus.data.AgentInfo;
import org.lsst.ccs.bus.data.KeyValueData;
import org.lsst.ccs.bus.messages.BusMessage;
import org.lsst.ccs.bus.messages.CommandRequest;
import org.lsst.ccs.bus.messages.StatusConfigurationInfo;
import org.lsst.ccs.bus.messages.StatusMessage;
import org.lsst.ccs.bus.messages.StatusSubsystemData;
import org.lsst.ccs.messaging.BusMessageFilterFactory;

/**
 ***************************************************************************
 **
 ** Catches configuration changes to the Metrology Subsystem
 *
 ** @author Homer Neal *
 * **************************************************************************
 */
public class MetrologyConfigCatcher implements Serializable, AgentPresenceListener,
        StatusMessageListener {

    private static MetrologyConfig tsc = null;
    private AgentMessagingLayer agentMessagingLayer = null;
    private static final Logger LOGGER = Logger.getLogger("org.lsst.ccs.subsystem.metrology.main");
    private static String DEST = "metrology";
//    private BusMessageFilter filterConfig;
    private ConcurrentMessagingUtils cmu;
    private boolean first = true;

    public MetrologyConfigCatcher() {
    }

    public MetrologyConfigCatcher(MetrologyConfig tsc) {
        this.tsc = tsc;
    }

    public void initCatcher(ConfigurationInfo configInfo) {
        System.out.println("Initializing the configuration catcher");
        agentMessagingLayer = Agent.getEnvironmentMessagingAccess();
        cmu = new ConcurrentMessagingUtils(agentMessagingLayer);
        DEST = System.getProperty("lsst.ccs.metrology.metrologyguidest", "metrology");
//        filterConfig = BusMessageFilter.messageOrigin(DEST).and(BusMessageFilter.messageClass(StatusConfigurationInfo.class));
        agentMessagingLayer.getAgentPresenceManager().addAgentPresenceListener(this);
        Predicate<BusMessage<? extends Serializable, ?>> filter
                = BusMessageFilterFactory.messageOrigin(DEST).and(BusMessageFilterFactory.messageClass(StatusSubsystemData.class));
        agentMessagingLayer.addStatusMessageListener(this, filter);

        // assume that the initial configuration parameters are already available
        // ==> connect
        updateConfig(configInfo);
        /*
         try {
         Future<Object> future = cmu.sendAsynchronousCommand(new CommandRequest(DEST, "getConfigurationInfo"));
         ConfigurationInfo configInfo = (ConfigurationInfo) future.get();
         updateConfig(configInfo);
         } catch (Exception ex) {
         LOGGER.warn("unable to retrieve configuration information", ex);
         }
         */
    }

    @Override
    public void onStatusMessage(StatusMessage msg) {
        /*
         Object msgObject = msg.getObject();
         System.out.println("msg type = "+msgObject.toString());
         if (msgObject instanceof KeyValueData) {
         KeyValueData d = (KeyValueData) msgObject;
         System.out.println("MetrologyConfigCatcher: In onStatusMessage method. KEY=" + d.getKey());
         */
        if (msg == null) {
            return;
        }

        ConfigurationInfo configInfo = null;
//        System.out.println("MetrologyConfigCatcher msg text = "+msg.toString());
//        System.out.println("Now getting object");
        if (msg.getObject() instanceof ConfigurationInfo) {
            configInfo = (ConfigurationInfo) msg.getObject();
        } else {
            return;
        }
        updateConfig(configInfo);

    }

    public void updateConfig(ConfigurationInfo configInfo) {
        System.out.println("update config for " + configInfo.getConfigurationName());

        List<ConfigurationParameterInfo> listChanges = configInfo.getLatestChanges();

//        if (first || configInfo.hasChanges()) {
        if (true) {
            first = false;
            for (String componentName : MetrologyConfig.COMPONENT_NAMES) {
                Map<String, String> configForComponent = configInfo.getCurrentValuesFor(componentName);
                System.out.println("componentName: "+componentName);
                if (!configForComponent.isEmpty()) {
                    System.out.println("config Map: " + configForComponent.toString());

                    int istate = MetrologyConfig.configuration_states.valueOf(componentName).ordinal();
                    System.out.println("istate = " + istate);
                    double startx, stopx, dx, starty, stopy, dy, rotation, cornerang, acceleration, speed;
                    int nsamps, measmode;
                    System.out.println("getting startx");
                    startx = Double.valueOf(configForComponent.get("startx"));
                    System.out.println("getting stopx");
                    stopx = Double.valueOf(configForComponent.get("stopx"));
                    System.out.println("getting dx");
                    dx = Double.valueOf(configForComponent.get("dx"));
                    System.out.println("getting starty");
                    starty = Double.valueOf(configForComponent.get("starty"));
                    System.out.println("getting stopy");
                    stopy = Double.valueOf(configForComponent.get("stopy"));
                    System.out.println("getting dy");
                    dy = Double.valueOf(configForComponent.get("dy"));
                    System.out.println("getting rotation");
                    rotation = Double.valueOf(configForComponent.get("rotation"));
                    System.out.println("getting cornerang");
                    cornerang = Double.valueOf(configForComponent.get("cornerang"));
                    System.out.println("getting nsamples");
                    nsamps = Integer.valueOf(configForComponent.get("nsamples"));
                    System.out.println("getting measmode");
                    measmode = Integer.valueOf(configForComponent.get("measmode"));
                    System.out.println("getting acceleration");
                    acceleration = Double.valueOf(configForComponent.get("acceleration"));
                    System.out.println("getting speed");
                    speed = Double.valueOf(configForComponent.get("speed"));

                    tsc.update(istate, startx, stopx, dx, starty, stopy, dy, rotation, cornerang, nsamps, measmode, acceleration, speed);
                } else {
                    System.out.println("empty config!");
                }
            }
        }
    }

    public void connecting(AgentInfo agent) {
        System.out.println("Agent name = " + agent.getName());
        DEST = System.getProperty("lsst.ccs.metrology.metrologyguidest", "metrology");

        if (!agent.getName().equals(DEST)) {
            return;
        }
        LOGGER.info("connecting");
        Future<Object> future = cmu.sendAsynchronousCommand(new CommandRequest(DEST, "getConfigurationInfo"));
        try {
            ConfigurationInfo configInfo = (ConfigurationInfo) future.get();
            updateConfig(configInfo);
            DEST = System.getProperty("lsst.ccs.metrology.metrologyguidest", "metrology");
            Predicate<BusMessage<? extends Serializable, ?>> filter
                    = BusMessageFilterFactory.messageOrigin(DEST).and(BusMessageFilterFactory.messageClass(StatusSubsystemData.class));
            agentMessagingLayer.addStatusMessageListener(this, filter);
        } catch (Exception ex) {
            LOGGER.warn("unable to retrieve configuration information", ex);
        }
    }

    public void disconnecting(AgentInfo agent) {
    }
}

