/*
 *                            COPYRIGHT
 *
 *  PCB, interactive printed circuit board design
 *  Copyright (C) 1994,1995,1996 Thomas Nau
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Contact addresses for paper mail and Email:
 *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
 *  Thomas.Nau@rz.uni-ulm.de
 *
 */


/* routines to update widgets and global settings
 * (except output window and dialogs)
 */

#include "config.h"
#include "conf_core.h"

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "action_helper.h"
#include "buffer.h"
#include "crosshair.h"
#include "data.h"
#include "draw.h"
#include "error.h"
#include "find.h"
#include "set.h"
#include "undo.h"
#include "hid_actions.h"
#include "route_style.h"

static int mode_position = 0;
static int mode_stack[MAX_MODESTACK_DEPTH];

/* ---------------------------------------------------------------------------
 * sets cursor grid with respect to grid offset values
 */
void SetGrid(Coord Grid, pcb_bool align)
{
	if (Grid >= 1 && Grid <= MAX_GRID) {
		if (align) {
			PCB->GridOffsetX = Crosshair.X % Grid;
			PCB->GridOffsetY = Crosshair.Y % Grid;
		}
		PCB->Grid = Grid;
		conf_set_design("editor/grid", "%$mS", Grid);
		if (conf_core.editor.draw_grid)
			Redraw();
	}
}

/* ---------------------------------------------------------------------------
 * sets a new line thickness
 */
void SetLineSize(Coord Size)
{
	if (Size >= MIN_LINESIZE && Size <= MAX_LINESIZE) {
		conf_set_design("design/line_thickness", "%$mS", Size);
		if (conf_core.editor.auto_drc)
			FitCrosshairIntoGrid(Crosshair.X, Crosshair.Y);
	}
}

/* ---------------------------------------------------------------------------
 * sets a new via thickness
 */
void SetViaSize(Coord Size, pcb_bool Force)
{
	if (Force || (Size <= MAX_PINORVIASIZE && Size >= MIN_PINORVIASIZE && Size >= conf_core.design.via_drilling_hole + MIN_PINORVIACOPPER)) {
		conf_set_design("design/via_thickness", "%$mS", Size);
	}
}

/* ---------------------------------------------------------------------------
 * sets a new via drilling hole
 */
void SetViaDrillingHole(Coord Size, pcb_bool Force)
{
	if (Force || (Size <= MAX_PINORVIASIZE && Size >= MIN_PINORVIAHOLE && Size <= conf_core.design.via_thickness - MIN_PINORVIACOPPER)) {
		conf_set_design("design/via_drilling_hole", "%$mS", Size);
	}
}

/* ---------------------------------------------------------------------------
 * sets a clearance width
 */
void SetClearanceWidth(Coord Width)
{
	if (Width <= MAX_LINESIZE) {
		conf_set_design("design/clearance", "%$mS", Width);
	}
}

/* ---------------------------------------------------------------------------
 * sets a text scaling
 */
void SetTextScale(int Scale)
{
	if (Scale <= MAX_TEXTSCALE && Scale >= MIN_TEXTSCALE) {
		conf_set_design("design/text_scale", "%d", Scale);
	}
}

/* ---------------------------------------------------------------------------
 * sets or resets changed flag and redraws status
 */
void SetChangedFlag(pcb_bool New)
{
	if (PCB->Changed != New) {
		PCB->Changed = New;

	}
}

/* ---------------------------------------------------------------------------
 * sets the crosshair range to the current buffer extents 
 */
void SetCrosshairRangeToBuffer(void)
{
	if (conf_core.editor.mode == PCB_MODE_PASTE_BUFFER) {
		SetBufferBoundingBox(PASTEBUFFER);
		SetCrosshairRange(PASTEBUFFER->X - PASTEBUFFER->BoundingBox.X1,
											PASTEBUFFER->Y - PASTEBUFFER->BoundingBox.Y1,
											PCB->MaxWidth -
											(PASTEBUFFER->BoundingBox.X2 - PASTEBUFFER->X),
											PCB->MaxHeight - (PASTEBUFFER->BoundingBox.Y2 - PASTEBUFFER->Y));
	}
}

/* ---------------------------------------------------------------------------
 * sets a new buffer number
 */
void SetBufferNumber(int Number)
{
	if (Number >= 0 && Number < MAX_BUFFER) {
		conf_set_design("editor/buffer_number", "%d", Number);

		/* do an update on the crosshair range */
		SetCrosshairRangeToBuffer();
	}
}

/* ---------------------------------------------------------------------------
 */

