/** \file cblock.c
 * Implement blocks: a group of trackwork with a single occ. detector
 */
/* Created by Robert Heller on Thu Mar 12 09:43:02 2009
 * ------------------------------------------------------------------
 * Modification History: $Log: not supported by cvs2svn $
 * Modification History: Revision 1.4  2009/09/16 18:32:24  m_fischer
 * Modification History: Remove unused locals
 * Modification History:
 * Modification History: Revision 1.3  2009/09/05 16:40:53  m_fischer
 * Modification History: Make layout control commands a build-time choice
 * Modification History:
 * Modification History: Revision 1.2  2009/07/08 19:13:58  m_fischer
 * Modification History: Make compile under MSVC
 * Modification History:
 * Modification History: Revision 1.1  2009/07/08 18:40:27  m_fischer
 * Modification History: Add switchmotor and block for layout control
 * Modification History:
 * Modification History: Revision 1.1  2002/07/28 14:03:50  heller
 * Modification History: Add it copyright notice headers
 * Modification History:
 * ------------------------------------------------------------------
 * Contents:
 * ------------------------------------------------------------------
 *
 *     Generic Project
 *     Copyright (C) 2005  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
 *
 *  T_BLOCK
 * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cblock.c,v 1.5 2009-11-23 19:46:16 rheller Exp $
 */

#include "common.h"
#include "compound.h"
#include "cundo.h"
#include "custom.h"
#include "fileio.h"
#include "param.h"
#include "track.h"
#include "trkendpt.h"
#include "common-ui.h"

#ifdef UTFCONVERT
#include "include/utf8convert.h"
#endif // UTFCONVERT

EXPORT TRKTYP_T T_BLOCK = -1;

static int log_block = 0;

static void NoDrawLine(drawCmd_p d, coOrd p0, coOrd p1, wDrawWidth width,
                       wDrawColor color ) {}
static void NoDrawArc(drawCmd_p d, coOrd p, DIST_T r, ANGLE_T angle0,
                      ANGLE_T angle1, BOOL_T drawCenter, wDrawWidth width,
                      wDrawColor color ) {}
static void NoDrawString( drawCmd_p d, coOrd p, ANGLE_T a, char * s,
                          wFont_p fp, FONTSIZE_T fontSize, wDrawColor color ) {}
static void NoDrawBitMap( drawCmd_p d, coOrd p, wDrawBitMap_p bm,
                          wDrawColor color) {}
static void NoDrawPoly( drawCmd_p d, int cnt, coOrd * pts, int * types,
                        wDrawColor color, wDrawWidth width, drawFill_e eFillOpt ) {}
static void NoDrawFillCircle( drawCmd_p d, coOrd p, DIST_T r,
                              wDrawColor color ) {}
static void NoDrawRectangle( drawCmd_p d, coOrd orig, coOrd size,
                             wDrawColor color, drawFill_e eFill ) {}

static drawFuncs_t noDrawFuncs = {
	NoDrawLine,
	NoDrawArc,
	NoDrawString,
	NoDrawBitMap,
	NoDrawPoly,
	NoDrawFillCircle,
	NoDrawRectangle
};

static drawCmd_t blockD = {
	NULL,
	&noDrawFuncs,
	0,
	1.0,
	0.0,
	{0.0,0.0}, {0.0,0.0},
	Pix2CoOrd, CoOrd2Pix
};

static char blockName[STR_SHORT_SIZE];
static char blockScript[STR_LONG_SIZE];
static long blockElementCount;
static track_p first_block;
static track_p last_block;

static paramData_t blockPLs[] = {
	/*0*/ { PD_STRING, blockName, "name", PDO_NOPREF | PDO_NOTBLANK, I2VP(200), N_("Name"), 0, 0, sizeof( blockName )},
	/*1*/ { PD_STRING, blockScript, "script", PDO_NOPREF, I2VP(350), N_("Script"), 0, 0, sizeof( blockScript)}
};
static paramGroup_t blockPG = { "block", 0, blockPLs,  COUNT( blockPLs ) };
static wWin_p blockW;

static char blockEditName[STR_SHORT_SIZE];
static char blockEditScript[STR_LONG_SIZE];
static char blockEditSegs[STR_LONG_SIZE];
static track_p blockEditTrack;

static paramData_t blockEditPLs[] = {
	/*0*/ { PD_STRING, blockEditName, "name", PDO_NOPREF | PDO_NOTBLANK, I2VP(200), N_("Name"), 0, 0, sizeof(blockEditName)},
	/*1*/ { PD_STRING, blockEditScript, "script", PDO_NOPREF, I2VP(350), N_("Script"), 0, 0, sizeof(blockEditScript)},
	/*2*/ { PD_STRING, blockEditSegs, "segments", PDO_NOPREF, I2VP(350), N_("Segments"), BO_READONLY, 0, sizeof(blockEditSegs) },
};
static paramGroup_t blockEditPG = { "block", 0, blockEditPLs,  COUNT( blockEditPLs ) };
static wWin_p blockEditW;

