/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
//%%  (c) Copyright 1993, 1994 Hewlett-Packard Company
//%%  (c) Copyright 1993, 1994 International Business Machines Corp.
//%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
//%%  (c) Copyright 1993, 1994 Novell, Inc.
//%%  $XConsortium: DtTt.C /main/4 1996/03/19 10:47:59 barstow $
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <sstream>

#include <Xm/TextF.h>
#include <Dt/SpinBox.h>
#include <Dt/HelpDialog.h>
#include "tt_c++.h"
#include "DtTt.h"
#include "messageProps_ui.h"
#include "patternProps_ui.h"

static const char **	dtTtProcids		= 0;
static const void **	dtTtProcidClientDatums	= 0;
static unsigned int	dtTtProcidsCount	= 0;
static Tt_pattern *	dtTtPatterns		= 0;
static unsigned int	dtTtPatternsCount	= 0;
static Tt_message *	dtTtMessages		= 0;
static unsigned int	dtTtMessagesCount	= 0;
static Tt_pattern **	dtTtDtSessions		= 0;
static unsigned int	dtTtDtSessionsCount	= 0;
static Tt_pattern **	dtTtDtFiles		= 0;
static unsigned int	dtTtDtFilesCount	= 0;

int		_DtTtPatsNameKey	= (int) (long) DtTtNth;
extern Tt_pattern	snoopPat;

int
listGrow(
	void **		pList,
	unsigned int &	listCount,
	size_t		elemSize
)
{
	void *newList;
	newList = realloc( *pList, (1 + listCount) * elemSize );
	if (newList == 0) {
		return 0;
	}
	*pList = newList;
	listCount++;
	return 1;
}

XmString
DtTtStatusString(
	Tt_status	status
)
{
	char *s = tt_status_string( status );
	XmString s2 = XmStringCreateLocalized( s );
	tt_free( s );
	return s2;
}

Tt_status
DtTtCreated(
	DtTtType	type,
	const void *	entity,
	const void *	clientData
)
{
	Tt_status status = tt_ptr_error( entity );
	if ((tt_is_err( status )) || (entity == 0)) {
		return TT_OK;
	}
	switch (type) {
		char *proc;
	    case DTTT_PROCID:
		proc = strdup( (const char *)entity );
		if (proc == 0) {
			return TT_ERR_NOMEM;
		}
		if (! listAppend( dtTtProcids, dtTtProcidsCount, const char *,
				  proc ))
		{
			return TT_ERR_NOMEM;
		}
		dtTtProcidsCount--; // shared by both lists
		if (! listAppend( dtTtProcidClientDatums, dtTtProcidsCount,
				  const void *, clientData ))
		{
			return TT_ERR_NOMEM;
		}
		break;
	    case DTTT_MESSAGE:
		if (DtTtIndex( type, entity ) >= 0) {
			return TT_OK;
		}
		if (! listAppend( dtTtMessages, dtTtMessagesCount, Tt_message,
				  (const Tt_message)entity ))
		{
			return TT_ERR_NOMEM;
		}
		break;
	    case DTTT_PATTERN:
		if (DtTtIndex( type, entity ) >= 0) {
			return TT_OK;
		}
		if (! listAppend( dtTtPatterns, dtTtPatternsCount, Tt_pattern,
				  (const Tt_pattern)entity ))
		{
			return TT_ERR_NOMEM;
		}
		break;
	    case DTTT_PTYPE:
	    case DTTT_OTYPE:
	    case DTTT_OBJECT:
	    case DTTT_SESSION:
	    case DTTT_DTSESSION:
	    case DTTT_DTFILE:
	    case DTTT_OP:
		abort();
		break;
	}
	return TT_OK;
}

