/*********************************/
	/* Module de nettoyage du schema */
	/*********************************/

#include "fctsys.h"
#include "gr_basic.h"

#include "common.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"
#include "netlist.h"
#include "macros.h"
#include "protos.h"


/* Routines locales */
static int TstAlignSegment(EDA_DrawLineStruct* RefSegm, EDA_DrawLineStruct* TstSegm);

/* Variable locales */


/*******************************************/
bool SCH_SCREEN::SchematicCleanUp(wxDC * DC)
/*******************************************/
/* Routine de nettoyage:
	- regroupe les segments de fils (ou de bus) alignes en 1 seul segment
	- Detecte les objets identiques superposes
*/
{
EDA_BaseStruct *DrawList, * TstDrawList;
int flag;
bool Modify = FALSE;

	DrawList = EEDrawList;
	for ( ;DrawList != NULL; DrawList = DrawList->Pnext )
	{
		if( DrawList->m_StructType == DRAW_SEGMENT_STRUCT_TYPE )
		{
			TstDrawList = DrawList->Pnext;
			while ( TstDrawList )
			{
				if( TstDrawList->m_StructType == DRAW_SEGMENT_STRUCT_TYPE )
				{
					flag = TstAlignSegment( (EDA_DrawLineStruct*)DrawList,
										(EDA_DrawLineStruct*)TstDrawList);
					if (flag )	/* Suppression de TstSegm */
					{
						/* keep the bits set in .m_Flags, because the deleted segment can be flagged */
						DrawList->m_Flags |= TstDrawList->m_Flags;
						EraseStruct(TstDrawList, this);
						SetRefreshReq();
						TstDrawList = EEDrawList;
						Modify = TRUE;
					}
					else TstDrawList = TstDrawList->Pnext;
				}
				else TstDrawList = TstDrawList->Pnext;
			}
		}
	}
	EDA_Appl->SchematicFrame->TestDanglingEnds(EEDrawList, DC);
	return Modify;
}


/***********************************************/
void BreakSegmentOnJunction( SCH_SCREEN * Screen )
/************************************************/
/* Routine creant des debuts / fin de segment (BUS ou WIRES) sur les jonctions
et les raccords
*/
{
EDA_BaseStruct *DrawList;

	if( Screen == NULL )
	{
		DisplayError(NULL, wxT("BreakSegmentOnJunction() error: NULL screen"));
		return;
	}

	DrawList = Screen->EEDrawList;
	while ( DrawList )
		{
		switch( DrawList->m_StructType )
		{
			case DRAW_JUNCTION_STRUCT_TYPE :
				#undef STRUCT
				#define STRUCT ((DrawJunctionStruct*)DrawList)
				BreakSegment(Screen, STRUCT->m_Pos);
				break;

			case DRAW_BUSENTRY_STRUCT_TYPE :
				#undef STRUCT
				#define STRUCT ((DrawBusEntryStruct*)DrawList)
				BreakSegment(Screen, STRUCT->m_Pos);
				BreakSegment(Screen, STRUCT->m_End());
				break;

			case DRAW_SEGMENT_STRUCT_TYPE :
			case DRAW_NOCONNECT_STRUCT_TYPE :
			case DRAW_LABEL_STRUCT_TYPE :
			case DRAW_GLOBAL_LABEL_STRUCT_TYPE :
			case DRAW_LIB_ITEM_STRUCT_TYPE :
			case DRAW_PICK_ITEM_STRUCT_TYPE :
			case DRAW_POLYLINE_STRUCT_TYPE :
			case DRAW_MARKER_STRUCT_TYPE :
			case DRAW_TEXT_STRUCT_TYPE :
			case DRAW_SHEET_STRUCT_TYPE :
			case DRAW_SHEETLABEL_STRUCT_TYPE :
				break;

			default :
				break;
		}
		DrawList = DrawList->Pnext;
	}
}


/*********************************************************/
DrawPickedStruct * BreakSegment(SCH_SCREEN * screen,
			wxPoint breakpoint, bool PutInUndoList)