typedef struct btrackinfo_t {
	track_p t;
	TRKINX_T i;
} btrackinfo_t, *btrackinfo_p;

static dynArr_t blockTrk_da;

#define blockTrk(N) DYNARR_N( btrackinfo_t , blockTrk_da, N )

#define tracklist(N) (&(xx->trackList))[N]


typedef struct blockData_t {
	extraDataBase_t base;
	char * name;
	char * script;
	BOOL_T IsHilite;
	track_p next_block;
	wIndex_t numTracks;
	btrackinfo_t trackList;
} blockData_t, *blockData_p;

static blockData_p GetblockData ( track_p trk )
{
	return GET_EXTRA_DATA( trk, T_BLOCK, blockData_t );
}

static void DrawBlock (track_p t, drawCmd_p d, wDrawColor color )
{
}

static struct {
	char name[STR_SHORT_SIZE];
	char script[STR_LONG_SIZE];
	FLOAT_T length;
	coOrd endPt[2];
} blockData;

typedef enum { NM, SC, LN, E0, E1 } blockDesc_e;
static descData_t blockDesc[] = {
	/*NM*/	{ DESC_STRING, N_("Name"), &blockData.name, sizeof(blockData.name) },
	/*SC*/  { DESC_STRING, N_("Script"), &blockData.script, sizeof(blockData.script) },
	/*LN*/  { DESC_DIM, N_("Length"), &blockData.length },
	/*E0*/	{ DESC_POS, N_("End Pt 1: X,Y"), &blockData.endPt[0] },
	/*E1*/	{ DESC_POS, N_("End Pt 2: X,Y"), &blockData.endPt[1] },
	{ DESC_NULL }
};

static void UpdateBlock (track_p trk, int inx, descData_p descUpd,
                         BOOL_T needUndoStart )
{
	blockData_p xx = GetblockData(trk);
	const char * thename, *thescript;
	char *newName, *newScript;
	BOOL_T changed, nChanged, sChanged;
	size_t max_str;

	LOG( log_block, 1, ("*** UpdateBlock(): needUndoStart = %d\n",needUndoStart))
	if ( inx == -1 ) {
		nChanged = sChanged = changed = FALSE;
		thename = wStringGetValue( (wString_p)blockDesc[NM].control0 );

		if ( !xx->name || strcmp( thename, xx->name ) != 0 ) {
			nChanged = changed = TRUE;
			max_str = blockDesc[NM].max_string;
			if (max_str && strlen(thename)>max_str-1) {
				newName = MyMalloc(max_str);
				strncpy(newName, thename, max_str - 1);
				newName[max_str-1] = '\0';
				NoticeMessage2(0, MSG_ENTERED_STRING_TRUNCATED, _("Ok"), NULL, max_str-1);
			} else { newName = MyStrdup(thename); }
		}

		thescript = wStringGetValue( (wString_p)blockDesc[SC].control0 );
		if ( !xx->script || strcmp( thescript, xx->script ) != 0 ) {
			sChanged = changed = TRUE;
			max_str = blockDesc[SC].max_string;
			if (max_str && strlen(thescript)>max_str-1) {
				newScript = MyMalloc(max_str);
				strncpy(newScript, thescript, max_str - 1);
				newScript[max_str-1] = '\0';
				NoticeMessage2(0, MSG_ENTERED_STRING_TRUNCATED, _("Ok"), NULL, max_str-1);
			} else { newScript = MyStrdup(thescript); }
		}
		if ( ! changed ) { return; }
		if ( needUndoStart ) {
			UndoStart( _("Change block"), "Change block" );
		}
		UndoModify( trk );
		if (nChanged) {
			if (xx->name) { MyFree(xx->name); }
			xx->name = newName;
		}
		if (sChanged) {
			if (xx->script) { MyFree(xx->script); }
			xx->script = newScript;
		}
		return;
	}
}

static DIST_T DistanceBlock (track_p t, coOrd * p )
{
	blockData_p xx = GetblockData(t);
	DIST_T closest, current;
	int iTrk = 1;
	coOrd pos = *p;
	closest = 99999.0;
	coOrd best_pos = pos;
	for (iTrk = 0; iTrk < xx->numTracks; iTrk++) {
		pos = *p;
		if ((&(xx->trackList))[iTrk].t == NULL) { continue; }
		current = GetTrkDistance ((&(xx->trackList))[iTrk].t, &pos);
		if (current < closest) {
			closest = current;
			best_pos = pos;
		}
	}
	*p = best_pos;
	return closest;
}

