/** \file csignal.c
 * Signals
 */

/* -*- C -*- ****************************************************************
 *
 *  System        :
 *  Module        :
 *  Object Name   : $RCSfile$
 *  Revision      : $Revision$
 *  Date          : $Date$
 *  Author        : $Author$
 *  Created By    : Robert Heller
 *  Created       : Sun Feb 19 13:11:45 2017
 *  Last Modified : <170417.1113>
 *
 *  Description
 *
 *  Notes
 *
 *  History
 *
 ****************************************************************************
 *
 *    Copyright (C) 2017  Robert Heller D/B/A Deepwoods Software
 *			51 Locke Hill Road
 *			Wendell, MA 01379-9728
 *
 *    This program 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 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program 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 this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 *
 *
 ****************************************************************************/

//static const char rcsid[] = "@(#) : $Id$";


#include "compound.h"
#include "cselect.h"
#include "cundo.h"
#include "custom.h"
#include "fileio.h"
#include "layout.h"
#include "param.h"
#include "track.h"
#include "common-ui.h"
#ifdef UTFCONVERT
#include "include/utf8convert.h"
#endif // UTFCONVERT

EXPORT TRKTYP_T T_SIGNAL = -1;

static int log_signal = 0;


#if 0
static drawCmd_t signalD = {
	NULL,
	&screenDrawFuncs,
	0,
	1.0,
	0.0,
	{0.0,0.0}, {0.0,0.0},
	Pix2CoOrd, CoOrd2Pix
};

static char signalName[STR_SHORT_SIZE];
static int  signalHeadCount;
#endif

typedef struct signalAspect_t {
	char * aspectName;
	char * aspectScript;
} signalAspect_t, *signalAspect_p;

static dynArr_t signalAspect_da;
#define signalAspect(N) DYNARR_N( signalAspect_t, signalAspect_da, N )

typedef struct signalData_t {
	extraDataBase_t base;
	coOrd orig;
	ANGLE_T angle;
	char * name;
	wIndex_t numHeads;
	BOOL_T IsHilite;
	wIndex_t numAspects;
	signalAspect_t aspectList;
} signalData_t, *signalData_p;

static signalData_p GetsignalData ( track_p trk )
{
	return GET_EXTRA_DATA( trk, T_SIGNAL, signalData_t );
}

#define BASEX 6
#define BASEY 0

#define MASTX 0
#define MASTY 12

#define HEADR 4


#define signal_SF (3.0)

static void DDrawSignal(drawCmd_p d, coOrd orig, ANGLE_T angle,
                        wIndex_t numHeads, DIST_T scaleRatio,
                        wDrawColor color )
{
	coOrd p1, p2;
	ANGLE_T x_angle, y_angle;
	DIST_T hoffset;
	wIndex_t ihead;

	x_angle = 90-(360-angle);
	if (x_angle < 0) { x_angle += 360; }
	y_angle = -(360-angle);
	if (y_angle < 0) { y_angle += 360; }

	Translate (&p1, orig, x_angle, (-BASEX) * signal_SF / scaleRatio);
	Translate (&p1, p1,   y_angle, BASEY * signal_SF / scaleRatio);
	Translate (&p2, orig, x_angle, BASEX * signal_SF / scaleRatio);
	Translate (&p2, p2,   y_angle, BASEY * signal_SF / scaleRatio);
	DrawLine(d, p1, p2, 2, color);
	p1 = orig;
	Translate (&p2, orig, x_angle, MASTX * signal_SF / scaleRatio);
	Translate (&p2, p2,   y_angle, MASTY * signal_SF / scaleRatio);
	DrawLine(d, p1, p2, 2, color);
	hoffset = MASTY;
	for (ihead = 0; ihead < numHeads; ihead++) {
		Translate (&p1, orig, x_angle, MASTX * signal_SF / scaleRatio);
		Translate (&p1, p1,   y_angle, (hoffset+HEADR) * signal_SF / scaleRatio);
		DrawFillCircle(d,p1,HEADR * signal_SF / scaleRatio,color);
		hoffset += HEADR*2;
	}
}

static void DrawSignal (track_p t, drawCmd_p d, wDrawColor color )
{
	signalData_p xx = GetsignalData(t);
	DDrawSignal(d,xx->orig, xx->angle, xx->numHeads, GetScaleRatio(GetTrkScale(t)),
	            color);
}

