F64 SpanTime()
{
	if (run_bttn.state)
		return a.elapsed_t + tS - a.start_wall_t;
	else
		return a.elapsed_t;
}

F64 Cost(CMathODE *ode)
{
	MyMass	 *tmpm;
	MySpring *tmps;
	F64		  res = 0;

	tmpm=ode->next_mass;
	while (tmpm != &ode->next_mass)
	{
		res += tmpm->cost;
		tmpm = tmpm->next;
	}
	tmps = ode->next_spring;
	while (tmps != &ode->next_spring)
	{
		res += tmps->cost;
		tmps = tmps->next;
	}

	return res;
}

U0 DrawIt(CTask *task, CDC *dc)
{
	MyMass	 *tmpm;
	MySpring *tmps;

	tmps = ode->next_spring;
	while (tmps != &ode->next_spring)
	{
		if (!(tmps->flags & SSF_INACTIVE))
		{
			dc->color = tmps->color;
			dc->thick = tmps->thick;
			GrLine3(dc, tmps->end1->x, tmps->end1->y, 0, tmps->end2->x, tmps->end2->y, 0);
		}
		tmps = tmps->next;
	}

	if (cursor_mass)
	{
		dc->color = RED;
		dc->thick = 2;
		GrLine3(dc, mouse.pos.x - task->pix_left - task->scroll_x, 
					mouse.pos.y - task->pix_top  - task->scroll_y, 0, 
					cursor_mass->x, cursor_mass->y, 0);
	}

	tmpm=ode->next_mass;
	while (tmpm != &ode->next_mass)
	{
		if (!(tmpm->flags&MSF_INACTIVE))
		{
			dc->color = BLACK;
			GrCircle(dc, tmpm->x, tmpm->y, tmpm->radius);
			GrFloodFill(dc, tmpm->x, tmpm->y, TRUE);
			dc->color = tmpm->color;
			GrCircle(dc, tmpm->x, tmpm->y, tmpm->radius);
			GrFloodFill(dc, tmpm->x, tmpm->y, TRUE);
			dc->color = BLACK;
			GrCircle(dc, tmpm->x, tmpm->y, tmpm->radius);
		}
		tmpm = tmpm->next;
	}

	dc->color = BLACK;
	GrPrint(dc, 90, 0, "Cost:%12.2, f", Cost(ode));
	GrPrint(dc, 90, FONT_HEIGHT, "Time:%12.2f", SpanTime);
}

MyMass *PlaceMass(I64 x,  I64 y)
{
	MyMass *tmpm = CAlloc(sizeof(MyMass));

	tmpm->drag_profile_factor	= 1.0;
	tmpm->x						= x;
	tmpm->y						= y;
	tmpm->mass					= MASS_MASS;
	tmpm->radius				= MASS_RADIUS;
	tmpm->cost					= 25.0 * COST_SCALE;
	tmpm->color					= YELLOW;
	QueueInsert(tmpm, ode->last_mass);

	return tmpm;
}

U0 NullSpring(MySpring *tmps, F64 scale)
{
	F64 d = D3Dist(&tmps->end1->x, &tmps->end2->x);

	tmps->rest_len				= d * scale;
	tmps->compression_strength	= tmps->base_compression_strength	/ (tmps->rest_len + 1.0);
	tmps->tensile_strength		= tmps->base_tensile_strength		/ (tmps->rest_len + 1.0);
	tmps->const					= tmps->base_const					/ (tmps->rest_len + 1.0);
	tmps->cost					= tmps->base_cost					*  tmps->rest_len;
}

U0 MoveMass(MyMass *tmpm, I64 x,  I64 y)
{
	MySpring *tmps;

	tmpm->x		= x;
	tmpm->y		= y;
	tmpm->DxDt	= 0;
	tmpm->DyDt	= 0;
	tmps = ode->next_spring;
	while (tmps != &ode->next_spring)
	{
		if (tmps->end1 == tmpm || tmps->end2 == tmpm)
		{
			if (tmps->flags & SSF_NO_COMPRESSION)
				NullSpring(tmps, WIRE_PERCENT);
			else
				NullSpring(tmps, 1.0);
		}
		tmps = tmps->next;
	}
}