static void DescribeBlock (track_p trk, char * str, CSIZE_T len )
{
	blockData_p xx = GetblockData(trk);
	wIndex_t tcount = 0;
//	track_p lastTrk = NULL;
	long listLabelsOption = listLabels;

	LOG( log_block, 1, ("*** DescribeBlock(): trk is T%d\n",GetTrkIndex(trk)))
	FormatCompoundTitle( listLabelsOption, xx->name );
	if (message[0] == '\0') {
		FormatCompoundTitle( listLabelsOption|LABEL_DESCR, xx->name );
	}
	strcpy( str, _(GetTrkTypeName( trk )) );
	str++;
	while (*str) {
		*str = tolower((unsigned char)*str);
		str++;
	}
	sprintf( str, _("(%d): Layer=%u %s"),
	         GetTrkIndex(trk), GetTrkLayer(trk)+1, message );
	blockData.name[0] = '\0';
	strncat(blockData.name,xx->name,STR_SHORT_SIZE-1);
	blockData.script[0] = '\0';
	strncat(blockData.script,xx->script,STR_LONG_SIZE-1);
	blockData.length = 0;
	BOOL_T first = TRUE;
	for (tcount = 0; tcount < xx->numTracks; tcount++) {
		if ((&(xx->trackList))[tcount].t == NULL) { continue; }
		if (first) {
			blockData.endPt[0] = GetTrkEndPos((&(xx->trackList))[tcount].t,0);
			first = FALSE;
		}
		blockData.endPt[1] = GetTrkEndPos((&(xx->trackList))[tcount].t,1);
		blockData.length += GetTrkLength((&(xx->trackList))[tcount].t,0,1);
		tcount++;
		break;
	}
	blockDesc[E0].mode =
	        blockDesc[E1].mode =
	                blockDesc[LN].mode = DESC_RO;
	blockDesc[NM].mode =
	        blockDesc[SC].mode = DESC_NOREDRAW;
	DoDescribe(_("Block"), trk, blockDesc, UpdateBlock );

}

static int blockDebug (track_p trk)
{
	wIndex_t iTrack;
	blockData_p xx = GetblockData(trk);
	LOG( log_block, 1, ("*** blockDebug(): trk = %08x\n",trk))
	LOG( log_block, 1, ("*** blockDebug(): Index = %d\n",GetTrkIndex(trk)))
	LOG( log_block, 1, ("*** blockDebug(): name = \"%s\"\n",xx->name))
	LOG( log_block, 1, ("*** blockDebug(): script = \"%s\"\n",xx->script))
	LOG( log_block, 1, ("*** blockDebug(): numTracks = %d\n",xx->numTracks))
	for (iTrack = 0; iTrack < xx->numTracks; iTrack++) {
		if ((&(xx->trackList))[iTrack].t == NULL) { continue; }
		LOG( log_block, 1, ("*** blockDebug(): trackList[%d] = T%d, ",iTrack,
		                    GetTrkIndex((&(xx->trackList))[iTrack].t)))
		LOG( log_block, 1, ("%s\n",GetTrkTypeName((&(xx->trackList))[iTrack].t)))
	}
	return(0);
}

static BOOL_T blockCheckContiguousPath()
{
	EPINX_T ep, epCnt, epN;
	int inx;
	track_p trk, trk1;
	DIST_T dist;
	ANGLE_T angle;
	/*int pathElemStart = 0;*/
	coOrd endPtOrig = zero;
	BOOL_T IsConnectedP;
	trkEndPt_p endPtP;
	TempEndPtsReset();

	for ( inx=0; inx<blockTrk_da.cnt; inx++ ) {
		trk = blockTrk(inx).t;
		epCnt = GetTrkEndPtCnt(trk);
		IsConnectedP = FALSE;
		for ( ep=0; ep<epCnt; ep++ ) {
			trk1 = GetTrkEndTrk(trk,ep);
			if ( trk1 == NULL || !GetTrkSelected(trk1) ) {
				/* boundary EP */
				for ( epN=0; epN<TempEndPtsCount(); epN++ ) {
					endPtP = TempEndPt(epN);
					dist = FindDistance( GetTrkEndPos(trk,ep), GetEndPtPos(endPtP) );
					angle = NormalizeAngle( GetTrkEndAngle(trk,
					                                       ep) - GetEndPtAngle(endPtP) + connectAngle/2.0 );
					if ( dist < connectDistance && angle < connectAngle ) {
						break;
					}
				}
				if ( epN>=TempEndPtsCount() ) {
					endPtP = TempEndPtsAppend();
					SetEndPt( endPtP, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) );
					/*endPtP->track = trk1;*/
					/* These End Points are dummies --
					   we don't want DeleteTrack to look at
					   them. */
					SetEndPtTrack( endPtP, NULL );
					// TODO-EPP What is this for?
					SetEndPtEndPt( endPtP, (trk1?GetEndPtConnectedToMe(trk1,trk):-1) );
					endPtOrig.x += GetEndPtPos(endPtP).x;
					endPtOrig.y += GetEndPtPos(endPtP).y;
				}
			} else {
				IsConnectedP = TRUE;
			}
		}
		if (!IsConnectedP && blockTrk_da.cnt > 1) { return FALSE; }
	}
	return TRUE;
}