static void SignalBoundingBox (coOrd orig, ANGLE_T angle,wIndex_t numHeads,
                               DIST_T scaleRatio, coOrd *hi, coOrd *lo)
{
	coOrd p1, p2, headp1, headp2;
	ANGLE_T x_angle, y_angle;
	DIST_T hoffset,delta;
	wIndex_t ihead;

	x_angle = 90-(360-angle);
	if (x_angle < 0) { x_angle += 360; }
	y_angle = -(360-angle);
	if (y_angle < 0) { y_angle += 360; }

	Translate (&p1, orig, x_angle, (-BASEX) * signal_SF / scaleRatio);
	Translate (&p1, p1,   y_angle, BASEY * signal_SF / scaleRatio);
	Translate (&p2, orig, x_angle, BASEX * signal_SF / scaleRatio);
	Translate (&p2, p2,   y_angle, BASEY * signal_SF / scaleRatio);
	*hi = p1; *lo = p1;
	if (p2.x > hi->x) { hi->x = p2.x; }
	if (p2.x < lo->x) { lo->x = p2.x; }
	if (p2.y > hi->y) { hi->y = p2.y; }
	if (p2.y < lo->y) { lo->y = p2.y; }
	p1 = orig;
	Translate (&p2, orig, x_angle, MASTX * signal_SF / scaleRatio);
	Translate (&p2, p2,   y_angle, MASTY * signal_SF / scaleRatio);
	if (p1.x > hi->x) { hi->x = p1.x; }
	if (p1.x < lo->x) { lo->x = p1.x; }
	if (p1.y > hi->y) { hi->y = p1.y; }
	if (p1.y < lo->y) { lo->y = p1.y; }
	if (p2.x > hi->x) { hi->x = p2.x; }
	if (p2.x < lo->x) { lo->x = p2.x; }
	if (p2.y > hi->y) { hi->y = p2.y; }
	if (p2.y < lo->y) { lo->y = p2.y; }
	hoffset = MASTY;
	for (ihead = 0; ihead < numHeads; ihead++) {
		Translate (&p1, orig, x_angle, MASTX * signal_SF / scaleRatio);
		Translate (&p1, p1,   y_angle, (hoffset+HEADR) * signal_SF / scaleRatio);
		delta = HEADR * signal_SF / scaleRatio;
		headp1.x = p1.x - delta;
		headp1.y = p1.y - delta;
		headp2.x = p1.x + delta;
		headp2.y = p1.y + delta;
		if (headp1.x > hi->x) { hi->x = headp1.x; }
		if (headp1.x < lo->x) { lo->x = headp1.x; }
		if (headp1.y > hi->y) { hi->y = headp1.y; }
		if (headp1.y < lo->y) { lo->y = headp1.y; }
		if (headp2.x > hi->x) { hi->x = headp2.x; }
		if (headp2.x < lo->x) { lo->x = headp2.x; }
		if (headp2.y > hi->y) { hi->y = headp2.y; }
		if (headp2.y < lo->y) { lo->y = headp2.y; }
		hoffset += HEADR*2;
	}

}

static void ComputeSignalBoundingBox (track_p t )
{
	coOrd lo, hi;
	signalData_p xx = GetsignalData(t);
	SignalBoundingBox(xx->orig, xx->angle, xx->numHeads,
	                  GetScaleRatio(GetTrkScale(t)), &hi, &lo);
	SetBoundingBox(t, hi, lo);
}

static DIST_T DistanceSignal (track_p t, coOrd * p )
{
	signalData_p xx = GetsignalData(t);
	return FindDistance(xx->orig, *p);
}

static struct {
	char name[STR_SHORT_SIZE];
	coOrd pos;
	ANGLE_T orient;
	long heads;
} signalProperties;

typedef enum { NM, PS, OR, HD } signalDesc_e;
static descData_t signalDesc[] = {
	/* NM */ { DESC_STRING, N_("Name"),     &signalProperties.name, sizeof(signalProperties.name) },
	/* PS */ { DESC_POS,    N_("Position"), &signalProperties.pos },
	/* OR */ { DESC_ANGLE,  N_("Angle"),    &signalProperties.orient },
	/* HD */ { DESC_LONG,   N_("Number Of Heads"), &signalProperties.heads },
	{ DESC_NULL }
};

