/** \file cstruct.c
 * T_STRUCTURE
 */

/*  XTrkCad - Model Railroad CAD
 *  Copyright (C) 2005 Dave Bullis
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <string.h>

#include "compound.h"
#include "cundo.h"
#include "custom.h"
#include "fileio.h"
#include "i18n.h"
#include "layout.h"
#include "messages.h"
#include "param.h"
#include "track.h"
#include "utility.h"

EXPORT TRKTYP_T T_STRUCTURE = -1;

#define STRUCTCMD

EXPORT dynArr_t structureInfo_da;

typedef struct compoundData extraData;


static wIndex_t pierListInx;
EXPORT turnoutInfo_t * curStructure = NULL;
static int log_structure = 0;

static wMenu_p structPopupM;

#ifdef STRUCTCMD
static drawCmd_t structureD = {
		NULL,
		&screenDrawFuncs,
		0,
		1.0,
		0.0,
		{0.0,0.0}, {0.0,0.0},
		Pix2CoOrd, CoOrd2Pix };

static wIndex_t structureHotBarCmdInx;
static wIndex_t structureInx;
static long hideStructureWindow;
static void RedrawStructure(void);

static wPos_t structureListWidths[] = { 80, 80, 220 };
static const char * structureListTitles[] = { N_("Manufacturer"), N_("Part No"), N_("Description") };
static paramListData_t listData = { 13, 400, 3, structureListWidths, structureListTitles };
static const char * hideLabels[] = { N_("Hide"), NULL };
static paramDrawData_t structureDrawData = { 490, 200, (wDrawRedrawCallBack_p)RedrawStructure, NULL, &structureD };
static paramData_t structurePLs[] = {
#define I_LIST	(0)
#define structureListL	((wList_p)structurePLs[I_LIST].control)
	{	PD_LIST, &structureInx, "list", PDO_NOPREF|PDO_DLGRESIZEW, &listData, NULL, BL_DUP },
#define I_DRAW	(1)
	{	PD_DRAW, NULL, "canvas", PDO_NOPSHUPD|PDO_DLGUNDERCMDBUTT|PDO_DLGRESIZE, &structureDrawData, NULL, 0 },
#define I_HIDE	(2)
	{	PD_TOGGLE, &hideStructureWindow, "hide", PDO_DLGCMDBUTTON, /*CAST_AWAY_CONST*/(void*)hideLabels, NULL, BC_NOBORDER },
#define I_MSGSCALE		(3)
	{	PD_MESSAGE, NULL, NULL, 0, (void*)80 },
#define I_MSGWIDTH		(4)
	{	PD_MESSAGE, NULL, NULL, 0, (void*)80 },
#define I_MSGHEIGHT		(5)
	{	PD_MESSAGE, NULL, NULL, 0, (void*)80 } };
static paramGroup_t structurePG = { "structure", 0, structurePLs, sizeof structurePLs/sizeof structurePLs[0] };
#endif


/****************************************
 *
 * STRUCTURE LIST MANAGEMENT
 *
 */