static void DeleteBlock ( track_p t )
{
	track_p trk1;
	blockData_p xx1;

	LOG( log_block, 1, ("*** DeleteBlock(%p)\n",t))
	blockData_p xx = GetblockData(t);
	LOG( log_block, 1, ("*** DeleteBlock(): index is %d\n",GetTrkIndex(t)))
	LOG( log_block, 1,
	     ("*** DeleteBlock(): xx = %p, xx->name = %p, xx->script = %p\n",
	      xx,xx->name,xx->script))
	MyFree(xx->name); xx->name = NULL;
	MyFree(xx->script); xx->script = NULL;

	if (first_block == t) {
		first_block = xx->next_block;
	}
	trk1 = first_block;
	while(trk1) {
		xx1 = GetblockData (trk1);
		if (xx1->next_block == t) {
			xx1->next_block = xx->next_block;
			break;
		}
		trk1 = xx1->next_block;
	}
	if (t == last_block) {
		last_block = trk1;
	}

}

static BOOL_T WriteBlock ( track_p t, FILE * f )
{
	BOOL_T rc = TRUE;
	wIndex_t iTrack;
	blockData_p xx = GetblockData(t);
	char *blockName = MyStrdup(xx->name);

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

	rc &= fprintf(f, "BLOCK %d \"%s\" \"%s\"\n",
	              GetTrkIndex(t), blockName, xx->script)>0;
	for (iTrack = 0; iTrack < xx->numTracks && rc; iTrack++) {
		if ((&(xx->trackList))[iTrack].t == NULL) { continue; }
		rc &= fprintf(f, "\tTRK %d\n",
		              GetTrkIndex((&(xx->trackList))[iTrack].t))>0;
	}
	rc &= fprintf( f, "\t%s\n", END_BLOCK )>0;
	MyFree(blockName);
	return rc;
}

static BOOL_T ReadBlock ( char * line )
{
	TRKINX_T trkindex;
	wIndex_t index;
	track_p trk, trk1;
	char * cp = NULL;
	blockData_p xx,xx1;
	wIndex_t iTrack;
	EPINX_T ep;
	trkEndPt_p endPtP;
	char *name, *script;

	LOG( log_block, 1, ("*** ReadBlock: line is '%s'\n",line))
	if (!GetArgs(line+6,"dqq",&index,&name,&script)) {
		return FALSE;
	}

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


	DYNARR_RESET( btrackinfo_t, blockTrk_da );
	while ( (cp = GetNextLine()) != NULL ) {
		if ( IsEND( END_BLOCK ) ) {
			break;
		}
		while (isspace((unsigned char)*cp)) { cp++; }
		if ( *cp == '\n' || *cp == '#' ) {
			continue;
		}
		if ( strncmp( cp, "TRK", 3 ) == 0 ) {
			if (!GetArgs(cp+4,"d",&trkindex)) { return FALSE; }
			/*trk = FindTrack(trkindex);*/
			DYNARR_APPEND( btrackinfo_t, blockTrk_da, 10 );
			DYNARR_LAST( btrackinfo_t, blockTrk_da ).i = trkindex;
		}
	}
	/*blockCheckContigiousPath(); save for ResolveBlockTracks */
	trk = NewTrack(index, T_BLOCK, TempEndPtsCount(),
	               sizeof(blockData_t)+(sizeof(btrackinfo_t)*(blockTrk_da.cnt))+1);
	for ( ep=0; ep<TempEndPtsCount(); ep++) {
		endPtP = TempEndPt(ep);
		SetTrkEndPoint( trk, ep, GetEndPtPos(endPtP), GetEndPtAngle(endPtP) );
	}
	xx = GetblockData( trk );
	LOG( log_block, 1, ("*** ReadBlock(): trk = %p (%d), xx = %p\n",trk,
	                    GetTrkIndex(trk),xx))
	LOG( log_block, 1, ("*** ReadBlock(): name = %p, script = %p\n",name,script))
	xx->name = name;
	xx->script = script;
	xx->IsHilite = FALSE;
	xx->numTracks = blockTrk_da.cnt;
	trk1 = last_block;
	if (!trk1) { first_block = trk; }
	else {
		xx1 = GetblockData(trk1);
		xx1->next_block = trk;
	}
	xx->next_block = NULL;
	last_block = trk;
	for (iTrack = 0; iTrack < blockTrk_da.cnt; iTrack++) {
		LOG( log_block, 1, ("*** ReadBlock(): copying track T%d\n",blockTrk(iTrack).i))
		tracklist(iTrack).i = blockTrk(iTrack).i;
		tracklist(iTrack).t = NULL;  			// Not resolved yet!! //
	}
	blockDebug(trk);
	return TRUE;
}