static void UpdateSignalProperties ( track_p trk, int inx, descData_p
                                     descUpd, BOOL_T needUndoStart )
{
	signalData_p xx = GetsignalData( trk );
	const char *thename;
	char *newName;
	BOOL_T changed, nChanged, pChanged, oChanged;

	switch (inx) {
	case NM: break;
	case PS: break;
	case OR: break;
	case HD: break;
	case -1:
		changed = nChanged = pChanged = oChanged = FALSE;
		thename = wStringGetValue( (wString_p) signalDesc[NM].control0 );
		if (strcmp(thename,xx->name) != 0) {
			nChanged = changed = TRUE;
			unsigned int max_str = signalDesc[NM].max_string;
			if (max_str && strlen(thename)>max_str) {
				newName = MyMalloc(max_str);
				newName[max_str-1] = '\0';
				strncat(newName,thename, max_str-1);
				NoticeMessage2(0, MSG_ENTERED_STRING_TRUNCATED, _("Ok"), NULL, max_str-1);
			} else { newName = MyStrdup(thename); }
		}

		if (signalProperties.pos.x != xx->orig.x ||
		    signalProperties.pos.y != xx->orig.y) {
			pChanged = changed = TRUE;
		}
		if (signalProperties.orient != xx->angle) {
			oChanged = changed = TRUE;
		}
		if (!changed) { break; }
		if (needUndoStart) {
			UndoStart( _("Change Signal"), "Change Signal" );
		}
		UndoModify( trk );
		if (nChanged) {
			MyFree(xx->name);
			xx->name = newName;
		}
		if (pChanged || oChanged) {
			UndrawNewTrack( trk );
		}
		if (pChanged) {
			xx->orig = signalProperties.pos;
		}
		if (oChanged) {
			xx->angle = signalProperties.orient;
		}
		if (pChanged || oChanged) {
			ComputeSignalBoundingBox( trk );
			DrawNewTrack( trk );
		}
		break;
	}
}


static void DescribeSignal (track_p trk, char * str, CSIZE_T len )
{
	signalData_p xx = GetsignalData(trk);

	strcpy( str, _(GetTrkTypeName( trk )) );
	str++;
	while (*str) {
		*str = tolower((unsigned char)*str);
		str++;
	}
	sprintf( str, _("(%d [%s]): Layer=%u, %d heads at %0.3f,%0.3f A%0.3f"),
	         GetTrkIndex(trk),
	         xx->name,GetTrkLayer(trk)+1, xx->numHeads,
	         xx->orig.x, xx->orig.y,xx->angle );
	strncpy(signalProperties.name,xx->name,STR_SHORT_SIZE-1);
	signalProperties.name[STR_SHORT_SIZE-1] = '\0';
	signalProperties.pos = xx->orig;
	signalProperties.orient = xx->angle;
	signalProperties.heads = xx->numHeads;
	signalDesc[HD].mode = DESC_RO;
	signalDesc[NM].mode = DESC_NOREDRAW;
	DoDescribe( _("Signal"), trk, signalDesc, UpdateSignalProperties );
}

static void DeleteSignal ( track_p trk )
{
	wIndex_t ia;
	signalData_p xx = GetsignalData(trk);
	MyFree(xx->name); xx->name = NULL;
	for (ia = 0; ia < xx->numAspects; ia++) {
		MyFree((&(xx->aspectList))[ia].aspectName);
		MyFree((&(xx->aspectList))[ia].aspectScript);
	}
}

static BOOL_T WriteSignal ( track_p t, FILE * f )
{
	BOOL_T rc = TRUE;
	wIndex_t ia;
	signalData_p xx = GetsignalData(t);
	char *signalName = MyStrdup(xx->name);

#ifdef UTFCONVERT
	signalName = Convert2UTF8(signalName);
#endif // UTFCONVERT

	rc &= fprintf(f, "SIGNAL %d %u %s %d %0.6f %0.6f %0.6f %d \"%s\"\n",
	              GetTrkIndex(t), GetTrkLayer(t), GetTrkScaleName(t),
	              GetTrkVisible(t), xx->orig.x, xx->orig.y, xx->angle,
	              xx->numHeads, signalName)>0;
	for (ia = 0; ia < xx->numAspects; ia++) {
		rc &= fprintf(f, "\tASPECT \"%s\" \"%s\"\n",
		              (&(xx->aspectList))[ia].aspectName,
		              (&(xx->aspectList))[ia].aspectScript)>0;
	}
	rc &= fprintf( f, "\t%s\n",END_SIGNAL )>0;

	MyFree(signalName);
	return rc;
}

