// $Id$
// Agent interface on a callback basis

// Copyright (C) 1995-1997 Technische Universitaet Braunschweig, Germany.
// Written by Andreas Zeller <zeller@gnu.org>.
// 
// This file is part of DDD.
// 
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
// 
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, see <http://www.gnu.org/licenses/>.
// 
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page, 
// `http://www.gnu.org/software/ddd/',
// or send a mail to the DDD developers <ddd@gnu.org>.

#ifndef _DDD_LiterateAgent_h
#define _DDD_LiterateAgent_h

/*
    LiterateAgent(app_context, p) opens an asynchronous three-way communication
    channel to the agent p (see Agent, AsyncAgent).

    Communication with the agent is achieved by writing data
    to the agent (write()).

    Each LiterateAgent can be associated with handlers for the events
    defined in Agent, plus:
    LiterateAgent::Output -- data was written to the agent
    LiterateAgent::Input  -- data was received from the agent
    LiterateAgent::Error  -- data was received from the agent's stderr

    A handler takes the form
    void handler(Agent *source, void *client_data, void *call_data)
    where source is the agent causing the event,
    DataLength *d = (DataLength *)call_data is the data sent or received,
    and client_data is arbitrary data passed to addHandler() when
    the handler was added.

    This class dispatches data to handlers as soon as it is available.
    Use a more elaborate subclass to dispatch data in appropriate units.
*/

#include "assert.h"
#include <stdio.h>
#include <string.h>
#include "strclass.h"

#include "AsyncAgent.h"
#include "DataLength.h"


// Event types
const unsigned Ready  = AsyncAgent_NTypes; // Ready for output
const unsigned Output = Ready + 1;         // Output written
const unsigned Input  = Output + 1;        // Input read
const unsigned Error  = Input + 1;	   // Error read

const unsigned LiterateAgent_NTypes = Error + 1; // number of events


class LiterateAgent: public AsyncAgent {
public:
    DECLARE_TYPE_INFO

private:
    bool activeIO;		// Flag: I/O handlers active?
    void _activateIO();         // activate I/O handlers
    void _deactivateIO();       // deactivate I/O handlers

    virtual void activateIO()   
    {
	if (!activeIO) 
	{
	    _activateIO();
	    activeIO = true; 
	}
    }
    virtual void deactivateIO() 
    {
	if (activeIO) 
	{
	    _deactivateIO();
	    activeIO = false; 
	}
    }

    int _read(const char*& data, FILE *fp); // Simple read function
    int _readNonBlocking(char* data, int size, FILE *fp); // Read what's there

    // Event Handlers
    static void outputReady(AsyncAgent *c);
    static void inputReady(AsyncAgent *c);
    static void errorReady(AsyncAgent *c);

    // Flag: do we want TTYs to be read in blocking mode?
    bool _block_tty_input;

    // Return default value for _block_tty_input
    static bool default_block_tty_input();

public:
    // Resources
    bool block_tty_input() const     { return _block_tty_input; }
    bool block_tty_input(bool state) { return _block_tty_input = state; }

protected:
    // Input data handling
    int readInput(const char*& data);
    virtual int _readInput(const char *& data);
    virtual void readAndDispatchInput(bool expectEOF = false);

    // Error data handling
    int readError(const char*& data);
    virtual int _readError(const char *& data);
    virtual void readAndDispatchError(bool expectEOF = false);

    // Event management
    virtual void dispatch(int type, const char *data, int length); // dispatch data

    // Called when handlers were changed
    void handlerChange();

    // Check if fp is a tty and wants blocking input
    bool blocking_tty(FILE *fp) const
    {
	return block_tty_input() && isatty(fileno(fp));
    }

public:
    // Constructor for Agent users
    LiterateAgent(XtAppContext app_context, const string& pth,
		  unsigned nTypes = LiterateAgent_NTypes):
	AsyncAgent(app_context, pth, nTypes), activeIO(false),
	_block_tty_input(default_block_tty_input())
    {}

    // Constructor for Agent writers
    LiterateAgent(XtAppContext app_context, FILE *in = stdin,
		  FILE *out = stdout, FILE *err = 0, 
		  unsigned nTypes = LiterateAgent_NTypes):
	AsyncAgent(app_context, in, out, err, nTypes), activeIO(false),
	// When reading from stdin, always block TTY input.
	_block_tty_input(in == stdin || default_block_tty_input())
    {}

    // "Dummy" Constructor without any communication
    LiterateAgent(XtAppContext app_context, bool dummy,
		  unsigned nTypes = LiterateAgent_NTypes):
	AsyncAgent(app_context, dummy, nTypes), activeIO(false),
	_block_tty_input(default_block_tty_input())
    {}

    // Duplicator
    LiterateAgent(const LiterateAgent& lit)
	: AsyncAgent(lit), activeIO(lit.activeIO),
	  _block_tty_input(lit.block_tty_input())
    {}
    virtual Agent *dup() const { return new LiterateAgent(*this); }

    // Output data handling
    virtual int write(const char *data, int length);

    // Flush output
    int flush();

    // Starter
    virtual void start();
    
    // Terminator
    virtual void abort();
};

#endif // _DDD_LiterateAgent_h
// DON'T ADD ANYTHING BEHIND THIS #endif