/*********************************************************/
/* Coupe un segment ( BUS, WIRE ) en 2 au point breakpoint,
	- si ce point est sur le segment
	- extremites non comprises
  If PutInUndoList == TRUE, create a list of modifictions, for undo command
*/
{
EDA_BaseStruct *DrawList;
EDA_DrawLineStruct * segment, * NewSegment;
int ox, oy, fx, fy;
DrawPickedStruct * List = NULL;
	
	DrawList = screen->EEDrawList;
	while ( DrawList )
	{
		switch( DrawList->m_StructType )
		{
			case DRAW_SEGMENT_STRUCT_TYPE :
				segment = (EDA_DrawLineStruct*)DrawList;
				ox = segment->m_Start.x; oy = segment->m_Start.y;
				fx = segment->m_End.x; fy = segment->m_End.y;
				if( distance( fx - ox, fy - oy, breakpoint.x - ox, breakpoint.y - oy, 0 ) == 0 )
					break;
				/* Segment connecte: doit etre coupe en 2 si px,py n'est
					pas une extremite */
				if( (ox == breakpoint.x) && (oy == breakpoint.y ) ) break;
				if( (fx == breakpoint.x) && (fy == breakpoint.y ) ) break;
				/* Ici il faut couper le segment en 2 */
				if ( PutInUndoList )	// First: put copy of the old segment in undo list
				{
					DrawPickedStruct * wrapper = new DrawPickedStruct();
					wrapper->m_Flags = IS_CHANGED;
					wrapper->m_PickedStruct = segment->GenCopy();
					wrapper->m_Image = segment;
					wrapper->m_PickedStruct->m_Image = segment;
					wrapper->Pnext = List;
					List = wrapper;
				}
				NewSegment = segment->GenCopy();
				NewSegment->m_Start = breakpoint;
				segment->m_End = NewSegment->m_Start;
				NewSegment->Pnext = segment->Pnext;
				segment->Pnext = NewSegment;
				DrawList = NewSegment;
				if ( PutInUndoList )
				{
					DrawPickedStruct * wrapper = new DrawPickedStruct();
					wrapper->m_Flags = IS_NEW;
					wrapper->m_Image = NewSegment;
					wrapper->Pnext = List;
					List = wrapper;
				}
				break;

			case DRAW_JUNCTION_STRUCT_TYPE :
			case DRAW_BUSENTRY_STRUCT_TYPE :
			case DRAW_POLYLINE_STRUCT_TYPE :
				break;

			default :
				break;
		}
		DrawList = DrawList->Pnext;
	}
	
	return List;
}



/***********************************************************/
static int TstAlignSegment( EDA_DrawLineStruct* RefSegm, 
							EDA_DrawLineStruct* TstSegm)
/***********************************************************/

/* Search if the 2 segments RefSegm and TstSegm are on a line.
	Retourn 0 if no
		1 if yes, and RefSegm is modified to be the equivalent segment
*/
{
	if( RefSegm == TstSegm ) return(0);
	if( RefSegm->m_Layer != TstSegm->m_Layer ) return(0);
	
	// search for a common end, and modify coordinates to ensure RefSegm->m_End == TstSegm->m_Start
	if ( RefSegm->m_Start == TstSegm->m_Start )
	{
		if ( RefSegm->m_End == TstSegm->m_End )			// trivial case: RefSegm and TstSegm are identical
			return 1;
		EXCHG(RefSegm->m_Start, RefSegm->m_End);	// at this point, RefSegm->m_End == TstSegm->m_Start
	}
	else if ( RefSegm->m_Start == TstSegm->m_End )
	{
		EXCHG(RefSegm->m_Start, RefSegm->m_End);
		EXCHG(TstSegm->m_Start, TstSegm->m_End);	// at this point, RefSegm->m_End == TstSegm->m_Start
	}
	else if ( RefSegm->m_End == TstSegm->m_End )
	{
		EXCHG(TstSegm->m_Start, TstSegm->m_End);	// at this point, RefSegm->m_End == TstSegm->m_Start
	}
	else if ( RefSegm->m_End != TstSegm->m_Start )		// No common end point, segments cannot be merged
		return 0;

	/* Test alignment: */
	if ( RefSegm->m_Start.y == RefSegm->m_End.y )		// Horizontal segment
	{
		if ( TstSegm->m_Start.y == TstSegm->m_End.y )
		{
			RefSegm->m_End = TstSegm->m_End;
			return 1;
		}
	}
	
	else if ( RefSegm->m_Start.x == RefSegm->m_End.x )	// Vertical segment
	{
		if ( TstSegm->m_Start.x == TstSegm->m_End.x )
		{
			RefSegm->m_End = TstSegm->m_End;
			return 1;
		}
	}
	
	else
	{
		if (atan2(RefSegm->m_Start.x - RefSegm->m_End.x, RefSegm->m_Start.y - RefSegm->m_End.y) ==
				atan2(TstSegm->m_Start.x - TstSegm->m_End.x, TstSegm->m_Start.y - TstSegm->m_End.y) )
		{
			RefSegm->m_End = TstSegm->m_End;
			return 1;
		}
	}
	
	return(0);
}