/** \file cparalle.c
 * PARALLEL
 */

/*  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "ccurve.h"
#include "cstraigh.h"
#include "cselect.h"
#include "cundo.h"
#include "param.h"
#include "track.h"
#include "layout.h"
#include "common-ui.h"

static struct {
	track_p Trk;
	coOrd orig;
	track_p anchor_Trk;
} Dpa;

static DIST_T parSeparation = 1.0;
static double parSepFactor = 0.0;
static long parType = 0;

enum PAR_TYPE_E { PAR_TRACK, PAR_LINE };

static paramFloatRange_t r_0o1_100 = { 0.0, 100.0, 100 };
static paramFloatRange_t r_0_10 = { 0.0, 10.0 };
static paramData_t parSepPLs[] = {
#define parSepPD (parSepPLs[0])
#define parSepI 0
	{	PD_FLOAT, &parSeparation, "separation", PDO_DIM, &r_0o1_100, N_("Separation") },
#define parFactorPD (parSepPLs[1])
#define parFactorI 1
	{   PD_FLOAT, &parSepFactor, "factor", 0, &r_0_10, N_("Radius Factor") }
};
static paramGroup_t parSepPG = { "parallel", 0, parSepPLs, COUNT( parSepPLs ) };


static STATUS_T CmdParallel(wAction_t action, coOrd pos)
{

	DIST_T d;
	track_p t=NULL;
	coOrd p;
	static coOrd p0, p1;
	ANGLE_T a;
	track_p t0, t1;
	EPINX_T ep0=-1, ep1=-1;
	wControl_p controls[4];
	char * labels[3];
	static DIST_T parRFactor;

	parType = VP2L(commandContext);

	switch (action&0xFF) {

	case C_START:
		if (parSepPLs[0].control==NULL) {
			ParamCreateControls(&parSepPG, NULL);
		}
		if (parType == PAR_TRACK) {
			sprintf(message, "parallel-separation-%s", curScaleName);
			parSeparation = ceil(13.0*12.0/curScaleRatio);
		} else {
			sprintf(message, "parallel-line-separation-%s", curScaleName);
			parSeparation = 5.0*12.0/curScaleRatio;
		}
		wPrefGetFloat("misc", message, &parSeparation, parSeparation);
		ParamLoadControls(&parSepPG);
		ParamGroupRecord(&parSepPG);
		parSepPD.option |= PDO_NORECORD;
		parFactorPD.option |= PDO_NORECORD;
		controls[0] = parSepPD.control;
		if (parType == PAR_TRACK) {
			controls[1] = parFactorPD.control;
		} else {
			controls[1] = NULL;
		}
		controls[2] = NULL;
		labels[0] = N_("Separation");
		labels[1] = N_("Radius Factor");
		InfoSubstituteControls(controls, labels);
		parSepPD.option &= ~PDO_NORECORD;
		parFactorPD.option &= ~PDO_NORECORD;
		Dpa.anchor_Trk = NULL;
		DYNARR_RESET( trkSeg_t, tempSegs_da );
		SetAllTrackSelect( FALSE );
		return C_CONTINUE;

	case wActionMove:
		DYNARR_RESET( trkSeg_t, tempSegs_da );
		Dpa.anchor_Trk = NULL;
		if (parType == PAR_TRACK) {
			Dpa.anchor_Trk = OnTrack(&pos, FALSE, TRUE);
		} else {
			Dpa.anchor_Trk = OnTrack(&pos, FALSE, FALSE);
		}

		if (!Dpa.anchor_Trk) {
			return C_CONTINUE;
		}
		if (Dpa.anchor_Trk && !CheckTrackLayerSilent(Dpa.anchor_Trk)) {
			Dpa.anchor_Trk = NULL;
			return C_CONTINUE;
		}
		if (!QueryTrack(Dpa.anchor_Trk, Q_CAN_PARALLEL)) {
			Dpa.anchor_Trk = NULL;
			return C_CONTINUE;
		}
		break;
	case C_DOWN:
		Dpa.anchor_Trk = NULL;
		DYNARR_RESET( trkSeg_t, tempSegs_da );
		if (parSeparation < 0.0) {
			ErrorMessage(MSG_PARALLEL_SEP_GTR_0);
			return C_ERROR;
		}

		controls[0] = parSepPD.control;
		controls[1] = parFactorPD.control;
		controls[2] = NULL;
		labels[0] = N_("Separation");
		labels[1] = N_("Radius factor");
		InfoSubstituteControls(controls, labels);
		ParamLoadData(&parSepPG);
		Dpa.orig = pos;
		if (parType == PAR_TRACK) {
			Dpa.Trk = OnTrack(&pos, FALSE, TRUE);
		} else {
			Dpa.Trk = OnTrack(&pos, FALSE, FALSE);        //Also lines for line
		}
		if (!Dpa.Trk) {
			return C_CONTINUE;
		}
		if (!QueryTrack(Dpa.Trk, Q_CAN_PARALLEL)) {
			Dpa.Trk = NULL;
			InfoMessage(_(" Track/Line doesn't support parallel"));
			wBeep();
			return C_CONTINUE;
		}

		parRFactor = (2864.0*(double)parSepFactor)/curScaleRatio;

		if ((parType == PAR_TRACK) && (parSeparation == 0.0)) {
			DIST_T orig_gauge = GetTrkGauge(Dpa.Trk);
			DIST_T new_gauge = GetScaleTrackGauge(GetLayoutCurScale());
			if (orig_gauge == new_gauge) {
				ErrorMessage(MSG_PARALLEL_SEP_GTR_0);
				return C_ERROR;
			}
			parSeparation = fabs(orig_gauge/2-new_gauge/2);
			parRFactor = 0.0;
		} else if (parType != PAR_TRACK) {
			parRFactor = 0.0;
		}
		/* in case query has changed things (eg joint) */
		/*
		 * this seems to cause problems so I commented it out
		 * until further investigation shows the necessity
		 */
		//Dpa.Trk = OnTrack( &Dpa.orig, TRUE, TRUE );
		DYNARR_RESET( trkSeg_t, tempSegs_da );
	/* no break */

	case C_MOVE:
		if (Dpa.Trk == NULL) {
			return C_CONTINUE;
		}
		DYNARR_RESET( trkSeg_t, tempSegs_da );
		if (!MakeParallelTrack(Dpa.Trk, pos, parSeparation, parRFactor, NULL, &p0, &p1,
		                       parType == PAR_TRACK)) {
			Dpa.Trk = NULL;
			return C_CONTINUE;
		}
		return C_CONTINUE;

	case C_UP:
		Dpa.anchor_Trk = NULL;
		if (Dpa.Trk == NULL) {
			return C_CONTINUE;
		}
		t0=t1=NULL;
		if (parType == PAR_TRACK) {
			p = p0;
			DYNARR_RESET( trkSeg_t, tempSegs_da );
			if ((t0=OnTrack(&p, FALSE, TRUE)) != NULL) {
				ep0 = PickEndPoint(p, t0);
				if (ep0 < 0 || GetTrkEndTrk(t0,ep0) != NULL) {
					t0 = NULL;
				} else {
					p = GetTrkEndPos(t0, ep0);
					d = FindDistance(p, p0);
					if (d > connectDistance) {
						t0 = NULL;
					}
				}
			}
			p = p1;
			if ((t1=OnTrack(&p, FALSE, TRUE)) != NULL) {
				ep1 = PickEndPoint(p, t1);
				if (ep1 < 0 || GetTrkEndTrk(t1,ep1) != NULL) {
					t1 = NULL;
				} else {
					p = GetTrkEndPos(t1, ep1);
					d = FindDistance(p, p1);
					if (d > connectDistance) {
						t1 = NULL;
					}
				}
			}
		}
		UndoStart(_("Create Parallel Track"), "newParallel");
		if (!MakeParallelTrack(Dpa.Trk, pos, parSeparation, parRFactor, &t, NULL, NULL,
		                       parType == PAR_TRACK)) {
			DYNARR_RESET( trkSeg_t, tempSegs_da );
			return C_TERMINATE;
		}
		if (parType == PAR_TRACK) {
			if (GetTrkGauge(Dpa.Trk)> parSeparation) {
				SetTrkNoTies(t, TRUE);
			}
			//CopyAttributes( Dpa.Trk, t );    Don't force scale or track width or Layer
			SetTrkBits(t,(GetTrkBits(t)&TB_HIDEDESC) | (GetTrkBits(Dpa.Trk)&~TB_HIDEDESC));

			if (t0) {
				a = NormalizeAngle(GetTrkEndAngle(t0, ep0) - GetTrkEndAngle(t,
				                   0) + (180.0+connectAngle/2.0));
				if (a < connectAngle) {
					DrawEndPt(&mainD, t0, ep0, wDrawColorWhite);
					ConnectTracks(t0, ep0, t, 0);
					DrawEndPt(&mainD, t0, ep0, wDrawColorBlack);
				}
			}
			if (t1) {
				a = NormalizeAngle(GetTrkEndAngle(t1, ep1) - GetTrkEndAngle(t,
				                   1) + (180.0+connectAngle/2.0));
				if (a < connectAngle) {
					DrawEndPt(&mainD, t1, ep1, wDrawColorWhite);
					ConnectTracks(t1, ep1, t, 1);
					DrawEndPt(&mainD, t1, ep1, wDrawColorBlack);
				}
			}
		}
		DrawNewTrack(t);
		UndoEnd();
		InfoSubstituteControls(NULL, NULL);
		if (parType == PAR_TRACK) {
			sprintf(message, "parallel-separation-%s", curScaleName);
		} else {
			sprintf(message, "parallel-line-separation-%s", curScaleName);
		}
		wPrefSetFloat("misc", message, parSeparation);
		DYNARR_RESET( trkSeg_t, tempSegs_da );
		return C_TERMINATE;

	case C_REDRAW:
		if (Dpa.anchor_Trk) {
			DrawTrack(Dpa.anchor_Trk,&tempD,
			          wDrawColorPreviewSelected);    //Special color means THICK3 as well
		}
		DrawSegsDA( &tempD, NULL, zero, 0.0, &tempSegs_da, trackGauge, wDrawColorBlack,
		            0 );
		return C_CONTINUE;

	case C_CANCEL:
		Dpa.anchor_Trk = NULL;
		DYNARR_RESET( trkSeg_t, tempSegs_da );
		InfoSubstituteControls(NULL, NULL);
		return C_TERMINATE;

	}
	return C_CONTINUE;
}


#include "bitmaps/parallel.xpm3"
#include "bitmaps/parallel-line.xpm3"

EXPORT void InitCmdParallel( wMenu_p menu )
{
	ButtonGroupBegin( _("Parallel"), "cmdParallelSetCmd", _("Parallel") );
	AddMenuButton( menu, CmdParallel, "cmdParallelTrack", _("Parallel Track"),
	               wIconCreatePixMap(parallel_xpm3[iconSize]), LEVEL0_50,
	               IC_STICKY|IC_POPUP|IC_WANT_MOVE, ACCL_PARALLEL, I2VP(0) );
	AddMenuButton( menu, CmdParallel, "cmdParallelLine", _("Parallel Line"),
	               wIconCreatePixMap(parallel_line_xpm3[iconSize]), LEVEL0_50,
	               IC_STICKY|IC_POPUP|IC_WANT_MOVE, ACCL_PARALLEL, I2VP(1) );
	ButtonGroupEnd();
	ParamRegister( &parSepPG );
}