static BOOL_T ReadSignal ( char * line )
{
	/*TRKINX_T trkindex;*/
	wIndex_t index;
	track_p trk;
	char * cp = NULL;
	wIndex_t ia;
	char *name;
	char *aspname, *aspscript;
	wIndex_t numHeads;
	coOrd orig;
	ANGLE_T angle;
	BOOL_T visible;
	char scale[10];
	wIndex_t layer;
	signalData_p xx;
	if (!GetArgs(line+6,"dLsdpfdq",&index,&layer,scale, &visible, &orig,
	             &angle, &numHeads,&name)) {
		return FALSE;
	}

#ifdef UTFCONVERT
	ConvertUTF8ToSystem(name);
#endif // UTFCONVERT

	DYNARR_RESET( signalAspect_p, signalAspect_da );
	while ( (cp = GetNextLine()) != NULL ) {
		if ( IsEND( END_SIGNAL) ) {
			break;
		}
		while (isspace((unsigned char)*cp)) { cp++; }
		if ( *cp == '\n' || *cp == '#' ) {
			continue;
		}
		if ( strncmp( cp, "ASPECT", 6 ) == 0 ) {
			if (!GetArgs(cp+4,"qq",&aspname,&aspscript)) { return FALSE; }
			DYNARR_APPEND( signalAspect_p *, signalAspect_da, 10 );
			signalAspect(signalAspect_da.cnt-1).aspectName = aspname;
			signalAspect(signalAspect_da.cnt-1).aspectScript = aspscript;
		}
	}
	trk = NewTrack(index, T_SIGNAL, 0,
	               sizeof(signalData_t)+(sizeof(signalAspect_t)*(signalAspect_da.cnt-1))+1);
	SetTrkVisible(trk, visible);
	SetTrkScale(trk, LookupScale( scale ));
	SetTrkLayer(trk, layer);
	xx = GetsignalData ( trk );
	xx->name = name;
	xx->numHeads = numHeads;
	xx->orig = orig;
	xx->angle = angle;
	xx->numAspects = signalAspect_da.cnt;
	for (ia = 0; ia < xx->numAspects; ia++) {
		(&(xx->aspectList))[ia].aspectName = signalAspect(ia).aspectName;
		(&(xx->aspectList))[ia].aspectScript = signalAspect(ia).aspectScript;
	}
	ComputeSignalBoundingBox(trk);
	return TRUE;
}

static void MoveSignal (track_p trk, coOrd orig )
{
	signalData_p xx = GetsignalData ( trk );
	xx->orig.x += orig.x;
	xx->orig.y += orig.y;
	ComputeSignalBoundingBox(trk);
}

static void RotateSignal (track_p trk, coOrd orig, ANGLE_T angle )
{
	signalData_p xx = GetsignalData ( trk );
	Rotate(&(xx->orig), orig, angle);
	xx->angle = NormalizeAngle(xx->angle + angle);
	ComputeSignalBoundingBox(trk);
}

static void RescaleSignal (track_p trk, FLOAT_T ratio )
{
}

static void FlipSignal (track_p trk, coOrd orig, ANGLE_T angle )
{
	signalData_p xx = GetsignalData ( trk );
	FlipPoint(&(xx->orig), orig, angle);
	xx->angle = NormalizeAngle(2*angle - xx->angle);
	ComputeSignalBoundingBox(trk);
}

static trackCmd_t signalCmds = {
	"SIGNAL",
	DrawSignal,
	DistanceSignal,
	DescribeSignal,
	DeleteSignal,
	WriteSignal,
	ReadSignal,
	MoveSignal,
	RotateSignal,
	RescaleSignal,
	NULL, /* audit */
	NULL, /* getAngle */
	NULL, /* split */
	NULL, /* traverse */
	NULL, /* enumerate */
	NULL, /* redraw */
	NULL, /* trim */
	NULL, /* merge */
	NULL, /* modify */
	NULL, /* getLength */
	NULL, /* getTrkParams */
	NULL, /* moveEndPt */
	NULL, /* query */
	NULL, /* ungroup */
	FlipSignal, /* flip */
	NULL, /* drawPositionIndicator */
	NULL, /* advancePositionIndicator */
	NULL, /* checkTraverse */
	NULL, /* makeParallel */
	NULL  /* drawDesc */
};

static BOOL_T signalCreate_P;
static coOrd signalEditOrig;
static ANGLE_T signalEditAngle;
static track_p signalEditTrack;

static char signalEditName[STR_SHORT_SIZE];
static long signalEditHeadCount;
static char signalAspectEditName[STR_SHORT_SIZE];
static char signalAspectEditScript[STR_LONG_SIZE];
static long signalAspectEditIndex;

static paramIntegerRange_t r1_3 = {1, 3};
static wWinPix_t aspectListWidths[] = { STR_SHORT_SIZE, 150 };
static const char * aspectListTitles[] = { N_("Name"), N_("Script") };
static paramListData_t aspectListData = {10, 400, 2, aspectListWidths, aspectListTitles};

static void AspectEdit( void * action );
static void AspectAdd( void * action );
static void AspectDelete( void * action );

