//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
// Jefferson Lab HPC Group, 12000 Jefferson Ave., Newport News, VA 23606
//-----------------------------------------------------------------------------
//
// Description:
//    rsvcClient Reader that is reading data out of socket  
//
// Author:  
//      Jie Chen
//      Jefferson Lab HPC Group
//
// Revision History:
//   $Log: rsvcClientReader.java,v $
//   Revision 1.1.1.1  2000/05/23 15:12:50  pal
//   cdev_psi_1.7.2
//
//   Revision 1.2  1999/12/14 15:38:15  chen
//   Add scrollbar to display
//
//   Revision 1.1  1999/10/18 17:12:39  chen
//   *** empty log message ***
//
//
//
//
import java.io.*;
import java.net.*;
import java.util.*;
import rsvcData;
import rsvcEvent;
import rsvcEventHandler;
import rsvcClient;

public final class rsvcClientReader implements Runnable
{
     // disconnection callback list (list of event handler)
    private Vector discCbkList_ = null;

    // all send/get command callback list
    private Hashtable cmdCbkList_ = null;

    // monitor callback list
    private Hashtable monitorCbkTable_ = null;

    // Input and output buffer
    private DataInputStream input_ = null;

    // Pointer back to rsvcClient 
    private rsvcClient      client_;

    /**
     * Construct a rsvcClientReader from a established client object
     */
    public rsvcClientReader (rsvcClient client)
    {
	client_ = client;

	// create lists and a table for event handlers
	discCbkList_ = new Vector ();
	cmdCbkList_ = new Hashtable (20, (float)0.25);
	monitorCbkTable_ = new Hashtable (20, (float)0.25);

	InputStream tinput = null;
	Socket      socket = null;

	try {
	    socket = client_.getSocket();
	}catch (NullPointerException e) {
	    System.err.print (e);
	    System.exit (-1);
	}

	try {
	    tinput = socket.getInputStream();
	}catch (IOException e) {
	    System.err.print (e);
	    System.exit (-1);
	}
	input_ = new DataInputStream (tinput);
    }

    /**
     * Add an event handler to handle disconnection event.
     * Return true if this event handler is registered successfully.
     * Return false if this event handler is already here.
     */
    public boolean addDisconnectHandler (rsvcEventHandler handler)
    {
	if (discCbkList_.contains (handler) == true)
	    return false;
	discCbkList_.addElement (handler);
	return true;
    }

    /**
     * Clean up all event handlers
     */
    public void cleanupEventHandlers ()
    {
	discCbkList_.removeAllElements ();
	cmdCbkList_.clear ();
	monitorCbkTable_.clear ();
    }

    /**
     * Add an event handler to command event handler list
     */
    public void addCommandEventHandler (int id,
					rsvcEvent oevent)
    {
	// vector is thread safe
	cmdCbkList_.put (new Integer(id), oevent);
    }

    /**
     * Add an event handler to monitor event handler table
     */
    public void addMonitorEventHandler (int id, rsvcEvent oevent)
    {
	// vector is thread safe
	monitorCbkTable_.put (new Integer(id), oevent);
    }
	    
    /**
     * Find out whether a particular event is already in the
     * monitor event handler table
     */
    public boolean containsMonitorEvent (rsvcEvent event)
    {
	Integer key = new Integer (event.getEventid ());
	return monitorCbkTable_.containsKey (key);
    }

    /**
     * Dispatch all event handlers when the server goes down
     */
    public void dispatchDiscEventHandlers () 
    {
	Enumeration list = discCbkList_.elements ();
	rsvcEventHandler handler = null;

	rsvcEvent event = new rsvcEvent ();
	event.setStatus (rsvcConfig.RSVC_DISCONNECTED);

	while (list.hasMoreElements ()) {
	    handler = (rsvcEventHandler)(list.nextElement());
	    handler.handleEvent (event);
	}
    }