U0 DelSpring(MySpring *tmps)
{
	QueueRemove(tmps);
	Free(tmps);
}

U0 DelMass(MyMass *tmpm)
{
	MySpring *tmps, *tmps1;

	tmps = ode->next_spring;
	while (tmps != &ode->next_spring)
	{
		tmps1 = tmps->next;
		if (tmps->end1 == tmpm || tmps->end2 == tmpm)
			DelSpring(tmps);
		tmps = tmps1;
	}
	QueueRemove(tmpm);
	Free(tmpm);
}

U0 DrawSpring(CDC *dc, MyMass *tmpm, I64 x, I64 y)
{
	switch (mode_bttn.state)
	{
		case MD_CONCRETE:
			dc->color = LTGRAY;
			dc->thick = 2;
			break;

		case MD_STEEL:
			dc->color = DKGRAY;
			dc->thick = 2;
			break;

		case MD_WIRE:
			dc->color = RED;
			dc->thick = 1;
			break;
	}
	GrLine3(dc, tmpm->x, tmpm->y, 0, x, y, 0);
}

U0 PlaceSpring(MyMass *tmpm1, MyMass *tmpm2)
{
	MySpring *tmps = CAlloc(sizeof(MySpring));

	tmps->end1 = tmpm1;
	tmps->end2 = tmpm2;
	switch (mode_bttn.state)
	{
		case MD_CONCRETE:
			tmps->base_const				= 3.00  * SPRING_SCALE;
			tmps->base_compression_strength	= 10.00 * STRENGTH_SCALE;
			tmps->base_tensile_strength 	= 0.35  * STRENGTH_SCALE;
			tmps->base_cost 				= 0.30  * COST_SCALE;
			NullSpring(tmps, 1.0);
			tmps->color						= LTGRAY;
			tmps->thick						= 2;
			break;

		case MD_STEEL:
			tmps->base_const				= 1.00 * SPRING_SCALE;
			tmps->base_compression_strength	= 1.00 * STRENGTH_SCALE;
			tmps->base_tensile_strength 	= 1.00 * STRENGTH_SCALE;
			tmps->base_cost 				= 1.00 * COST_SCALE;
			NullSpring(tmps, 1.0);
			tmps->color						= DKGRAY;
			tmps->thick						= 2;
			break;

		case MD_WIRE:
			tmps->base_const				= 0.25 * SPRING_SCALE;
			tmps->base_compression_strength	= 0.00;
			tmps->base_tensile_strength 	= 0.50 * STRENGTH_SCALE;
			tmps->base_cost 				= 0.10 * COST_SCALE;
			NullSpring(tmps, WIRE_PERCENT);
			tmps->color						= RED;
			tmps->thick						= 1;
			tmps->flags	|= SSF_NO_COMPRESSION;
			break;
	}
	QueueInsert(tmps, ode->last_spring);
}

U0 AnimateTask(SpanAnimateStruct *a)
{
	MySpring	*tmps, *tmps1;
	Bool		 old_run = FALSE;
	F64			 f;

	while (TRUE)
	{
		tmps = ode->next_spring;
		while (tmps != &ode->next_spring)
		{
			tmps1 = tmps->next;
			f = tmps->f;
			if (	f > 0 && f > tmps->compression_strength && !(tmps->flags & SSF_NO_COMPRESSION) ||
					f < 0 && -f>tmps->tensile_strength && !(tmps->flags & SSF_NO_TENSION))
				tmps->flags |= SSF_INACTIVE;
			tmps = tmps1;
		}
		AdjustLoads(ode);
		Refresh; //CMathODE updated once per refresh.
		if (old_run != run_bttn.state)
		{
			if (run_bttn.state)
			{
				if (!a->elapsed_t || !a->saved_ode)
				{
					Free(a->saved_ode);
					a->saved_ode = SpanSave(ode);
				}
				a->start_wall_t = tS;
				ODEPause(ode, OFF);
			}
			else
			{
				ODEPause(ode);
				a->elapsed_t += tS - a->start_wall_t;
			}
			old_run = run_bttn.state;
		}
	}
}