Tt_status
DtTtCreated(
	DtTtType	type,
	Tt_pattern *	entity,
	const char *	name
)
{
	if ((tt_is_err( tt_ptr_error( entity ) )) || (entity == 0)) {
		return TT_OK;
	}
	if ((tt_is_err( tt_ptr_error( name ) )) || (name == 0)) {
		return TT_OK;
	}
	switch (type) {
	    case DTTT_PROCID:
	    case DTTT_MESSAGE:
	    case DTTT_PATTERN:
	    case DTTT_PTYPE:
	    case DTTT_OTYPE:
	    case DTTT_OBJECT:
	    case DTTT_SESSION:
	    case DTTT_OP:
		abort();
		break;
	    case DTTT_DTSESSION:
		tt_pattern_user_set( *entity, _DtTtPatsNameKey,
				     strdup( name ));
		if (DtTtIndex( type, entity ) >= 0) {
			return TT_OK;
		}
		if (! listAppend( dtTtDtSessions, dtTtDtSessionsCount,
				  Tt_pattern *, (Tt_pattern *)entity ))
		{
			return TT_ERR_NOMEM;
		}
		break;
	    case DTTT_DTFILE:
		tt_pattern_user_set( *entity, _DtTtPatsNameKey,
				     strdup( name ));
		if (DtTtIndex( type, entity ) >= 0) {
			return TT_OK;
		}
		if (! listAppend( dtTtDtFiles, dtTtDtFilesCount,
				  Tt_pattern *, (Tt_pattern *)entity ))
		{
			return TT_ERR_NOMEM;
		}
		break;
	}
	while (*entity != 0) {
		DtTtCreated( DTTT_PATTERN, *entity );
		entity++;
	}
	return TT_OK;
}

void *
DtTtNth(
	DtTtType	type,
	int		n
)
{
	switch (type) {
	    case DTTT_PROCID:
		if ((n < 0) || (n >= dtTtProcidsCount)) {
			return 0;
		}
		return (void *)dtTtProcids[ n ];
	    case DTTT_MESSAGE:
		if ((n < 0) || (n >= dtTtMessagesCount)) {
			return 0;
		}
		return dtTtMessages[ n ];
	    case DTTT_PATTERN:
		if ((n < 0) || (n >= dtTtPatternsCount)) {
			return 0;
		}
		return dtTtPatterns[ n ];
	    case DTTT_DTSESSION:
		if ((n < 0) || (n >= dtTtDtSessionsCount)) {
			return 0;
		}
		return dtTtDtSessions[ n ];
	    case DTTT_DTFILE:
		if ((n < 0) || (n >= dtTtDtFilesCount)) {
			return 0;
		}
		return dtTtDtFiles[ n ];
	    case DTTT_OP:
		return tttk_op_string( (Tttk_op)(n+1) );
	}

        return 0;
}

void *
DtTtNthClientDatum(
	DtTtType	type,
	int		n
)
{
	switch (type) {
	    case DTTT_PROCID:
		if ((n < 0) || (n >= dtTtProcidsCount)) {
			return 0;
		}
		return (void *)dtTtProcidClientDatums[ n ];
	    default:
		abort();
		return 0;
	}
}

void
DtTtNthClientDatumSet(
	DtTtType	type,
	int		n,
	const void *	clientData
)
{
	switch (type) {
	    case DTTT_PROCID:
		if ((n < 0) || (n >= dtTtProcidsCount)) {
			return;
		}
		dtTtProcidClientDatums[ n ] = clientData;
		return;
	    default:
		abort();
	}
}

int
DtTtIndex(
	DtTtType	type,
	const void *	entity
)
{
	Tt_status status = tt_ptr_error( entity );
	if ((tt_is_err( status )) || (entity == 0)) {
		return -1;
	}
	switch (type) {
		int i;
	    case DTTT_PROCID:
		for (i = dtTtProcidsCount - 1; i >= 0; i--) {
			if (strcmp( dtTtProcids[i], (char *)entity) == 0) {
				return i;
			}
		}
		return -1;
	    case DTTT_MESSAGE:
		for (i = dtTtMessagesCount - 1; i >= 0; i--) {
			if (dtTtMessages[i] == entity) {
				return i;
			}
		}
		return -1;
	    case DTTT_PATTERN:
		for (i = dtTtPatternsCount - 1; i >= 0; i--) {
			if (dtTtPatterns[i] == entity) {
				return i;
			}
		}
		return -1;
	    case DTTT_DTSESSION:
		for (i = dtTtDtSessionsCount - 1; i >= 0; i--) {
			if (dtTtDtSessions[i] == entity) {
				return i;
			}
		}
		return -1;
	    case DTTT_DTFILE:
		for (i = dtTtDtFilesCount - 1; i >= 0; i--) {
			if (dtTtDtFiles[i] == entity) {
				return i;
			}
		}
		return -1;
	    case DTTT_OP:
		for (i = 1; i < TTDT_OP_LAST; i++) {
			if (0 == strcmp( tttk_op_string( (Tttk_op)i ),
					 (char *)entity ))
			{
				return i;
			}
		}
		return -1;
	}

        return 0;
}