EXPORT void ResolveBlockTrack ( track_p trk )
{
	LOG( log_block, 1, ("*** ResolveBlockTrack(%p)\n",trk))
	blockData_p xx;
	track_p t_trk;
	wIndex_t iTrack;
	if (GetTrkType(trk) != T_BLOCK) { return; }
	LOG( log_block, 1, ("*** ResolveBlockTrack(%d)\n",GetTrkIndex(trk)))
	xx = GetblockData(trk);
	for (iTrack = 0; iTrack < xx->numTracks; iTrack++) {
		t_trk = FindTrack(tracklist(iTrack).i);
		if (t_trk == NULL) {
			NoticeMessage( _("resolveBlockTrack: T%d[%d]: T%d doesn't exist"),
			               _("Continue"), NULL, GetTrkIndex(trk), iTrack, tracklist(iTrack).i,t_trk );
		}
		tracklist(iTrack).t = t_trk;
		LOG( log_block, 1, ("*** ResolveBlockTrack(): %d (%d): %p\n",iTrack,
		                    tracklist(iTrack).i,t_trk))
	}
}

static void MoveBlock (track_p trk, coOrd orig ) {}
static void RotateBlock (track_p trk, coOrd orig, ANGLE_T angle ) {}
static void RescaleBlock (track_p trk, FLOAT_T ratio ) {}

static trackCmd_t blockCmds = {
	"BLOCK",
	DrawBlock,
	DistanceBlock,
	DescribeBlock,
	DeleteBlock,
	WriteBlock,
	ReadBlock,
	MoveBlock,
	RotateBlock,
	RescaleBlock,
	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 */
	NULL, /* flip */
	NULL, /* drawPositionIndicator */
	NULL, /* advancePositionIndicator */
	NULL, /* checkTraverse */
	NULL, /* makeParallel */
	NULL  /* drawDesc */
};



static BOOL_T TrackInBlock (track_p trk, track_p blk)
{
	wIndex_t iTrack;
	blockData_p xx = GetblockData(blk);
	for (iTrack = 0; iTrack < xx->numTracks; iTrack++) {
		if (trk == (&(xx->trackList))[iTrack].t) { return TRUE; }
	}
	return FALSE;
}

static track_p FindBlock (track_p trk)
{
	track_p a_trk;
	blockData_p xx;
	if (!first_block) { return NULL; }
	a_trk = first_block;
	while (a_trk) {
		if (!IsTrackDeleted(a_trk)) {
			if (GetTrkType(a_trk) == T_BLOCK &&
			    TrackInBlock(trk,a_trk)) { return a_trk; }
		}
		xx = GetblockData(a_trk);
		a_trk = xx->next_block;
	}
	return NULL;
}

static void BlockOk ( void * junk )
{
	blockData_p xx,xx1;
	track_p trk,trk1;
	wIndex_t iTrack;
	EPINX_T ep;
	trkEndPt_p endPtP;

	LOG( log_block, 1, ("*** BlockOk()\n"))
	DYNARR_RESET( btrackinfo_p *, blockTrk_da );

	ParamUpdate( &blockPG );
	if ( blockName[0]==0 ) {
		NoticeMessage( _("Block must have a name!"), _("Ok"), NULL);
		return;
	}
	wDrawDelayUpdate( mainD.d, TRUE );
	/*
	 * Collect tracks
	 */
	trk = NULL;
	while ( TrackIterate( &trk ) ) {
		if ( GetTrkSelected( trk ) ) {
			if ( IsTrack(trk) ) {
				DYNARR_APPEND( btrackinfo_t, blockTrk_da, 10 );
				blockTrk(blockTrk_da.cnt - 1).t = trk;
				blockTrk(blockTrk_da.cnt - 1).i = GetTrkIndex(trk);
				LOG( log_block, 1, ("*** BlockOk(): adding track T%d\n",GetTrkIndex(trk)))

			}
		}
	}
	if ( blockTrk_da.cnt>0 ) {
		if ( blockTrk_da.cnt > 128 ) {
			NoticeMessage( MSG_TOOMANYSEGSINGROUP, _("Ok"), NULL );
			wDrawDelayUpdate( mainD.d, FALSE );
			wHide( blockW );
			return;
		}
		/* Need to check that all block elements are connected to each
		   other... */
		if (!blockCheckContiguousPath()) {
			NoticeMessage( _("Block is discontiguous!"), _("Ok"), NULL );
			wDrawDelayUpdate( mainD.d, FALSE );
			wHide( blockW );
			return;
		}
		UndoStart( _("Create block"), "Create block" );
		/* Create a block object */
		LOG( log_block, 1, ("*** BlockOk(): %d tracks in block\n",blockTrk_da.cnt))
		trk = NewTrack(0, T_BLOCK, TempEndPtsCount(),
		               sizeof(blockData_t)+(sizeof(btrackinfo_t)*(blockTrk_da.cnt-1))+1);
		for ( ep=0; ep<TempEndPtsCount(); ep++) {
			endPtP = TempEndPt(ep);
			SetTrkEndPoint( trk, ep, GetEndPtPos(endPtP), GetEndPtAngle(endPtP) );
		}

		xx = GetblockData( trk );
		LOG(log_block, 1, ("*** BlockOk(): trk = %p (%d), xx = %p\n", trk,
		                   GetTrkIndex(trk), xx))
		xx->name = MyStrdup(blockName);
		xx->script = MyStrdup(blockScript);
		xx->IsHilite = FALSE;
		xx->numTracks = blockTrk_da.cnt;
		trk1 = last_block;
		if (!trk1) {
			first_block = trk;
		} else {
			xx1 = GetblockData(trk1);
			xx1->next_block = trk;
		}
		xx->next_block = NULL;
		last_block = trk;
		for (iTrack = 0; iTrack < blockTrk_da.cnt; iTrack++) {
			LOG( log_block, 1, ("*** BlockOk(): copying track T%d\n",tracklist(iTrack).i))
			tracklist(iTrack).i = blockTrk(iTrack).i;
			tracklist(iTrack).t = blockTrk(iTrack).t;
		}
		blockDebug(trk);
		UndoEnd();
	}
	wHide( blockW );

}