EXPORT turnoutInfo_t * CreateNewStructure(
		char * scale,
		char * title,
		wIndex_t segCnt,
		trkSeg_p segData,
		BOOL_T updateList )
{
	turnoutInfo_t * to;
#ifdef REORIGSTRUCT
	coOrd orig;
#endif

	if (segCnt == 0)
		return NULL; 
	to = FindCompound( FIND_STRUCT, scale, title );
	if (to == NULL) {
		DYNARR_APPEND( turnoutInfo_t *, structureInfo_da, 10 );
		to = (turnoutInfo_t*)MyMalloc( sizeof *to );
		structureInfo(structureInfo_da.cnt-1) = to;
		to->title = MyStrdup( title );
		to->scaleInx = LookupScale( scale );
	}
	to->segCnt = segCnt;
	to->segs = (trkSeg_p)memdup( segData, (sizeof *segData) * segCnt );
	CopyPoly(to->segs,segCnt);
	GetSegBounds( zero, 0.0, to->segCnt, to->segs, &to->orig, &to->size );
#ifdef REORIGSTRUCT
	GetSegBounds( zero, 0.0, to->segCnt, to->segs, &orig, &to->size );
	orig.x = - orig.x;
	orig.y = - orig.y;
	MoveSegs( to->segCnt, to->segs, orig );
	to->orig = zero;
#endif
	to->paramFileIndex = curParamFileIndex;
	if (curParamFileIndex == PARAM_CUSTOM)
		to->contentsLabel = "Custom Structures";
	else
		to->contentsLabel = curSubContents;
	to->endCnt = 0;
	to->pathLen = 0;
	to->paths = (PATHPTR_T)"";
#ifdef STRUCTCMD
	if (updateList && structureListL != NULL) {
		FormatCompoundTitle( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, to->title );
		if (message[0] != '\0')
			wListAddValue( structureListL, message, NULL, to );
	}
#endif

	to->barScale = curBarScale>0?curBarScale:-1;
	return to;
}


static BOOL_T ReadStructureParam(
		char * firstLine )
{
	char scale[10];
	char *title;
	turnoutInfo_t * to;
	char * cp;
static dynArr_t pierInfo_da;
#define pierInfo(N) DYNARR_N( pierInfo_t, pierInfo_da, N )

	if ( !GetArgs( firstLine+10, "sq", scale, &title ) )
		return FALSE;
	ReadSegs();
	to = CreateNewStructure( scale, title, tempSegs_da.cnt, &tempSegs(0), FALSE );
	if (to == NULL)
		return FALSE;
	if (tempSpecial[0] != '\0') {
		if (strncmp( tempSpecial, PIER, strlen(PIER) ) == 0) {
			DYNARR_RESET( pierInfo_t, pierInfo_da );
			to->special = TOpierInfo;
			cp = tempSpecial+strlen(PIER);
			while (cp) {
				DYNARR_APPEND( pierInfo_t, pierInfo_da, 10 );
				GetArgs( cp, "fqc", &pierInfo(pierInfo_da.cnt-1).height, &pierInfo(pierInfo_da.cnt-1).name, &cp );
			}
			to->u.pierInfo.cnt = pierInfo_da.cnt;
			to->u.pierInfo.info = (pierInfo_t*)MyMalloc( pierInfo_da.cnt * sizeof *(pierInfo_t*)NULL );
			memcpy( to->u.pierInfo.info, &pierInfo(0), pierInfo_da.cnt * sizeof *(pierInfo_t*)NULL );
		} else {
				InputError("Unknown special case", TRUE);
		}
	}
	if (tempCustom[0] != '\0') {
		to->customInfo = MyStrdup( tempCustom );
	}
	MyFree( title );
	return TRUE;
}


EXPORT turnoutInfo_t * StructAdd( long mode, SCALEINX_T scale, wList_p list, coOrd * maxDim )
{
	wIndex_t inx;
	turnoutInfo_t * to, *to1=NULL;
	for ( inx = 0; inx < structureInfo_da.cnt; inx++ ) {
		to = structureInfo(inx);
		if ( IsParamValid(to->paramFileIndex) &&
			 to->segCnt > 0 &&
			 CompatibleScale( FALSE, to->scaleInx, scale ) &&
			 to->segCnt != 0 ) {
			if (to1 == NULL)
				to1 = to;
			FormatCompoundTitle( mode, to->title );
			if (message[0] != '\0') {
				wListAddValue( list, message, NULL, to );
				if (maxDim) {
					if (to->size.x > maxDim->x) 
						maxDim->x = to->size.x;
					if (to->size.y > maxDim->y) 
					  maxDim->y = to->size.y;
				}
			}
		}
	}
	return to1;
}


/****************************************
 *
 * GENERIC FUNCTIONS
 *
 */