static paramFloatRange_t r_1000_1000    = { -1000.0, 1000.0, 80 };
static paramFloatRange_t r0_360         = { 0.0, 360.0, 80 };
static paramData_t signalEditPLs[] = {
#define I_SIGNALNAME (0)
	/*0*/ { PD_STRING, signalEditName, "name", PDO_NOPREF|PDO_NOTBLANK, I2VP(200), N_("Name"), 0, 0, sizeof(signalEditName)},
#define I_ORIGX (1)
	/*1*/ { PD_FLOAT, &signalEditOrig.x, "origx", PDO_DIM, &r_1000_1000, N_("Origin X") },
#define I_ORIGY (2)
	/*2*/ { PD_FLOAT, &signalEditOrig.y, "origy", PDO_DIM, &r_1000_1000, N_("Origin Y") },
#define I_ANGLE (3)
	/*3*/ { PD_FLOAT, &signalEditAngle, "origa", PDO_ANGLE, &r0_360, N_("Angle") },
#define I_SIGNALHEADCOUNT (4)
	/*4*/ { PD_LONG,   &signalEditHeadCount, "headCount", PDO_NOPREF, &r1_3, N_("Number of Heads") },
#define I_SIGNALASPECTLIST (5)
#define aspectSelL ((wList_p)signalEditPLs[I_SIGNALASPECTLIST].control)
	/*5*/ { PD_LIST, NULL, "inx", PDO_DLGRESETMARGIN|PDO_DLGRESIZE, &aspectListData, NULL, BL_MANY },
#define I_SIGNALASPECTEDIT (6)
	/*6*/ { PD_BUTTON, AspectEdit, "edit", PDO_DLGCMDBUTTON, NULL, N_("Edit Aspect") },
#define I_SIGNALASPECTADD (7)
	/*7*/ { PD_BUTTON, AspectAdd, "add", PDO_DLGCMDBUTTON, NULL, N_("Add Aspect") },
#define I_SIGNALASPECTDELETE (8)
	/*8*/ { PD_BUTTON, AspectDelete, "delete", 0, NULL, N_("Delete Aspect") },
};
static paramGroup_t signalEditPG = { "signalEdit", 0, signalEditPLs, COUNT( signalEditPLs ) };
static wWin_p signalEditW;

static paramIntegerRange_t rm1_999999 = { -1, 999999 };

static paramData_t aspectEditPLs[] = {
#define I_ASPECTNAME (0)
	/*0*/ { PD_STRING, signalAspectEditName, "name", PDO_NOPREF|PDO_NOTBLANK, I2VP(200),  N_("Name"), 0, 0, sizeof(signalAspectEditName)},
#define I_ASPECTSCRIPT (1)
	/*1*/ { PD_STRING, signalAspectEditScript, "script", PDO_NOPREF, I2VP(350), N_("Script"), 0, 0, sizeof(signalAspectEditScript)},
#define I_ASPECTINDEX (2)
	/*2*/ { PD_LONG,   &signalAspectEditIndex, "index", PDO_NOPREF, &rm1_999999, N_("Aspect Index"), BO_READONLY },
};

static paramGroup_t aspectEditPG = { "aspectEdit", 0, aspectEditPLs, COUNT( aspectEditPLs ) };
static wWin_p aspectEditW;


static void SignalEditOk ( void * junk )
{
	track_p trk;
	signalData_p xx;
	wIndex_t ia;
	CSIZE_T newsize;

	if (signalCreate_P) {
		UndoStart( _("Create Signal"), "Create Signal");
		trk = NewTrack(0, T_SIGNAL, 0,
		               sizeof(signalData_t)+(sizeof(signalAspect_t)*(signalAspect_da.cnt-1))+1);
		xx = GetsignalData(trk);
	} else {
		UndoStart( _("Modify Signal"), "Modify Signal");
		trk = signalEditTrack;
		xx = GetsignalData(trk);
		if (xx->numAspects != signalAspect_da.cnt) {
			/* We need to reallocate the extra data. */
			for (ia = 0; ia < xx->numAspects; ia++) {
				MyFree((&(xx->aspectList))[ia].aspectName);
				MyFree((&(xx->aspectList))[ia].aspectScript);
				(&(xx->aspectList))[ia].aspectName = NULL;
				(&(xx->aspectList))[ia].aspectScript = NULL;
			}
			newsize = sizeof(signalData_t)+(sizeof(signalAspect_t)*(signalAspect_da.cnt-1))
			          +1;
			ResizeExtraData( trk, newsize );
			xx = GetsignalData(trk);
		}
	}
	xx->orig = signalEditOrig;
	xx->angle = signalEditAngle;
	xx->numHeads = signalEditHeadCount;
	if ( xx->name == NULL
	     || strncmp (xx->name, signalEditName, STR_SHORT_SIZE) != 0) {
		MyFree(xx->name);
		xx->name = MyStrdup(signalEditName);
	}
	xx->numAspects = signalAspect_da.cnt;
	for (ia = 0; ia < xx->numAspects; ia++) {
		if ((&(xx->aspectList))[ia].aspectName == NULL) {
			(&(xx->aspectList))[ia].aspectName = signalAspect(ia).aspectName;
		} else if (strcmp((&(xx->aspectList))[ia].aspectName,
		                  signalAspect(ia).aspectName) != 0) {
			MyFree((&(xx->aspectList))[ia].aspectName);
			(&(xx->aspectList))[ia].aspectName = signalAspect(ia).aspectName;
		} else {
			MyFree(signalAspect(ia).aspectName);
		}
		if ((&(xx->aspectList))[ia].aspectScript == NULL) {
			(&(xx->aspectList))[ia].aspectScript = signalAspect(ia).aspectScript;
		} else if (strcmp((&(xx->aspectList))[ia].aspectScript,
		                  signalAspect(ia).aspectScript) != 0) {
			MyFree((&(xx->aspectList))[ia].aspectScript);
			(&(xx->aspectList))[ia].aspectScript = signalAspect(ia).aspectScript;
		} else {
			MyFree(signalAspect(ia).aspectScript);
		}
	}
	UndoEnd();
	DoRedraw();
	ComputeSignalBoundingBox(trk);
	wHide( signalEditW );
}