static void NewBlockDialog()
{
	track_p trk = NULL;

	LOG( log_block, 1, ("*** NewBlockDialog()\n"))
	blockElementCount = 0;

	while ( TrackIterate( &trk ) ) {
		if ( GetTrkSelected( trk ) ) {
			if ( !IsTrack( trk ) ) {
				ErrorMessage( _("Non track object skipped!") );
				continue;
			}
			if ( FindBlock( trk ) != NULL ) {
				ErrorMessage( _("Selected track is already in a block, skipped!") );
				continue;
			}
			blockElementCount++;
		}
	}

	if (blockElementCount == 0) {
		ErrorMessage( MSG_NO_SELECTED_TRK );
		return;
	}
	if ( log_block < 0 ) { log_block = LogFindIndex( "block" ); }
	if ( !blockW ) {
		ParamRegister( &blockPG );
		blockW = ParamCreateDialog (&blockPG, MakeWindowTitle(_("Create Block")),
		                            _("Ok"), BlockOk, ParamCancel_Current,
		                            TRUE, NULL, F_BLOCK, NULL );
		blockD.dpi = mainD.dpi;
	}
	ParamLoadControls( &blockPG );
	wShow( blockW );
}

static STATUS_T CmdBlockCreate( wAction_t action, coOrd pos )
{
	LOG( log_block, 1, ("*** CmdBlockAction(%08x,{%f,%f})\n",action,pos.x,pos.y))
	switch (action & 0xFF) {
	case C_START:
		LOG( log_block, 1,("*** CmdBlockCreate(): C_START\n"))
		NewBlockDialog();
		return C_TERMINATE;
	default:
		return C_CONTINUE;
	}
}

#if 0

static STATUS_T CmdBlockEdit( wAction_t action, coOrd pos )
{
	track_p trk,btrk;
	char msg[STR_SIZE];

	switch (action) {
	case C_START:
		InfoMessage( _("Select a track") );
		inDescribeCmd = TRUE;
		return C_CONTINUE;
	case C_DOWN:
		if ((trk = OnTrack(&pos, TRUE, TRUE )) == NULL) {
			return C_CONTINUE;
		}
		btrk = FindBlock( trk );
		if ( !btrk ) {
			ErrorMessage( _("Not a block!") );
			return C_CONTINUE;
		}
		DescribeTrack (btrk, msg, sizeof msg );
		InfoMessage( msg );
		return C_CONTINUE;
	case C_REDRAW:
		return C_CONTINUE;
	case C_CANCEL:
		inDescribeCmd = FALSE;
		return C_TERMINATE;
	default:
		return C_CONTINUE;
	}
}

static STATUS_T CmdBlockDelete( wAction_t action, coOrd pos )
{
	track_p trk,btrk;
	blockData_p xx;

	switch (action) {
	case C_START:
		InfoMessage( _("Select a track") );
		return C_CONTINUE;
	case C_DOWN:
		if ((trk = OnTrack(&pos, TRUE, TRUE )) == NULL) {
			return C_CONTINUE;
		}
		btrk = FindBlock( trk );
		if ( !btrk ) {
			ErrorMessage( _("Not a block!") );
			return C_CONTINUE;
		}
		/* Confirm Delete Block */
		xx = GetblockData(btrk);
		if ( NoticeMessage( _("Really delete block %s?"), _("Yes"), _("No"),
		                    xx->name) ) {
			UndoStart( _("Delete Block"), "delete" );
			DeleteTrack (btrk, FALSE);
			UndoEnd();
			return C_TERMINATE;
		}
		return C_CONTINUE;
	case C_REDRAW:
		return C_CONTINUE;
	case C_CANCEL:
		return C_TERMINATE;
	default:
		return C_CONTINUE;
	}
}