void SaveMode(void)
{
	mode_stack[mode_position] = conf_core.editor.mode;
	if (mode_position < MAX_MODESTACK_DEPTH - 1)
		mode_position++;
}

void RestoreMode(void)
{
	if (mode_position == 0) {
		Message(PCB_MSG_DEFAULT, "hace: underflow of restore mode\n");
		return;
	}
	SetMode(mode_stack[--mode_position]);
}


/* ---------------------------------------------------------------------------
 * set a new mode and update X cursor
 */
void SetMode(int Mode)
{
	char sMode[32];
	static pcb_bool recursing = pcb_false;
	/* protect the cursor while changing the mode
	 * perform some additional stuff depending on the new mode
	 * reset 'state' of attached objects
	 */
	if (recursing)
		return;
	recursing = pcb_true;
	notify_crosshair_change(pcb_false);
	addedLines = 0;
	Crosshair.AttachedObject.Type = PCB_TYPE_NONE;
	Crosshair.AttachedObject.State = STATE_FIRST;
	Crosshair.AttachedPolygon.PointN = 0;
	if (PCB->RatDraw) {
		if (Mode == PCB_MODE_ARC || Mode == PCB_MODE_RECTANGLE ||
				Mode == PCB_MODE_VIA || Mode == PCB_MODE_POLYGON ||
				Mode == PCB_MODE_POLYGON_HOLE || Mode == PCB_MODE_TEXT || Mode == PCB_MODE_INSERT_POINT || Mode == PCB_MODE_THERMAL) {
			Message(PCB_MSG_DEFAULT, _("That mode is NOT allowed when drawing ratlines!\n"));
			Mode = PCB_MODE_NO;
		}
	}
	if (conf_core.editor.mode == PCB_MODE_LINE && Mode == PCB_MODE_ARC && Crosshair.AttachedLine.State != STATE_FIRST) {
		Crosshair.AttachedLine.State = STATE_FIRST;
		Crosshair.AttachedBox.State = STATE_SECOND;
		Crosshair.AttachedBox.Point1.X = Crosshair.AttachedBox.Point2.X = Crosshair.AttachedLine.Point1.X;
		Crosshair.AttachedBox.Point1.Y = Crosshair.AttachedBox.Point2.Y = Crosshair.AttachedLine.Point1.Y;
		AdjustAttachedObjects();
	}
	else if (conf_core.editor.mode == PCB_MODE_ARC && Mode == PCB_MODE_LINE && Crosshair.AttachedBox.State != STATE_FIRST) {
		Crosshair.AttachedBox.State = STATE_FIRST;
		Crosshair.AttachedLine.State = STATE_SECOND;
		Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = Crosshair.AttachedBox.Point1.X;
		Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = Crosshair.AttachedBox.Point1.Y;
		sprintf(sMode, "%d", Mode);
		conf_set(CFR_DESIGN, "editor/mode", -1, sMode, POL_OVERWRITE);
		AdjustAttachedObjects();
	}
	else {
		if (conf_core.editor.mode == PCB_MODE_ARC || conf_core.editor.mode == PCB_MODE_LINE)
			SetLocalRef(0, 0, pcb_false);
		Crosshair.AttachedBox.State = STATE_FIRST;
		Crosshair.AttachedLine.State = STATE_FIRST;
		if (Mode == PCB_MODE_LINE && conf_core.editor.auto_drc) {
			if (ResetConnections(pcb_true)) {
				IncrementUndoSerialNumber();
				Draw();
			}
		}
	}

	sprintf(sMode, "%d", Mode);
	conf_set(CFR_DESIGN, "editor/mode", -1, sMode, POL_OVERWRITE);

	if (Mode == PCB_MODE_PASTE_BUFFER)
		/* do an update on the crosshair range */
		SetCrosshairRangeToBuffer();
	else
		SetCrosshairRange(0, 0, PCB->MaxWidth, PCB->MaxHeight);

	recursing = pcb_false;

	/* force a crosshair grid update because the valid range
	 * may have changed
	 */
	MoveCrosshairRelative(0, 0);
	notify_crosshair_change(pcb_true);
}

void SetLocalRef(Coord X, Coord Y, pcb_bool Showing)
{
	static MarkType old;
	static int count = 0;

	if (Showing) {
		notify_mark_change(pcb_false);
		if (count == 0)
			old = Marked;
		Marked.X = X;
		Marked.Y = Y;
		Marked.status = pcb_true;
		count++;
		notify_mark_change(pcb_true);
	}
	else if (count > 0) {
		notify_mark_change(pcb_false);
		count = 0;
		Marked = old;
		notify_mark_change(pcb_true);
	}
}
