/*
 * Copyright (C) 2016 SignalFx, Inc. All rights reserved.
 */
package com.signalfx.signalflow;

import java.util.concurrent.Callable;

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

import com.signalfx.signalflow.ChannelMessage.DataMessage;
import com.signalfx.signalflow.ChannelMessage.EventMessage;
import com.signalfx.signalflow.ChannelMessage.JobProgressMessage;
import com.signalfx.signalflow.ChannelMessage.JobStartMessage;
import com.signalfx.signalflow.ChannelMessage.MetadataMessage;
import com.signalfx.signalflow.Computation.State;

/**
 * Class provides basic plumbing used by subclasses to processing computation.
 *
 * subclass the onMessage methods and invoke the process method to run on current
 * thread or use executor to submit as callable in another thread.
 *
 * @author dgriff
 */
public abstract class ComputationHandler implements Callable<Computation> {

    protected static final Logger log = LoggerFactory.getLogger(ComputationHandler.class);
    protected Computation computation;
    private long startTimeMs;
    private long stopTimeMs;

    /**
     * Constructor that sets the computation
     *
     * @param computation
     *            instance to process
     */
    public ComputationHandler(Computation computation) {
        this.computation = computation;
    }

    /**
     * Override to process job start messages
     *
     * @param message
     *            job start
     */
    protected void onMessage(JobStartMessage message) {}

    /**
     * Override to process job progress messages
     *
     * @param message
     *            job progress
     */
    protected void onMessage(JobProgressMessage message) {}

    /**
     * Override to process data messages
     *
     * @param message
     *            data
     */
    protected void onMessage(DataMessage message) {}

    /**
     * Override to process event messages
     *
     * @param message
     *            event
     */
    protected void onMessage(EventMessage message) {}

    /**
     * Override to process metadata messages
     *
     * @param message
     *            metadata
     */
    protected void onMessage(MetadataMessage message) {}

    /**
     * @return computation start time in milliseconds since midnight, January 1, 1970 UTC
     */
    public long getStartTimeMs() {
        return startTimeMs;
    }

    /**
     * @return Time at which the computation stopped, in milliseconds since midnight, January 1, 1970 UTC
     */
    public long getStopTimeMs() {
        return stopTimeMs;
    }

    /**
     * Processes the computation
     *
     * @return computation instance that was processed
     * @throws ComputationAbortedException
     *             Exception thrown if the computation is aborted during its execution
     * @throws ComputationFailedException
     *             Exception thrown when the computation failed after being started
     * @throws SignalFlowException
     *             A generic error encountered when interacting with the SignalFx SignalFlow API
     * @throws IllegalStateException
     *             Exception thrown is computation is closed
     */
    public Computation process() throws ComputationAbortedException, ComputationFailedException,
            SignalFlowException, IllegalStateException {
        if (computation.getState() == State.STATE_COMPLETED) {
            throw new IllegalStateException("computation is completed");
        }

        startTimeMs = System.currentTimeMillis();
        stopTimeMs = -1;

        try {
            // iterate computation messages and route to message handling methods
            for (ChannelMessage message : computation) {
                switch (message.getType()) {
                case JOB_START:
                    JobStartMessage jobStartMessage = (JobStartMessage) message;
                    onMessage(jobStartMessage);
                    break;

                case JOB_PROGRESS:
                    JobProgressMessage jobProgressMessage = (JobProgressMessage) message;
                    onMessage(jobProgressMessage);
                    break;

                case DATA_MESSAGE:
                    DataMessage dataMessage = (DataMessage) message;
                    onMessage(dataMessage);
                    break;

                case EVENT_MESSAGE:
                    EventMessage eventMessage = (EventMessage) message;
                    onMessage(eventMessage);
                    break;

                case METADATA_MESSAGE:
                    MetadataMessage metadataMessage = (MetadataMessage) message;
                    onMessage(metadataMessage);
                    break;

                default:
                    break;
                }
            }
        } finally {
            stopTimeMs = System.currentTimeMillis();
            close();
        }

        return computation;
    }

    /**
     * closes the computation
     */
    public void close() {
        computation.close();
    }

    /**
     * Callable implementation that calls process.
     */
    @Override
    public Computation call() throws ComputationAbortedException, ComputationFailedException,
            SignalFlowException, IllegalStateException {
        return process();
    }
}

@@ -300,32 +300,98 @@
 e.EventMessage;%0A
+import com.signalfx.signalflow.ChannelMessage.ExpiredTsIdMessage;%0A
 import com.signa
@@ -2254,39 +2254,251 @@
 %0A    /**%0A     * 
-@return
+Override to process expired tsId messages%0A     *%0A     * @param message%0A     *            expired tsid message%0A     */%0A    protected void onMessage(ExpiredTsIdMessage message) %7B%7D%0A%0A    /**%0A     * @return Time at which the
  computation sta
@@ -2503,13 +2503,11 @@
 tart
- time
+ed,
  in 
@@ -4933,32 +4933,244 @@
         break;%0A%0A
+                case EXPIRED_TSID_MESSAGE:%0A                    ExpiredTsIdMessage expiredTsIdMessage = (ExpiredTsIdMessage) message;%0A                    onMessage(expiredTsIdMessage);%0A                    break;%0A%0A
                 

Handle EXPIRED_TSID_MESSAGE in ComputationHandler