#define BLOCK_CREATE 0
#define BLOCK_EDIT   1
#define BLOCK_DELETE 2

static STATUS_T CmdBlock (wAction_t action, coOrd pos )
{
	LOG( log_block, 1, ("*** CmdBlock(%08x,{%f,%f})\n",action,pos.x,pos.y))

	switch (VP2L(commandContext)) {
	case BLOCK_CREATE: return CmdBlockCreate(action,pos);
	case BLOCK_EDIT:   return CmdBlockEdit(action,pos);
	case BLOCK_DELETE: return CmdBlockDelete(action,pos);
	default: return C_TERMINATE;
	}
}
#endif

void CheckDeleteBlock(track_p t)
{
	track_p blk;
	blockData_p xx;
	if (!IsTrack(t)) {
		return;
	}
	blk = FindBlock(t);
	if (blk == NULL) {
		return;
	}
	xx = GetblockData(blk);
	NoticeMessage(_("Deleting block %s"),_("Ok"),NULL,xx->name);
	DeleteTrack(blk,FALSE);
}

static void BlockEditOk ( void * junk )
{
	blockData_p xx;
	track_p trk;

	LOG( log_block, 1, ("*** BlockEditOk()\n"))
	ParamUpdate (&blockEditPG );
	if ( blockEditName[0]==0 ) {
		NoticeMessage( _("Block must have a name!"), _("Ok"), NULL);
		return;
	}
	wDrawDelayUpdate( mainD.d, TRUE );
	UndoStart( _("Modify Block"), "Modify Block" );
	trk = blockEditTrack;
	xx = GetblockData( trk );
	xx->name = MyStrdup(blockEditName);
	xx->script = MyStrdup(blockEditScript);
	blockDebug(trk);
	UndoEnd();
	wHide( blockEditW );
}


static void EditBlock (track_p trk)
{
	blockData_p xx = GetblockData(trk);
	wIndex_t iTrack;
	BOOL_T needComma = FALSE;
	char temp[32];
	strncpy(blockEditName, xx->name, STR_SHORT_SIZE - 1);
	blockEditName[STR_SHORT_SIZE-1] = '\0';
	strncpy(blockEditScript, xx->script, STR_LONG_SIZE - 1);
	blockEditScript[STR_LONG_SIZE-1] = '\0';
	blockEditSegs[0] = '\0';
	for (iTrack = 0; iTrack < xx->numTracks ; iTrack++) {
		if ((&(xx->trackList))[iTrack].t == NULL) { continue; }
		sprintf(temp,"%d",GetTrkIndex((&(xx->trackList))[iTrack].t));
		if (needComma) { strcat(blockEditSegs,", "); }
		strcat(blockEditSegs,temp);
		needComma = TRUE;
	}
	blockEditTrack = trk;
	if ( !blockEditW ) {
		ParamRegister( &blockEditPG );
		blockEditW = ParamCreateDialog (&blockEditPG,
		                                MakeWindowTitle(_("Edit block")),
		                                _("Ok"), BlockEditOk,
		                                ParamCancel_Current, TRUE, NULL, F_BLOCK,
		                                NULL );
	}
	ParamLoadControls( &blockEditPG );
	sprintf( message, _("Edit block %d"), GetTrkIndex(trk) );
	wWinSetTitle( blockEditW, message );
	wShow (blockEditW);
}

static coOrd blkhiliteOrig, blkhiliteSize;
static POS_T blkhiliteBorder;
static wDrawColor blkhiliteColor = 0;
static void DrawBlockTrackHilite( void )
{
	if (blkhiliteColor==0) {
		blkhiliteColor = wDrawColorGray(87);
	}
	// This is incomplete.  We should be in temp drawing mode and clearing temp draw on UN_HILIGHT
	DrawRectangle( &tempD, blkhiliteOrig, blkhiliteSize, blkhiliteColor,
	               DRAW_TRANSPARENT );
}


