package peak.can;

import java.util.HashMap;
import java.util.Vector;
import peak.can.basic.TPCANMsg;
import peak.can.basic.TPCANTimestamp;
import peak.can.basic.IRcvEventProcessor;
import peak.can.basic.PCANBasic;
import peak.can.basic.TPCANHandle;
import peak.can.basic.TPCANStatus;

/**
 * The CANReadThread class extends Thread class and is used to process readed CAN Messages.
 * In addition, the class provides different read mode that are "By Timer" or "By Event".
 * It is possible to read CAN Messages with its Time Stamp.
 *
 * @version 1.0
 * @LastChange 15/12/2009
 * @author Urban Jonathan
 *
 * @Copyright (C) 1999-2009  PEAK-System Technik GmbH, Darmstadt
 * more Info at http://www.peak-system.com
 */
public class CANReadThread extends Thread implements IRcvEventProcessor
{
    // PCANBasic instance used to call read functions
    private PCANBasic pcanBasic;
    // Collection which stores all connected channels
    private Vector<ChannelItem> connectedChannelCollection = null;
    // Collection to store readed CAN Messages
    private HashMap dataRowCollection;
    // Used to read CAN Messages with its Time stamp
    private Boolean readTimeStamp = false;

    public Boolean getReadTimeStamp()
    {
        return readTimeStamp;
    }

    public void setReadTimeStamp(Boolean useReadEx)
    {
        this.readTimeStamp = useReadEx;
    }

    /**
     *
     * @param can PCANBasic instance used to call read functions
     * @param channels Reference to the collection which stores all connected channels
     * @param data Reference to the Collection which store readed CAN Messages
     */
    public CANReadThread(PCANBasic pcanbasic, Vector<ChannelItem> connectedChannelCollection, HashMap dataRowCollection)
    {
        this.pcanBasic = pcanbasic;
        this.dataRowCollection = dataRowCollection;
        this.connectedChannelCollection = connectedChannelCollection;
    }

    /**
     * Starts thread process
     */
    public void run()
    {
        while (true)
        {
            // Process each connected channel
            for (ChannelItem item : connectedChannelCollection)
            {
                if ((item != MarkAllChannelItem.getInstance()) && (item.getWorking()))
                    // Call the PCANBasic Read Function
                    callAPIFunctionRead(item.getHandle());
            }

            // Sleep Time
            try
            {
                Thread.sleep(10);
            }
            catch (InterruptedException e)
            {
                return;
            }
        }
    }

    /**
     * Calls the PCANBasic Read Function according the readTimeStamp parameter
     *
     * @param handle The handle of a PCAN Channel
     */
    public void callAPIFunctionRead(TPCANHandle handle)
    {
        //Local variables
        TPCANMsg canMessage = null;
        TPCANTimestamp rcvTime = null;
        TableDataRow dataRow = null;
        TPCANStatus ret;
        int messageID = 0;

        try
        {
            // Create new CAN Message
            canMessage = new TPCANMsg();
            // Create new TimeStamp object
            rcvTime = new TPCANTimestamp();

            //If TimeStamp is needed
            if (readTimeStamp)
                // We execute the "Read" function of the PCANBasic
                ret = pcanBasic.Read(handle, canMessage, rcvTime);
            //If TimeStamp is not needed
            else
                // We execute the "Read" function of the PCANBasic
                ret = pcanBasic.Read(handle, canMessage, null);

            //Process result
            if (ret == TPCANStatus.PCAN_ERROR_OK)
            {
                //Gets CAN Message ID
                messageID = canMessage.getID();

                // Searchs dataRowCollection contains CAN Message ID
                if (dataRowCollection.containsKey(messageID))
                    // Update it
                    dataRow = (TableDataRow) dataRowCollection.get(messageID);
                else
                    // Create a new TableDataRow object
                    dataRow = new TableDataRow();

                // Sets Message content
                dataRow.setMessage(canMessage);

                // Sets readTimeStamp if need be
                if (readTimeStamp)
                    dataRow.setRcvTime(rcvTime);
                else
                    dataRow.setRcvTime(null);

                // Sets counter
                dataRow.setCounter(dataRow.getCounter() + 1);

                //Critical Area
                synchronized (Application.token)
                {
                    //Put Message In the dataRowCollection
                    dataRowCollection.put(messageID, dataRow);
                }

                // Free local variables
                canMessage = null;
                rcvTime = null;
            }
        }
        catch (Exception e)
        {
            System.out.println("CANReadThread Exception:" + e.getMessage());
            e.printStackTrace();
            System.exit(0);
        }
    }

    // This function is called by the JNI library when a CAN Receive-Event is detected
    public void processRcvEvent(TPCANHandle channel)
    {
        // Process a PCANBasic read call
        callAPIFunctionRead(channel);
    }
}