    public void run ()
    {
	// buffer for header
	int headerlen = rsvcEvent.headerLen ();
	byte[] header = new byte[headerlen];
	ByteArrayInputStream bainput = new ByteArrayInputStream (header);

	// buffer for payload
	int datasize = 4096;
	int prevsize = 4096;
	byte[] payload = new byte[datasize];
	ByteArrayInputStream plinput = new ByteArrayInputStream (payload);

	// event data object
	rsvcEvent event = new rsvcEvent ();

	while (true) {
	    // read header
	    try {
		input_.readFully (header);
	    }catch (IOException e) {
		System.err.println ("Server is down");
		client_.handleClose ();
		break;
	    }

	    // reset input stream buffer
	    bainput.reset ();
	    try {
		datasize = rsvcEvent.readHeader (bainput);
	    }catch (IOException e) {
		System.err.println ("Receiving header error" + e);
		client_.handleClose ();
		break;		
	    }
	    
	    
	    if (datasize > prevsize) {
		datasize = 2*datasize;
		prevsize = datasize;
		payload = new byte[datasize];
		plinput = new ByteArrayInputStream (payload);
	    }

	    // read raw bytes of length 'datasize'
	    try {
		input_.readFully (payload, 0, datasize);
	    }catch (IOException e) {
		System.err.println ("Server is down here");
		client_.handleClose ();
		break;
	    }

	    // convert above raw bytes into an event
	    plinput.reset ();
	    try {
		event.streamIn (plinput);
	    }catch (IOException e) {
		System.err.println ("Payload data format error: " + e);
		client_.handleClose ();
		break;
	    }

	    // dispatch this event to an appropriate destination
	    if (dispatchEvent (event) != 0) {
		client_.handleClose ();
		break;
	    }
	}
	System.out.println ("rsvcClientReader Thread is finished");
	    
    }
    
    private int dispatchEvent (rsvcEvent event)
    {
	int op = event.getOpcode ();
	int status = 0;

	if (op < rsvcConfig.RSVC_MONITOR_ON) 
	    // all simple command transaction events
	    status = dispatchCmdEventFromServer (event);
	else if (op < rsvcConfig.RSVC_OP_UNKNOWN)
	    // all monitor events
	    status = dispatchMonitorEventFromServer (event);
	else {
	    System.err.println ("rsvcClient: Invalid option code: " + String.valueOf(op));
	    status = -1;
	}
	return status;
    }

    private int dispatchCmdEventFromServer (rsvcEvent event)
    {
	Integer key    = new Integer (event.getEventid());
	int     status = event.getStatus ();
	Object  obj = null;
	rsvcEventHandler handler = null;
	rsvcEvent        oevent = null;

	// retrieve object from event which has key value
	obj = cmdCbkList_.get (key);

	if (obj != null) {
	    oevent = (rsvcEvent)obj;

	    if (oevent.match (event) == false) {
		System.err.println ("Incoming and outgoing events are not match");
		return -1;
	    }

	    try {
		handler = oevent.getHandler ();
	    } catch (NullPointerException e) {
		System.err.println (e);
		return -1;
	    }
	    handler.handleEvent (event);
	    
	    if (status != rsvcConfig.RSVC_INCOMPLETE)
		cmdCbkList_.remove (event);
	}
	else {
	    System.out.println ("rsvcClient: Event from server does not match");
	}
	return 0;
    }

    private int dispatchMonitorEventFromServer (rsvcEvent event)
    {
	Integer key = new Integer (event.getEventid ());
	int     status = event.getStatus();
	Object  obj = null;
	rsvcEventHandler handler = null;
	rsvcEvent oevent = null;

	obj = monitorCbkTable_.get (key);
	if (obj != null) {
	    oevent = (rsvcEvent)obj;
	    try {
		handler = oevent.getHandler ();
	    }catch (NullPointerException e) {
		System.err.println (e);
		return -1;
	    }
	    handler.handleEvent (event);	

	    if (status != rsvcConfig.RSVC_SUCCESS && 
		status != rsvcConfig.RSVC_INCOMPLETE) {
		// if an event is bad or finished, remove it
		monitorCbkTable_.remove (event);
	    }
	}
	else if (status != rsvcConfig.RSVC_SUCCESS) {
	    System.out.println ("rsvcClient: monitor event from server does not match any");
	    System.out.println (event.toString());
	}
	return 0;
    }
}