static void SignalEditCancel ( wWin_p junk )
{
	wIndex_t ia;

	for (ia = 0; ia < signalAspect_da.cnt; ia++) {
		MyFree(signalAspect(ia).aspectName);
		MyFree(signalAspect(ia).aspectScript);
	}
	DYNARR_RESET( signalAspect_p, signalAspect_da );
	wHide( signalEditW );
}

static void SignalEditDlgUpdate (paramGroup_p pg, int inx, void *valueP )
{
	wIndex_t selcnt = wListGetSelectedCount( aspectSelL );

	if ( inx != I_SIGNALASPECTLIST ) { return; }
	ParamControlActive( &signalEditPG, I_SIGNALASPECTEDIT, selcnt>0 );
	ParamControlActive( &signalEditPG, I_SIGNALASPECTADD, TRUE );
	ParamControlActive( &signalEditPG, I_SIGNALASPECTDELETE, selcnt>0 );
}

static void aspectEditOK ( void * junk )
{
	if (signalAspectEditIndex < 0) {
		DYNARR_APPEND( signalAspect_p *, signalAspect_da, 10 );
		signalAspect(signalAspect_da.cnt-1).aspectName = MyStrdup(signalAspectEditName);
		signalAspect(signalAspect_da.cnt-1).aspectScript = MyStrdup(
		                        signalAspectEditScript);
		snprintf(message,sizeof(message),"%s\t%s",signalAspectEditName,
		         signalAspectEditScript);
		wListAddValue( aspectSelL, message, NULL, NULL );
	} else {
		if ( strncmp( signalAspectEditName,
		              signalAspect(signalAspectEditIndex).aspectName,STR_SHORT_SIZE ) != 0 ) {
			MyFree(signalAspect(signalAspectEditIndex).aspectName);
			signalAspect(signalAspectEditIndex).aspectName = MyStrdup(signalAspectEditName);
		}
		if ( strncmp( signalAspectEditScript,
		              signalAspect(signalAspectEditIndex).aspectScript, STR_LONG_SIZE ) != 0 ) {
			MyFree(signalAspect(signalAspectEditIndex).aspectScript);
			signalAspect(signalAspectEditIndex).aspectScript = MyStrdup(
			                        signalAspectEditScript);
		}
		snprintf(message,sizeof(message),"%s\t%s",
		         signalAspect(signalAspectEditIndex).aspectName,
		         signalAspect(signalAspectEditIndex).aspectScript);
		wListSetValues( aspectSelL, signalAspectEditIndex, message, NULL, NULL );
	}
	wHide( aspectEditW );
}

static void EditAspectDialog ( wIndex_t inx )
{
	if (inx < 0) {
		signalAspectEditName[0] = '\0';
		signalAspectEditScript[0] = '\0';
	} else {
		strncpy(signalAspectEditName,signalAspect(inx).aspectName,STR_SHORT_SIZE-1);
		signalAspectEditName[STR_SHORT_SIZE-1] = '\0';
		strncpy(signalAspectEditScript,signalAspect(inx).aspectScript,STR_LONG_SIZE-1);
		signalAspectEditScript[STR_LONG_SIZE-1] = '\0';
	}
	signalAspectEditIndex = inx;
	if ( !aspectEditW ) {
		ParamRegister( &aspectEditPG );
		aspectEditW = ParamCreateDialog (&aspectEditPG,
		                                 MakeWindowTitle(_("Edit aspect")),
		                                 _("Ok"), aspectEditOK,
		                                 ParamCancel_Current, TRUE, NULL,F_BLOCK,NULL);
	}
	ParamLoadControls( &aspectEditPG );
	wShow( aspectEditW );
}