U0 Init(SpanAnimateStruct *a)
{
	SpanDel(ode);
	ode = SpanNew;

	run_bttn.state = 0;
	Refresh(2); //Allow stop to reg in animate task.

	if (a->saved_ode)
		SpanLoad(ode, a->saved_ode);
	else
		SpanBridge1Init(ode);
	a->elapsed_t = 0;
	cursor_mass = NULL;
}

U0 SongTask(I64)
{//Song by Terry A. Davis
	Fs->task_end_cb = &SoundTaskEndCB;
	MusicSettingsReset;
	music.tempo				= 3.636;
	music.stacatto_factor	= 0.902;
	while (TRUE)
	{
		Play("5q.EeDqED4G5DhE");
		Play("5q.EeDqED4G5DhE");
		Play("5q.FeEFEqF4G5EhF");
		Play("5q.FeEFEqF4G5EhF");
	}
}

U0 Span()
{
	I64			 message_code, arg1, arg2;
	MyMass		*tmpm1 = NULL, *tmpm2 = NULL;
	MySpring	*tmps;
	CCtrl		*bt_run, *bt_mode;
	U8			*src;
	CDC			*dc = DCAlias;

	SettingsPush; //See $LK,"SettingsPush",A="MN:SettingsPush"$
	Fs->text_attr = BROWN << 4 + BLACK;
	AutoComplete;
	WinBorder;
	WinMax;
	DocCursor;
	Fs->song_task = Spawn(&SongTask, NULL, "Song",, Fs);

	bt_run =CtrlBttnNew(0, 0, 80,, 2, "Stopped\0Running\0", run_colors, &run_bttn);
	bt_mode=CtrlBttnNew(0, 3.0 * FONT_HEIGHT, 80,, MD_MODES_NUM, Define("ST_SPAN_MODES"), mode_colors, &mode_bttn);
	a.saved_ode = NULL;

	Fs->win_inhibit |= WIG_DBL_CLICK;

	MenuPush(	"File {"
				"  New(,CH_CTRLN);"
				"  Open(,CH_CTRLO);"
				"  SaveAs(,CH_CTRLA);"
				"  Abort(,CH_SHIFT_ESC);"
				"  Exit(,CH_ESC);"
				"}"
				"Play {"
				"  Restart(,'\n');"
				"  RunStop(,CH_SPACE);"
				"  Mass(,'m');"
				"  Concrete(,'c');"
				"  Steel(,'s');"
				"  Wire(,'w');"
				"  Move(,'v');"
				"  Delete(,'d');"
				"}"
				);

	ode = NULL;
	Init(&a);
	Fs->animate_task = Spawn(&AnimateTask, &a, "Animate",, Fs);
	Fs->draw_it		 = &DrawIt;

	PopUpOk(	"Build a bridge to hold-up the\n"
				"red masses.  Test your design\n"
				"by pressing run/stop.\n\n"
				"The lowest cost bridge that\n"
				"stays standing wins.\n\n"
				"For a variation, try without\n"
				"using the center base point.\n"
				"\n"
				"Use\n"
				"\t$$GREEN$$'m'$$FG$$ass\n"
				"\t$$GREEN$$'c'$$FG$$oncrete\n"
				"\t$$GREEN$$'s'$$FG$$teel\n"
				"\t$$GREEN$$'w'$$FG$$ire\n"
				"\nto sel materials.\n");

	try
	{
		while (TRUE)
		{
			message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_MS_R_DOWN |
													1 << MESSAGE_MS_L_UP | 1 << MESSAGE_KEY_DOWN | 1 << MESSAGE_MS_MOVE);
			DCFill(dc);
			switch (message_code)
			{
				case MESSAGE_MS_L_DOWN:
					cursor_mass = tmpm1 = tmpm2 = NULL;
					switch (mode_bttn.state)
					{
						case MD_MASS:
							PlaceMass(arg1, arg2);
							break;

						case MD_CONCRETE:
						case MD_STEEL:
						case MD_WIRE:
							tmpm1 = MassFind(ode, arg1, arg2);
							break;

						case MD_MOVE:
							if (run_bttn.state)
								cursor_mass = MassFind(ode, arg1, arg2);
							else
								if (tmpm1 = MassFind(ode, arg1, arg2))
									MoveMass(tmpm1, arg1, arg2);
							break;

						case MD_DELETE:
							MassOrSpringFind(ode, &tmpm1, &tmps, arg1, arg2);
							if (tmpm1)
								DelMass(tmpm1);
							if (tmps)
								DelSpring(tmps);
							break;
					}
					break;

				case MESSAGE_MS_L_UP:
					switch (mode_bttn.state)
					{
						case MD_CONCRETE:
						case MD_STEEL:
						case MD_WIRE:
							if (tmpm1 && (tmpm2 = MassFind(ode, arg1, arg2)) && tmpm1 != tmpm2)
								PlaceSpring(tmpm1, tmpm2);
							break;

						case MD_MOVE:
							if (!run_bttn.state && tmpm1)
								MoveMass(tmpm1, arg1, arg2);
							break;
					}
					cursor_mass = tmpm1 = tmpm2 = NULL;
					break;

				case MESSAGE_MS_MOVE:
					switch (mode_bttn.state)
					{
						case MD_MOVE:
							if (!run_bttn.state && tmpm1)
								MoveMass(tmpm1, arg1, arg2);
							break;

						case MD_CONCRETE:
						case MD_STEEL:
						case MD_WIRE:
							if (tmpm1)
							{
								DrawSpring(dc, tmpm1, arg1, arg2);
							}
							break;
					}
					break;

				case MESSAGE_MS_R_DOWN:
					mode_bttn.state++;
					if (mode_bttn.state >= MD_MODES_NUM)
						mode_bttn.state = 0;
					cursor_mass = tmpm1 = tmpm2 = NULL;
					break;

				case MESSAGE_KEY_DOWN:
					switch (arg1)
					{
						case '\n':
							if (!SpanTime || !a.saved_ode)
							{
								Free(a.saved_ode);
								a.saved_ode = SpanSave(ode);
							}
							Init(&a);
							break;

						case CH_CTRLN:
							Free(a.saved_ode);
							a.saved_ode = NULL;
							Init(&a);
							break;

						case CH_CTRLO:
							if (src = SpanRead)
							{
								Free(a.saved_ode);
								a.saved_ode = src;
								Init(&a);
							}
							break;

						case CH_CTRLA:
							if (!SpanTime || !a.saved_ode)
							{
								Free(a.saved_ode);
								a.saved_ode = SpanSave(ode);
							}
							Init(&a);
							SpanWrite(ode);
							break;

						case CH_SPACE:
							run_bttn.state = !run_bttn.state;
							break;

						case 'c':
							mode_bttn.state = MD_CONCRETE;
							break;

						case 's':
							mode_bttn.state = MD_STEEL;
							break;

						case 'w':
							mode_bttn.state = MD_WIRE;
							break;

						case 'm':
							mode_bttn.state = MD_MASS;
							break;

						case 'v':
							mode_bttn.state = MD_MOVE;
							break;

						case 'd':
							mode_bttn.state = MD_DELETE;
							break;

						case CH_ESC:
							if (!SpanTime || !a.saved_ode)
							{
								Free(a.saved_ode);
								a.saved_ode = SpanSave(ode);
							}
							Init(&a);
							SpanWrite(ode);
						case CH_SHIFT_ESC:
							goto span_done;
					}
					break;
			}
		}
span_done: //Don't goto out of try
		MessageGet(,, 1 << MESSAGE_KEY_UP);
	}
	catch
		PutExcept;
	DocClear;
	SettingsPop;
	CtrlBttnDel(bt_run);
	CtrlBttnDel(bt_mode);
	SpanDel(ode);
	DCFill(dc);
	DCDel(dc);
	MenuPop;
}