static int BlockMgmProc ( int cmd, void * data )
{
	track_p trk = (track_p) data;
	blockData_p xx = GetblockData(trk);
	wIndex_t iTrack;
	BOOL_T needComma = FALSE;
	char temp[32];
	/*char msg[STR_SIZE];*/
	coOrd tempOrig, tempSize;
	BOOL_T first = TRUE;

	switch ( cmd ) {
	case CONTMGM_CAN_EDIT:
		return TRUE;
		break;
	case CONTMGM_DO_EDIT:
		EditBlock (trk);
		/*inDescribeCmd = TRUE;*/
		/*DescribeTrack (trk, msg, sizeof msg );*/
		/*InfoMessage( msg );*/
		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) {
			blkhiliteBorder = mainD.scale*0.1;
			if ( blkhiliteBorder < trackGauge ) { blkhiliteBorder = trackGauge; }
			first = TRUE;
			for (iTrack = 0; iTrack < xx->numTracks ; iTrack++) {
				if ((&(xx->trackList))[iTrack].t == NULL) { continue; }
				GetBoundingBox( (&(xx->trackList))[iTrack].t, &tempSize, &tempOrig );
				if (first) {
					blkhiliteOrig = tempOrig;
					blkhiliteSize = tempSize;
					first = FALSE;
				} else {
					if (tempSize.x > blkhiliteSize.x) {
						blkhiliteSize.x = tempSize.x;
					}
					if (tempSize.y > blkhiliteSize.y) {
						blkhiliteSize.y = tempSize.y;
					}
					if (tempOrig.x < blkhiliteOrig.x) {
						blkhiliteOrig.x = tempOrig.x;
					}
					if (tempOrig.y < blkhiliteOrig.y) {
						blkhiliteOrig.y = tempOrig.y;
					}
				}
			}
			blkhiliteOrig.x -= blkhiliteBorder;
			blkhiliteOrig.y -= blkhiliteBorder;
			blkhiliteSize.x -= blkhiliteOrig.x-blkhiliteBorder;
			blkhiliteSize.y -= blkhiliteOrig.y-blkhiliteBorder;
			DrawBlockTrackHilite();
			xx->IsHilite = TRUE;
		}
		break;
	case CONTMGM_UN_HILIGHT:
		if (xx->IsHilite) {
			blkhiliteBorder = mainD.scale*0.1;
			if ( blkhiliteBorder < trackGauge ) { blkhiliteBorder = trackGauge; }
			first = TRUE;
			for (iTrack = 0; iTrack < xx->numTracks ; iTrack++) {
				if ((&(xx->trackList))[iTrack].t == NULL) { continue; }
				GetBoundingBox( (&(xx->trackList))[iTrack].t, &tempSize, &tempOrig );
				if (first) {
					blkhiliteOrig = tempOrig;
					blkhiliteSize = tempSize;
					first = FALSE;
				} else {
					if (tempSize.x > blkhiliteSize.x) {
						blkhiliteSize.x = tempSize.x;
					}
					if (tempSize.y > blkhiliteSize.y) {
						blkhiliteSize.y = tempSize.y;
					}
					if (tempOrig.x < blkhiliteOrig.x) {
						blkhiliteOrig.x = tempOrig.x;
					}
					if (tempOrig.y < blkhiliteOrig.y) {
						blkhiliteOrig.y = tempOrig.y;
					}
				}
			}
			blkhiliteOrig.x -= blkhiliteBorder;
			blkhiliteOrig.y -= blkhiliteBorder;
			blkhiliteSize.x -= blkhiliteOrig.x-blkhiliteBorder;
			blkhiliteSize.y -= blkhiliteOrig.y-blkhiliteBorder;
			DrawBlockTrackHilite();
			xx->IsHilite = FALSE;
		}
		break;
	case CONTMGM_GET_TITLE:
		sprintf( message, "\t%s\t", xx->name);
		for (iTrack = 0; iTrack < xx->numTracks ; iTrack++) {
			if ((&(xx->trackList))[iTrack].t == NULL) { continue; }
			sprintf(temp,"%d",GetTrkIndex((&(xx->trackList))[iTrack].t));
			if (needComma) { strcat(message,", "); }
			strcat(message,temp);
			needComma = TRUE;
		}
		break;
	}
	return FALSE;
}


#include "bitmaps/block.image3"

EXPORT void BlockMgmLoad( void )
{
	track_p trk;
	static wIcon_p blockI = NULL;

	if ( blockI == NULL) {
		blockI = wIconCreatePixMap( block_image3[iconSize] );
	}

	TRK_ITERATE(trk) {
		if (GetTrkType(trk) != T_BLOCK) { continue; }
		ContMgmLoad( blockI, BlockMgmProc, trk );
	}

}

EXPORT void InitCmdBlock( wMenu_p menu )
{
	blockName[0] = '\0';
	blockScript[0] = '\0';
	AddMenuButton( menu, CmdBlockCreate, "cmdBlockCreate", _("Block"),
	               wIconCreatePixMap( block_image3[iconSize] ), LEVEL0_50,
	               IC_STICKY|IC_POPUP2, ACCL_BLOCK1, NULL );
	ParamRegister( &blockPG );
}


EXPORT void InitTrkBlock( void )
{
	T_BLOCK = InitObject ( &blockCmds );
	log_block = LogFindIndex ( "block" );
	DYNARR_INIT( btrackinfo_t, blockTrk_da );
	last_block = NULL;
}