static void AspectEdit( void * action )
{
	wIndex_t selcnt = wListGetSelectedCount( aspectSelL );
	wIndex_t inx, cnt;

	if ( selcnt != 1) { return; }
	cnt = wListGetCount( aspectSelL );
	for ( inx=0;
	      inx<cnt && wListGetItemSelected( aspectSelL, inx ) != TRUE;
	      inx++ );
	if ( inx >= cnt ) { return; }
	EditAspectDialog(inx);
}

static void AspectAdd( void * action )
{
	EditAspectDialog(-1);
}

static void MoveAspectUp (wIndex_t inx)
{
	wIndex_t cnt = signalAspect_da.cnt;
	wIndex_t ia;

	MyFree(signalAspect(inx).aspectName);
	MyFree(signalAspect(inx).aspectScript);
	for (ia = inx+1; ia < cnt; ia++) {
		signalAspect(ia-1).aspectName = signalAspect(ia).aspectName;
		signalAspect(ia-1).aspectScript = signalAspect(ia).aspectScript;
	}
	DYNARR_SET(signalAspect_t,signalAspect_da,cnt-1);
}

static void AspectDelete( void * action )
{
	wIndex_t selcnt = wListGetSelectedCount( aspectSelL );
	wIndex_t inx, cnt;

	if ( selcnt <= 0) { return; }
	if ( (!NoticeMessage2( 1, _("Are you sure you want to delete the %d aspect(s)"),
	                       _("Yes"), _("No"), selcnt ) ) ) {
		return;
	}
	cnt = wListGetCount( aspectSelL );
	for ( inx=0; inx<cnt; inx++ ) {
		if ( !wListGetItemSelected( aspectSelL, inx ) ) { continue; }
		wListDelete( aspectSelL, inx );
		MoveAspectUp(inx);
		inx--;
		cnt--;
	}
	DoChangeNotification( CHANGE_PARAMS );
}

static void EditSignalDialog()
{
	signalData_p xx;
	wIndex_t ia;

	if ( !signalEditW ) {
		ParamRegister( &signalEditPG );
		signalEditW = ParamCreateDialog (&signalEditPG,
		                                 MakeWindowTitle(_("Edit signal")),
		                                 _("Ok"), SignalEditOk,
		                                 ParamCancel_Custom( SignalEditCancel ),
		                                 TRUE, NULL,
		                                 F_RESIZE|F_RECALLSIZE|F_BLOCK,
		                                 SignalEditDlgUpdate );
	}
	if (signalCreate_P) {
		signalEditName[0] = '\0';
		signalEditHeadCount = 1;
		wListClear( aspectSelL );
		DYNARR_RESET( signalAspect_p, signalAspect_da );
	} else {
		xx = GetsignalData ( signalEditTrack );
		strncpy(signalEditName,xx->name,STR_SHORT_SIZE - 1);
		signalEditName[STR_SHORT_SIZE - 1] = '\0';
		signalEditHeadCount = xx->numHeads;
		signalEditOrig = xx->orig;
		signalEditAngle = xx->angle;
		wListClear( aspectSelL );
		DYNARR_RESET( signalAspect_p, signalAspect_da );
		for (ia = 0; ia < xx->numAspects; ia++) {
			snprintf(message,sizeof(message),"%s\t%s",(&(xx->aspectList))[ia].aspectName,
			         (&(xx->aspectList))[ia].aspectScript);
			wListAddValue( aspectSelL, message, NULL, NULL );
			DYNARR_APPEND( signalAspect_p *, signalAspect_da, 10 );
			signalAspect(signalAspect_da.cnt-1).aspectName = MyStrdup((&
			                (xx->aspectList))[ia].aspectName);
			signalAspect(signalAspect_da.cnt-1).aspectScript = MyStrdup((&
			                (xx->aspectList))[ia].aspectScript);
		}
	}
	ParamLoadControls( &signalEditPG );
	ParamControlActive( &signalEditPG, I_SIGNALASPECTEDIT, FALSE );
	ParamControlActive( &signalEditPG, I_SIGNALASPECTADD, TRUE );
	ParamControlActive( &signalEditPG, I_SIGNALASPECTDELETE, FALSE );
	wShow( signalEditW );
}




static void EditSignal (track_p trk)
{
	signalCreate_P = FALSE;
	signalEditTrack = trk;
	EditSignalDialog();
}

static void CreateNewSignal (coOrd orig, ANGLE_T angle)
{
	signalCreate_P = TRUE;
	signalEditOrig = orig;
	signalEditAngle = angle;
	EditSignalDialog();
}

static coOrd pos0;
static ANGLE_T orient;