static void DrawStructure(
		track_p t,
		drawCmd_p d,
		wDrawColor color )
{
	struct extraData *xx = GetTrkExtraData(t);
	coOrd p00, px0, pxy, p0y, orig, size;

	if (d->options&DC_QUICK) {
		GetSegBounds( zero, 0.0, xx->segCnt, xx->segs, &orig, &size );
		p00.x = p0y.x = orig.x;
		p00.y = px0.y = orig.y;
		px0.x = pxy.x = orig.x + size.x;
		p0y.y = pxy.y = orig.y + size.y;
		REORIGIN1( p00, xx->angle, xx->orig )
		REORIGIN1( px0, xx->angle, xx->orig )
		REORIGIN1( p0y, xx->angle, xx->orig )
		REORIGIN1( pxy, xx->angle, xx->orig )
		DrawLine( d, p00, px0, 0, color );
		DrawLine( d, px0, pxy, 0, color );
		DrawLine( d, pxy, p0y, 0, color );
		DrawLine( d, p0y, p00, 0, color );
	} else {
		DrawSegs( d, xx->orig, xx->angle, xx->segs, xx->segCnt, 0.0, color );
		if ( ((d->funcs->options&wDrawOptTemp)==0) &&
			 (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) &&
			 labelScale >= d->scale &&
			 ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) {
			DrawCompoundDescription( t, d, color );
		}
	}
}


static void ReadStructure(
		char * line )
{
	ReadCompound( line+10, T_STRUCTURE );
}


static ANGLE_T GetAngleStruct(
		track_p trk,
		coOrd pos,
		EPINX_T * ep0,
		EPINX_T * ep1 )
{
	struct extraData * xx = GetTrkExtraData(trk);
	ANGLE_T angle;

	pos.x -= xx->orig.x;
	pos.y -= xx->orig.y;
	Rotate( &pos, zero, -xx->angle );
	angle = GetAngleSegs( xx->segCnt, xx->segs, &pos, NULL, NULL, NULL, NULL, NULL);
	if ( ep0 ) *ep0 = -1;
	if ( ep1 ) *ep1 = -1;
	return NormalizeAngle( angle+xx->angle );
}


static BOOL_T QueryStructure( track_p trk, int query )
{
	switch ( query ) {
	case Q_HAS_DESC:
		return TRUE;
	default:
		return FALSE;
	}
}


static trackCmd_t structureCmds = {
		"STRUCTURE",
		DrawStructure,
		DistanceCompound,
		DescribeCompound,
		DeleteCompound,
		WriteCompound,
		ReadStructure,
		MoveCompound,
		RotateCompound,
		RescaleCompound,
		NULL,
		GetAngleStruct,
		NULL,	/* split */
		NULL,	/* traverse */
		EnumerateCompound,
		NULL,	/* redraw */
		NULL,	/* trim */
		NULL,	/* merge */
		NULL,	/* modify */
		NULL,	/* getLength */
		NULL,	/* getTrkParams */
		NULL,	/* moveEndPt */
		QueryStructure,
		UngroupCompound,
		FlipCompound };

static paramData_t pierPLs[] = {
	{	PD_DROPLIST, &pierListInx, "inx", 0, (void*)50, N_("Pier Number") } };
static paramGroup_t pierPG = { "structure-pier", 0, pierPLs, sizeof pierPLs/sizeof pierPLs[0] };
#define pierL ((wList_p)pierPLs[0].control)