Tt_status
DtTtDestroyed(
	DtTtType	type,
	const void *	entity
)
{
	Tt_status status = tt_ptr_error( entity );
	if ((tt_is_err( status )) || (entity == 0)) {
		return TT_OK;
	}
	switch (type) {
		int i, j;
		Tt_pattern *pats;
	    case DTTT_PROCID:
		for (i = dtTtProcidsCount - 1; i >= 0; i--) {
			if (strcmp( dtTtProcids[i], (char *)entity) == 0) {
				break;
			}
		}
		if (i < 0) {
			return TT_WRN_NOTFOUND;
		}
		for (j = i; j < dtTtProcidsCount - 1; j++) {
			dtTtProcids[j] = dtTtProcids[j+1];
		}
		for (j = i; j < dtTtProcidsCount - 1; j++) {
			dtTtProcidClientDatums[j] =
				dtTtProcidClientDatums[j+1];
		}
		dtTtProcidsCount--;
		break;
	    case DTTT_MESSAGE:
		for (i = dtTtMessagesCount - 1; i >= 0; i--) {
			if (dtTtMessages[i] == entity) {
				break;
			}
		}
		if (i < 0) {
			return TT_WRN_NOTFOUND;
		}
		for (j = i; j < dtTtMessagesCount - 1; j++) {
			dtTtMessages[j] = dtTtMessages[j+1];
		}
		dtTtMessagesCount--;
		break;
	    case DTTT_PATTERN:
		if (snoopPat == entity) snoopPat = 0;
		for (i = dtTtPatternsCount - 1; i >= 0; i--) {
			if (dtTtPatterns[i] == entity) {
				break;
			}
		}
		if (i < 0) {
			return TT_WRN_NOTFOUND;
		}
		for (j = i; j < dtTtPatternsCount - 1; j++) {
			dtTtPatterns[j] = dtTtPatterns[j+1];
		}
		dtTtPatternsCount--;
		break;
	    case DTTT_DTSESSION:
		for (i = dtTtDtSessionsCount - 1; i >= 0; i--) {
			if (dtTtDtSessions[i] == entity) {
				break;
			}
		}
		if (i < 0) {
			return TT_WRN_NOTFOUND;
		}
		pats = (Tt_pattern *)entity;
		while (*pats != 0) {
			DtTtDestroyed( DTTT_PATTERN, *pats );
			pats++;
		}
		for (j = i; j < dtTtDtSessionsCount - 1; j++) {
			dtTtDtSessions[j] = dtTtDtSessions[j+1];
		}
		dtTtDtSessionsCount--;
		break;
	    case DTTT_DTFILE:
		for (i = dtTtDtFilesCount - 1; i >= 0; i--) {
			if (dtTtDtFiles[i] == entity) {
				break;
			}
		}
		if (i < 0) {
			return TT_WRN_NOTFOUND;
		}
		pats = (Tt_pattern *)entity;
		while (*pats != 0) {
			DtTtDestroyed( DTTT_PATTERN, *pats );
			pats++;
		}
		for (j = i; j < dtTtDtFilesCount - 1; j++) {
			dtTtDtFiles[j] = dtTtDtFiles[j+1];
		}
		dtTtDtFilesCount--;
		break;
	}
	return TT_OK;
}