static STATUS_T CmdSignal ( wAction_t action, coOrd pos )
{

	static BOOL_T create;
	switch (action) {
	case C_START:
		InfoMessage(_("Place base of signal"));
		create = FALSE;
		SetAllTrackSelect( FALSE );
		return C_CONTINUE;
	case C_DOWN:
		SnapPos(&pos);
		pos0 = pos;
		create = TRUE;
		InfoMessage(_("Drag to orient signal"));
		return C_CONTINUE;
	case C_MOVE:
		SnapPos(&pos);
		orient = FindAngle(pos0,pos);
		return C_CONTINUE;
	case C_UP:
		SnapPos(&pos);
		orient = FindAngle(pos0,pos);
		CreateNewSignal(pos0,orient);
		return C_TERMINATE;
	case C_REDRAW:
		if (create) {
			DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(GetLayoutCurScale()),
			             wDrawColorBlack );
		}
		return C_CONTINUE;
	case C_CANCEL:
		create = FALSE;
		return C_CONTINUE;
	default:
		return C_CONTINUE;
	}
}

static coOrd sighiliteOrig, sighiliteSize;
static POS_T sighiliteBorder;
static wDrawColor sighiliteColor = 0;
static void DrawSignalTrackHilite( void )
{
	if (sighiliteColor==0) {
		sighiliteColor = wDrawColorGray(87);
	}
	DrawRectangle( &tempD, sighiliteOrig, sighiliteSize, sighiliteColor,
	               DRAW_TRANSPARENT );
}

static int SignalMgmProc ( int cmd, void * data )
{
	track_p trk = (track_p) data;
	signalData_p xx = GetsignalData(trk);
	/*char msg[STR_SIZE];*/

	switch ( cmd ) {
	case CONTMGM_CAN_EDIT:
		return TRUE;
		break;
	case CONTMGM_DO_EDIT:
		EditSignal(trk);
		return TRUE;
		break;
	case CONTMGM_CAN_DELETE:
		return TRUE;
		break;
	case CONTMGM_DO_DELETE:
		DeleteTrack(trk, FALSE);
		return TRUE;
		break;
	case CONTMGM_DO_HILIGHT:
		if (!xx->IsHilite) {
			sighiliteBorder = mainD.scale*0.1;
			if ( sighiliteBorder < trackGauge ) { sighiliteBorder = trackGauge; }
			GetBoundingBox( trk, &sighiliteSize, &sighiliteOrig );
			sighiliteOrig.x -= sighiliteBorder;
			sighiliteOrig.y -= sighiliteBorder;
			sighiliteSize.x -= sighiliteOrig.x-sighiliteBorder;
			sighiliteSize.y -= sighiliteOrig.y-sighiliteBorder;
			DrawSignalTrackHilite();
			xx->IsHilite = TRUE;
		}
		break;
	case CONTMGM_UN_HILIGHT:
		if (xx->IsHilite) {
			sighiliteBorder = mainD.scale*0.1;
			if ( sighiliteBorder < trackGauge ) { sighiliteBorder = trackGauge; }
			GetBoundingBox( trk, &sighiliteSize, &sighiliteOrig );
			sighiliteOrig.x -= sighiliteBorder;
			sighiliteOrig.y -= sighiliteBorder;
			sighiliteSize.x -= sighiliteOrig.x-sighiliteBorder;
			sighiliteSize.y -= sighiliteOrig.y-sighiliteBorder;
			DrawSignalTrackHilite();
			xx->IsHilite = FALSE;
		}
		break;
	case CONTMGM_GET_TITLE:
		sprintf(message,"\t%s\t",xx->name);
		break;
	}
	return FALSE;
}

#include "bitmaps/signal.image3"

EXPORT void SignalMgmLoad ( void )
{
	track_p trk;
	static wIcon_p signalI = NULL;

	if (signalI == NULL) {
		signalI = wIconCreatePixMap( signal_image3[iconSize] );
	}

	TRK_ITERATE(trk) {
		if (GetTrkType(trk) != T_SIGNAL) { continue; }
		ContMgmLoad (signalI, SignalMgmProc, trk );
	}
}

#define ACCL_SIGNAL 0

EXPORT void InitCmdSignal ( wMenu_p menu )
{
	AddMenuButton( menu, CmdSignal, "cmdSignal", _("Signal"),
	               wIconCreatePixMap( signal_image3[iconSize] ), LEVEL0_50, IC_STICKY|IC_POPUP2,
	               ACCL_SIGNAL, NULL );
}

EXPORT void InitTrkSignal ( void )
{
	T_SIGNAL = InitObject ( &signalCmds );
	log_signal = LogFindIndex ( "signal" );
}