static void ShowPierL( void )
{
	int inx;
	wIndex_t currInx;
	wControl_p controls[2];
	char * labels[1];

	if ( curStructure->special==TOpierInfo && curStructure->u.pierInfo.cnt > 1) {
		if (pierL == NULL) {
			ParamCreateControls( &pierPG, NULL );
		}
		currInx = wListGetIndex( pierL );
		wListClear( pierL );
		for (inx=0;inx<curStructure->u.pierInfo.cnt; inx++) {
			wListAddValue( pierL, curStructure->u.pierInfo.info[inx].name, NULL, NULL );
		}
		if ( currInx < 0 )
		   currInx = 0;
		if ( currInx >= curStructure->u.pierInfo.cnt )
		   currInx = curStructure->u.pierInfo.cnt-1;
		wListSetIndex( pierL, currInx );
		controls[0] = (wControl_p)pierL;
		controls[1] = NULL;
		labels[0] = N_("Pier Number");
		InfoSubstituteControls( controls, labels );
	} else {
		InfoSubstituteControls( NULL, NULL );
	}
}


#ifdef STRUCTCMD
/*****************************************
 *
 *	 Structure Dialog
 *
 */

static void NewStructure();
static coOrd maxStructureDim;
static wWin_p structureW;


static void RescaleStructure( void )
{
	DIST_T xscale, yscale;
	wPos_t ww, hh;
	DIST_T w, h;
	wDrawGetSize( structureD.d, &ww, &hh );
	w = ww/structureD.dpi - 0.2;
	h = hh/structureD.dpi - 0.2;
	if (curStructure) {
		xscale = curStructure->size.x/w;
		yscale = curStructure->size.y/h;
	} else {
		xscale = yscale = 0;
	}
	structureD.scale = ceil(max(xscale,yscale));
	structureD.size.x = (w+0.2)*structureD.scale;
	structureD.size.y = (h+0.2)*structureD.scale;
	return;
}


static void structureChange( long changes )
{
	static char * lastScaleName = NULL;
	if (structureW == NULL)
		return;
	wListSetIndex( structureListL, 0 );
	if ( (!wWinIsVisible(structureW)) ||
	   ( ((changes&CHANGE_SCALE) == 0  || lastScaleName == curScaleName) &&
		  (changes&CHANGE_PARAMS) == 0 ) )
		return;
	lastScaleName = curScaleName;
	curStructure = NULL;
	wControlShow( (wControl_p)structureListL, FALSE );
	wListClear( structureListL );
	maxStructureDim.x = maxStructureDim.y = 0.0;
	if (structureInfo_da.cnt <= 0)
		return;
	curStructure = StructAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, GetLayoutCurScale(), structureListL, &maxStructureDim );
	wControlShow( (wControl_p)structureListL, TRUE );
	if (curStructure == NULL) {
		wDrawClear( structureD.d );
		return;
	}
	maxStructureDim.x += 2*trackGauge;
	maxStructureDim.y += 2*trackGauge;
	/*RescaleStructure();*/
	RedrawStructure();
	return;
}



static void RedrawStructure()
{
	RescaleStructure();
LOG( log_structure, 2, ( "SelStructure(%s)\n", (curStructure?curStructure->title:"<NULL>") ) )
	wDrawClear( structureD.d );
	if (curStructure == NULL) {
		return;
	}
	structureD.orig.x = -0.10*structureD.scale + curStructure->orig.x;
	structureD.orig.y = (curStructure->size.y + curStructure->orig.y) - structureD.size.y + trackGauge;
	DrawSegs( &structureD, zero, 0.0, curStructure->segs, curStructure->segCnt,
					 0.0, wDrawColorBlack );
	sprintf( message, _("Scale %d:1"), (int)structureD.scale );
	ParamLoadMessage( &structurePG, I_MSGSCALE, message );
	sprintf( message, _("Width %s"), FormatDistance(curStructure->size.x) );
	ParamLoadMessage( &structurePG, I_MSGWIDTH, message );
	sprintf( message, _("Height %s"), FormatDistance(curStructure->size.y) );
	ParamLoadMessage( &structurePG, I_MSGHEIGHT, message );
}


static void StructureDlgUpdate(
		paramGroup_p pg,
		int inx,
		void * valueP )
{
	turnoutInfo_t * to;
	if ( inx != I_LIST ) return;
	to = (turnoutInfo_t*)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP );
	NewStructure();
	curStructure = to;
	ShowPierL();
	RedrawStructure();
	ParamDialogOkActive( &structurePG, FALSE );
}