Tt_status
DtTtSetLabel(
	Widget labelWidget,
	const char *string
)
{
    if (labelWidget == 0) {
	    return TT_OK;
    }
    XmString labelXmString = XmStringCreateLocalized( (String)string );
    XtVaSetValues( labelWidget, XmNlabelString, labelXmString, NULL );
    XmStringFree( labelXmString );
    return TT_OK;
}

Tt_status
DtTtSetLabel(
	Widget labelWidget,
	const char *func,
	void *val
)
{
    Tt_status status = tt_ptr_error( val );
    std::ostringstream errStream;
    errStream << func << " = " << val << " (" << status << ")" << ends;
    DtTtSetLabel( labelWidget, errStream.str().c_str() );
    return status;
}

Tt_status
DtTtSetLabel(
	Widget labelWidget,
	const char *func,
	Tt_status status
)
{
    std::ostringstream errStream;
    errStream << func << " = " << status << ends;
    DtTtSetLabel( labelWidget, errStream.str().c_str() );
    return status;
}

int
DtTtSetLabel(
	Widget labelWidget,
	const char *func,
	int returnVal
)
{
    std::ostringstream errStream;
    errStream << func << " = " << returnVal << ends;
    DtTtSetLabel( labelWidget,
                  const_cast<char *>(errStream.str().c_str()) );
    return returnVal;
}


static XmString *
_DtTtChoices(
	Tt_pattern **	pPats,
	int		count
)
{
	// XXX when to free?
	XmString *items = (XmString *)XtMalloc( count * sizeof( XmString ));
	if (items == 0) {
		return 0;
	}
	for (int i = 0; i < count; i++) {
		std::ostringstream itemStream;
		itemStream << (void *)pPats[ i ];
		char *name = (char *)
			tt_pattern_user( *pPats[ i ], _DtTtPatsNameKey );
		if (! tt_is_err( tt_ptr_error( name ))) {
			itemStream << " " << name;
			tt_free( name );
		}
		itemStream << ends;
		items[ i ] =
                    XmStringCreateLocalized(const_cast<char *>(itemStream.str().c_str()));
	}
	return items;
}

XmString *
_DtTtChoices(
	DtTtType type,
	int *    itemCount
)
{
	*itemCount = 0;
	switch (type) {
		XmString *items;
		int i;
		int opCount;
	    case DTTT_PROCID:
		items = (XmString *)
			XtMalloc( dtTtProcidsCount * sizeof( XmString ));
		if (items == 0) {
			return 0;
		}
		*itemCount = dtTtProcidsCount;
		for (i = 0; i < dtTtProcidsCount; i++) {
			items[ i ] = XmStringCreateLocalized(
					(String)dtTtProcids[ i ] );
		}
		return items;
	    case DTTT_MESSAGE:
		items = (XmString *)
			XtMalloc( dtTtMessagesCount * sizeof( XmString ));
		if (items == 0) {
			return 0;
		}
		*itemCount = dtTtMessagesCount;
		for (i = 0; i < dtTtMessagesCount; i++) {
			std::ostringstream itemStream;
			itemStream << (void *)dtTtMessages[ i ];
			char *op = tt_message_op( dtTtMessages[ i ] );
			if (op && ! tt_is_err( tt_ptr_error( op ))) {
				itemStream << " " << op;
				tt_free( op );
			}
			char *id = tt_message_id( dtTtMessages[ i ] );
			if (! tt_is_err( tt_ptr_error( id ))) {
				itemStream << " " << id;
				tt_free( id );
			}
			itemStream << ends;
			items[ i ] =
                            XmStringCreateLocalized(const_cast<char *>(itemStream.str().c_str()));
		}
		return items;
	    case DTTT_PATTERN:
		items = (XmString *)
			XtMalloc( dtTtPatternsCount * sizeof( XmString ));
		if (items == 0) {
			return 0;
		}
		*itemCount = dtTtPatternsCount;
		for (i = 0; i < dtTtPatternsCount; i++) {
			std::ostringstream itemStream;
			itemStream << (void *)dtTtPatterns[ i ] << ends;
			items[ i ] = XmStringCreateLocalized(
                            const_cast<char *>(itemStream.str().c_str()) );
		}
		return items;
	    case DTTT_DTSESSION:
		*itemCount = dtTtDtSessionsCount;
		return _DtTtChoices( dtTtDtSessions, dtTtDtSessionsCount );
	    case DTTT_DTFILE:
		*itemCount = dtTtDtFilesCount;
		return _DtTtChoices( dtTtDtFiles, dtTtDtFilesCount );
	    case DTTT_OP:
		// XXX when to free? ditto for each case
		opCount = ((int)TTDT_OP_LAST) - 1;
		items = (XmString *)
			XtMalloc( opCount * sizeof( XmString ));
		if (items == 0) {
			return 0;
		}
		*itemCount = opCount;
		for (i = 1; i <= opCount; i++) {
			items[ i-1 ] = XmStringCreateLocalized(
					(String)tttk_op_string( (Tttk_op)i ));
		}
		return items;
	}

        return 0;
}