static void DoStructOk( void )
{
	NewStructure();
	Reset();
}

#endif

/****************************************
 *
 * GRAPHICS COMMANDS
 *
 */

/*
 * STATE INFO
 */
static struct {
		int state;
		coOrd pos;
		ANGLE_T angle;
		} Dst;

static track_p pierTrk;
static EPINX_T pierEp;

static ANGLE_T PlaceStructure(
		coOrd p0,
		coOrd p1,
		coOrd origPos,
		coOrd * resPos,
		ANGLE_T * resAngle )
{
	coOrd p2 = p1;
	if (curStructure->special == TOpierInfo) {
		pierTrk = OnTrack( &p1, FALSE, TRUE );
		if (pierTrk != NULL) {
			if (GetTrkType(pierTrk) == T_TURNOUT) {
				pierEp = PickEndPoint( p1, pierTrk );
				if (pierEp >= 0) {
					*resPos = GetTrkEndPos(pierTrk, pierEp);
					*resAngle = NormalizeAngle(GetTrkEndAngle(pierTrk, pierEp)-90.0);
					return TRUE;
				}
			}
			*resAngle = NormalizeAngle(GetAngleAtPoint( pierTrk, p1, NULL, NULL )+90.0);
			if ( NormalizeAngle( FindAngle( p1, p2 ) - *resAngle + 90.0 ) > 180.0 )
				*resAngle = NormalizeAngle( *resAngle + 180.0 );
			*resPos = p1;
			return TRUE;
		}
	}
	resPos->x = origPos.x + p1.x - p0.x;
	resPos->y = origPos.y + p1.y - p0.y;
	return FALSE;
}


static void NewStructure( void )
{
	track_p trk;
	struct extraData *xx;
	wIndex_t titleLen;
	wIndex_t pierInx;

	if (curStructure->segCnt < 1) {
		AbortProg( "newStructure: bad cnt" );
	}
	if (Dst.state == 0)
		return;
	if (curStructure->special == TOpierInfo &&
		curStructure->u.pierInfo.cnt>1 &&
		wListGetIndex(pierL) == -1) {
		return;
	}
	DrawSegs( &tempD, Dst.pos, Dst.angle,
		curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
	UndoStart( _("Place Structure"), "newStruct" );
	titleLen = strlen( curStructure->title );
	trk = NewCompound( T_STRUCTURE, 0, Dst.pos, Dst.angle, curStructure->title, 0, NULL, 0, "", curStructure->segCnt, curStructure->segs );
	xx = GetTrkExtraData(trk);
#ifdef LATER
	trk = NewTrack( 0, T_STRUCTURE, 0, sizeof (*xx) + 1 );
	xx->orig = Dst.pos;
	xx->angle = Dst.angle;
	xx->segs = MyMalloc( (curStructure->segCnt)*sizeof curStructure->segs[0] );

	/*
	 * copy data */
	xx->segCnt = curStructure->segCnt;
	memcpy( xx->segs, curStructure->segs, xx->segCnt * sizeof *(trkSeg_p)0 );
	xx->title = curStructure->title;
	xx->pathLen = 0;
	xx->paths = "";
#endif
	switch(curStructure->special) {
		case TOnormal:
			xx->special = TOnormal;
			break;
		case TOpierInfo:
			xx->special = TOpier;
			if (curStructure->u.pierInfo.cnt>1) {
				pierInx = wListGetIndex(pierL);
				if (pierInx < 0 || pierInx >= curStructure->u.pierInfo.cnt)
					pierInx = 0;
			} else {
				pierInx = 0;
			}
			xx->u.pier.height = curStructure->u.pierInfo.info[pierInx].height;
			xx->u.pier.name = curStructure->u.pierInfo.info[pierInx].name;
			if (pierTrk != NULL && xx->u.pier.height >= 0 ) {
				UpdateTrkEndElev( pierTrk, pierEp, ELEV_DEF, xx->u.pier.height, NULL );
			}
			break;
		default:
			AbortProg("bad special");
	}
		
	SetTrkVisible( trk, TRUE );
#ifdef LATER
	ComputeCompoundBoundingBox( trk );

	SetDescriptionOrig( trk );
	xx->descriptionOff = zero;
	xx->descriptionSize = zero;
#endif

	DrawNewTrack( trk );
	/*DrawStructure( trk, &mainD, wDrawColorBlack, 0 );*/

	UndoEnd();
	Dst.state = 0;
	Dst.angle = 0.0;
}


static void StructRotate( void * pangle )
{
	ANGLE_T angle = (ANGLE_T)(long)pangle;
	if (Dst.state == 1)
		DrawSegs( &tempD, Dst.pos, Dst.angle,
			curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
	else
		Dst.pos = cmdMenuPos;
	Rotate( &Dst.pos, cmdMenuPos, angle );
	Dst.angle += angle;
	DrawSegs( &tempD, Dst.pos, Dst.angle,
		curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
	Dst.state = 1;
}


EXPORT STATUS_T CmdStructureAction(
		wAction_t action,
		coOrd pos )
{

	ANGLE_T angle;
	static BOOL_T validAngle;
	static ANGLE_T baseAngle;
	static coOrd origPos;
	static ANGLE_T origAngle;
	static coOrd rot0, rot1;

	switch (action & 0xFF) {

	case C_START:
		Dst.state = 0;
		Dst.angle = 00.0;
		ShowPierL();
		return C_CONTINUE;

	case C_DOWN:
		if ( curStructure == NULL ) return C_CONTINUE;
		ShowPierL();
		if (Dst.state == 1) {
			DrawSegs( &tempD, Dst.pos, Dst.angle,
					curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		} else {
			Dst.pos = pos;
		}
		rot0 = pos;
		origPos = Dst.pos;
		PlaceStructure( rot0, pos, origPos, &Dst.pos, &Dst.angle );
		Dst.state = 1;
		DrawSegs( &tempD, Dst.pos, Dst.angle,
					curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		InfoMessage( _("Drag to place") );
		return C_CONTINUE;

	case C_MOVE:
		if ( curStructure == NULL ) return C_CONTINUE;
		DrawSegs( &tempD, Dst.pos, Dst.angle,
				curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		PlaceStructure( rot0, pos, origPos, &Dst.pos, &Dst.angle );
		DrawSegs( &tempD, Dst.pos, Dst.angle,
				curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
        MainRedraw();
        MapRedraw();
		InfoMessage( "[ %0.3f %0.3f ]", pos.x - origPos.x, pos.y - origPos.y );
		return C_CONTINUE;

	case C_RDOWN:
		if ( curStructure == NULL ) return C_CONTINUE;
		if (Dst.state == 1)
			DrawSegs( &tempD, Dst.pos, Dst.angle,
					curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		else
			Dst.pos = pos;
		rot0 = rot1 = pos;
		DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack );
		Dst.state = 1;
		DrawSegs( &tempD, Dst.pos, Dst.angle,
					curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		origPos = Dst.pos;
		origAngle = Dst.angle;
		InfoMessage( _("Drag to rotate") );
		validAngle = FALSE;
		return C_CONTINUE;

	case C_RMOVE:
		if ( curStructure == NULL ) return C_CONTINUE;
		DrawSegs( &tempD, Dst.pos, Dst.angle,
				curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack );
		rot1 = pos;
		if ( FindDistance( rot0, rot1 ) > (6.0/75.0)*mainD.scale ) {
			angle = FindAngle( rot0, rot1 );
			if (!validAngle) {
				baseAngle = angle;
				validAngle = TRUE;
			}
			angle -= baseAngle;
			Dst.pos = origPos;
			Dst.angle = NormalizeAngle( origAngle + angle );
			Rotate( &Dst.pos, rot0, angle );
		}
		InfoMessage( _("Angle = %0.3f"), Dst.angle );
		DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack );
		DrawSegs( &tempD, Dst.pos, Dst.angle,
				curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		return C_CONTINUE;
		
	case C_RUP:
		DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack );
	case C_UP:
        MainRedraw();
        MapRedraw();
		return C_CONTINUE;

	case C_CMDMENU:
		if ( structPopupM == NULL ) {
			structPopupM = MenuRegister( "Structure Rotate" );
			AddRotateMenu( structPopupM, StructRotate );
		}
		wMenuPopupShow( structPopupM );
		return C_CONTINUE;

	case C_REDRAW:
		if (Dst.state == 1)
			DrawSegs( &tempD, Dst.pos, Dst.angle,
				curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		return C_CONTINUE;

	case C_CANCEL:
		if (Dst.state == 1)
			DrawSegs( &tempD, Dst.pos, Dst.angle,
				curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
		Dst.state = 0;
		InfoSubstituteControls( NULL, NULL );
		HotBarCancel();
		/*wHide( newTurn.reg.win );*/
		return C_TERMINATE;

	case C_TEXT:
		if ((action>>8) != ' ')
			return C_CONTINUE;
	case C_OK:
		NewStructure();
		InfoSubstituteControls( NULL, NULL );
		return C_TERMINATE;

	case C_FINISH:
		if (Dst.state != 0)
			CmdStructureAction( C_OK, pos );
		else
			CmdStructureAction( C_CANCEL, pos );
		return C_TERMINATE;

	default:
		return C_CONTINUE;
	}
}


static STATUS_T CmdStructure(
		wAction_t action,
		coOrd pos )
{

	wIndex_t structureIndex;
	turnoutInfo_t * structurePtr;

	switch (action & 0xFF) {

	case C_START:
		if (structureW == NULL) {
			structureW = ParamCreateDialog( &structurePG, MakeWindowTitle(_("Structure")), _("Ok"), (paramActionOkProc)DoStructOk, (paramActionCancelProc)Reset, TRUE, NULL, F_RESIZE, StructureDlgUpdate );
			RegisterChangeNotification( structureChange );
		}
		ParamDialogOkActive( &structurePG, FALSE );
		structureIndex = wListGetIndex( structureListL );
		structurePtr = curStructure;
		wShow( structureW );
		structureChange( CHANGE_PARAMS );
		if (curStructure == NULL) {
			NoticeMessage( MSG_STRUCT_NO_STRUCTS, _("Ok"), NULL );
			return C_TERMINATE;
		}
		if (structureIndex > 0 && structurePtr) {
			curStructure = structurePtr;
			wListSetIndex( structureListL, structureIndex );
			RedrawStructure();
		}
		InfoMessage( _("Select Structure and then drag to place"));
		ParamLoadControls( &structurePG );
		ParamGroupRecord( &structurePG );
		return CmdStructureAction( action, pos );

	case C_DOWN:
	case C_RDOWN:
		ParamDialogOkActive( &structurePG, TRUE );
		if (hideStructureWindow)
			wHide( structureW );
	case C_MOVE:
	case C_RMOVE:
		return CmdStructureAction( action, pos );
		
	case C_RUP:
	case C_UP:
		if (hideStructureWindow)
			wShow( structureW );
		InfoMessage( _("Left drag to move, right drag to rotate, or press Return or click Ok to finalize") );
		return CmdStructureAction( action, pos );
		return C_CONTINUE;

	case C_CANCEL:
		wHide( structureW );
	case C_TEXT:
	case C_OK:
	case C_FINISH:
	case C_CMDMENU:
	case C_REDRAW:
		return CmdStructureAction( action, pos );

	default:
		return C_CONTINUE;
	}
}



static char * CmdStructureHotBarProc(
		hotBarProc_e op,
		void * data,
		drawCmd_p d,
		coOrd * origP )
{
	turnoutInfo_t * to = (turnoutInfo_t*)data;
	switch ( op ) {
	case HB_SELECT:
		CmdStructureAction( C_FINISH, zero );
		curStructure = to;
		DoCommandB( (void*)(intptr_t)structureHotBarCmdInx );
		return NULL;
	case HB_LISTTITLE:
		FormatCompoundTitle( listLabels, to->title );
		if (message[0] == '\0')
			FormatCompoundTitle( listLabels|LABEL_DESCR, to->title );
		return message;
	case HB_BARTITLE:
		FormatCompoundTitle( hotBarLabels<<1, to->title );
		return message;
	case HB_FULLTITLE:
		return to->title;
	case HB_DRAW:
		DrawSegs( d, *origP, 0.0, to->segs, to->segCnt, trackGauge, wDrawColorBlack );
		return NULL;
	}
	return NULL;
}


EXPORT void AddHotBarStructures( void )
{
	wIndex_t inx;
	turnoutInfo_t * to;
	for ( inx=0; inx < structureInfo_da.cnt; inx ++ ) {
		to = structureInfo(inx);
		if ( !( IsParamValid(to->paramFileIndex) &&
			    to->segCnt > 0 &&
			    CompatibleScale( FALSE, to->scaleInx, GetLayoutCurScale()) ) )
			 /*( (strcmp( to->scale, "*" ) == 0 && strcasecmp( curScaleName, "DEMO" ) != 0 ) ||
			   strncasecmp( to->scale, curScaleName, strlen(to->scale) ) == 0 ) ) )*/
				continue;
		AddHotBarElement( to->contentsLabel, to->size, to->orig, FALSE, to->barScale, to, CmdStructureHotBarProc );
	}
}

static STATUS_T CmdStructureHotBar(
		wAction_t action,
		coOrd pos )
{
	switch (action & 0xFF) {

	case C_START:
		structureChange( CHANGE_PARAMS );
		if (curStructure == NULL) {
			NoticeMessage( MSG_STRUCT_NO_STRUCTS, _("Ok"), NULL );
			return C_TERMINATE;
		}
		FormatCompoundTitle( listLabels|LABEL_DESCR, curStructure->title );
		InfoMessage( _("Place %s and draw into position"), message );
		ParamLoadControls( &structurePG );
		ParamGroupRecord( &structurePG );
		return CmdStructureAction( action, pos );

	case C_RUP:
	case C_UP:
		InfoMessage( _("Left drag to move, right drag to rotate, or press Return or click Ok to finalize") );
		return CmdStructureAction( action, pos );

	case C_TEXT:
		if ((action>>8) != ' ')
			return C_CONTINUE;
	case C_OK:
		CmdStructureAction( action, pos );
		return C_CONTINUE;

	case C_CANCEL:
		HotBarCancel();
	default:
		return CmdStructureAction( action, pos );
	}
}


#ifdef STRUCTCMD
#include "bitmaps/struct.xpm"

EXPORT void InitCmdStruct( wMenu_p menu )
{
	AddMenuButton( menu, CmdStructure, "cmdStructure", _("Structure"), wIconCreatePixMap(struct_xpm), LEVEL0_50, IC_STICKY|IC_CMDMENU|IC_POPUP2, ACCL_STRUCTURE, NULL );
	structureHotBarCmdInx = AddMenuButton( menu, CmdStructureHotBar, "cmdStructureHotBar", "", NULL, LEVEL0_50, IC_STICKY|IC_CMDMENU|IC_POPUP2, 0, NULL );
	ParamRegister( &structurePG );
}
#endif


EXPORT void InitTrkStruct( void )
{
	T_STRUCTURE = InitObject( &structureCmds );

	log_structure = LogFindIndex( "Structure" );
	AddParam( "STRUCTURE ", ReadStructureParam );
	ParamRegister( &pierPG );
}