#if defined(aix)
#define AIX_CONST_STRING	(char *)
#else
#define AIX_CONST_STRING
#endif

void
_DtOpen(
	Widget		label,
	const char *	cmd,
	const char *	tempnamTemplate
)
{
    char *file = tempnam( 0, AIX_CONST_STRING tempnamTemplate );
    std::ostringstream cmdStream;
    cmdStream << cmd << " > " << file << ends;
    int sysStat = system( cmdStream.str().c_str() );
    if (! WIFEXITED( sysStat )) {
	    std::ostringstream func;
	    func << "system( \"" << cmdStream.str() << "\" )" << ends;
	    DtTtSetLabel( label, func.str().c_str(), sysStat );
	    return;
    }
    if (WEXITSTATUS( sysStat ) != 0) {
        DtTtSetLabel( label, cmdStream.str().c_str(), WEXITSTATUS( sysStat ));
	    return;
    }
    _DtOpen( label, file );
}

void
_DtOpen(
	Widget		label,
	const char *	file
)
{
    std::ostringstream labelStream;
    labelStream << "dtaction Open " << file << ends;
    DtTtSetLabel( label, labelStream.str().c_str() );

    std::ostringstream cmd;
    cmd << "( unset TT_TRACE_SCRIPT; if dtaction Open " << file
	<< "; then :; else textedit " << file << "; fi; sleep 600; rm -f "
	<< file << " ) &" << ends;
    system( cmd.str().c_str() );
}

void
_DtOpen(
	Widget		label,
	void *		buf,
	size_t		len,
	const char *	tempnamTemplate
)
{
    char *file = tempnam( 0, AIX_CONST_STRING tempnamTemplate );
    int fd = open( file, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR );
    if (write( fd, buf, len ) < 0) {
	    DtTtSetLabel( label, file, errno );
	    return;
    }
    close( fd );
    _DtOpen( label, file );
}

void
_DtMan(
	Widget		label,
	const char *	topic
)
{
    std::ostringstream labelStream;
    labelStream << "dtaction Dtmanpageview " << topic << ends;
    DtTtSetLabel( label, labelStream.str().c_str() );

    std::ostringstream cmd;
    cmd << "unset TT_TRACE_SCRIPT; if dtaction Dtmanpageview " << topic
	<< "; then :; else cmdtool -c man " << topic << "; fi &" << ends;
    system( cmd.str().c_str() );
}

Boolean
_DtCanHelp(
	const char *topics
)
{
	if (topics == 0) return False;
	if (strchr( topics, ' ' ) != 0) {
		// Must not be a list of man pages
		return False;
	}
	return True;
}

Boolean
_DtHelped(
	Widget helpDialog
)
{
	char *topics;
	XtVaGetValues( helpDialog, DtNstringData, &topics, NULL );
	if (! _DtCanHelp( topics )) {
		return False;
	}
	char *newTopics = strdup( topics );
	const char *whiteSpace = "(12345689) \t:-,.*\n";
	const char *topic = strtok( newTopics, whiteSpace );
	while (topic != 0) {
		_DtMan( 0, topic );
		topic = strtok( 0, whiteSpace );
	}
	free( newTopics );
	return True;
}
