		/********************************************************/
		/**** Routine de lecture et visu d'un fichier GERBER ****/
		/********************************************************/

#include "fctsys.h"

#include "common.h"
#include "gerbview.h"
#include "pcbplot.h"

#include "protos.h"

#define DEFAULT_SIZE 100

/* Format Gerber : NOTES :
Fonctions preparatoires:
	Gn =
	G01			interpolation lineaire ( trace de droites )
	G02,G20,G21	Interpolation circulaire , sens trigo < 0
	G03,G30,G31	Interpolation circulaire , sens trigo > 0
	G04			commentaire
	G06			Interpolation parabolique
	G07			Interpolation cubique
	G10			interpolation lineaire ( echelle 10x )
	G11			interpolation lineaire ( echelle 0.1x )
	G12			interpolation lineaire ( echelle 0.01x )
	G52			plot symbole reference par Dnn code
	G53			plot symbole reference par Dnn ; symbole tourne de -90 degres
	G54			Selection d'outil
	G55			Mode exposition photo
	G56			plot symbole reference par Dnn A code
	G57			affiche le symbole reference sur la console
	G58			plot et affiche le symbole reference sur la console
	G60			interpolation lineaire ( echelle 100x )
	G70			Unites = Inches
	G71			Unites = Millimetres
	G74			supprime interpolation circulaire sur 360 degre, revient a G01
	G75			Active interpolation circulaire sur 360 degre
	G90			Mode Coordonnees absolues
	G91			Mode Coordonnees Relatives

Coordonnees X,Y
	X,Y sont suivies de + ou - et de m+n chiffres (non separes)
			m = partie entiere
			n = partie apres la virgule
			 formats classiques : 	m = 2, n = 3 (format 2.3)
			 						m = 3, n = 4 (format 3.4)
 	ex:
	G__ X00345Y-06123 D__*

Outils et D_CODES
	numero d'outil ( identification des formes )
	1 a 99 	(classique)
	1 a 999
	D_CODES:

	D01 ... D9 = codes d'action:
	D01			= activation de lumiere (baisser de plume) lors du déplacement
	D02			= extinction de lumiere (lever de plume) lors du déplacement
	D03			= Flash
	D09			= VAPE Flash

	D10 ... = Indentification d'outils ( d'ouvertures )
*/

	/*********************************/
	/* class GERBER_Descr : Methodes */
	/*********************************/

GERBER_Descr::GERBER_Descr(int layer)
{
int ii;
	m_Layer = layer;			// Layer Number
	m_Selected_Tool = FIRST_DCODE;
	ResetDefaultValues();
	for ( ii = 0; ii <= MAX_TOOLS; ii++ )
		m_Aperture_List[ii] = new D_CODE(ii + FIRST_DCODE);
}




GERBER_Descr::~GERBER_Descr()
{
int ii;

	if (m_Aperture_List )
		{
		for ( ii = 0; ii < MAX_TOOLS; ii++)
			{
			delete m_Aperture_List[ii];
			m_Aperture_List[ii] = NULL;
			}
		}
}

/******************************************/
void GERBER_Descr::ResetDefaultValues()
/******************************************/
{
	m_Parent = NULL;
	m_Pback = NULL;
	m_Pnext = NULL;
	m_FileName.Empty();
	m_Name = wxT("no name");			// Layer name
	m_LayerNegative = FALSE;	// TRUE = Negative Layer
	m_ImageNegative = FALSE;	// TRUE = Negative image
	m_GerbMetric = FALSE;		// FALSE = Inches, TRUE = metric
	m_Relative = FALSE;			// FALSE = absolute Coord, RUE = relative Coord
	m_NoTrailingZeros = FALSE;	// True: zeros a droite supprimés
	m_MirorA = FALSE;			// True: miror / axe A (X)
	m_MirorB = FALSE;			// True: miror / axe B (Y)
	m_As_DCode = FALSE;			// TRUE = DCodes in file (FALSE = no DCode->
								// separate DCode file
	m_Offset.x = m_Offset.y = 0;		// Coord Offset

	m_FmtScale.x = m_FmtScale.y = g_Default_GERBER_Format % 10;
	m_FmtLen.x = m_FmtLen.y = m_FmtScale.x + (g_Default_GERBER_Format / 10);

	m_LayerScale.x = m_LayerScale.y = 1.0;	// scale (X et Y) pour cette layer
	m_Rotation = 0;
	m_Iterpolation = GERB_INTERPOL_LINEAR_1X;		// Linear, 90 arc, Circ.
	m_360Arc_enbl = FALSE;	// 360 deg circular interpolation disable
	m_Current_Tool = 0;		// Current Tool (Dcode) number selected
	m_CommandState = 0;		// donne l'etat de l'analyse des commandes gerber
	m_CurrentPos.x = m_CurrentPos.y = 0;	// current specified coord for plot
	m_PreviousPos.x = m_PreviousPos.y = 0;	// old current specified coord for plot
	m_IJPos.x = m_IJPos.y = 0;	// current centre coord for plot arcs & circles
	m_Current_File = NULL;		// File to read
	m_FilesPtr = 0;
	m_Transform[0][0] = m_Transform[1][1] = 1;
	m_Transform[0][1] = m_Transform[1][0] = 0;	// Rotation/mirror = Normal
	m_PolygonFillMode = FALSE;
	m_PolygonFillModeState = 0;
}

/********************************************/
int GERBER_Descr::ReturnUsedDcodeNumber()
/********************************************/
{
int ii, jj;

	jj = 0;
	if (m_Aperture_List )
		{
		for ( ii = 0; ii < MAX_TOOLS; ii++)
			{
			if( m_Aperture_List[ii]->m_InUse || m_Aperture_List[ii]->m_Defined )
				jj++;
			}
		}
	return jj;
}



/******************************/
void GERBER_Descr::InitToolTable()
/******************************/
/* Creation du tableau des MAX_TOOLS DCodes utilisables, si il n'existe pas,
et Init des DCodes à une valeur raisonnable
*/
{
int count;

	/* Init du buffer des D_CODES a des valeurs raisonnables */
	for (count = 0 ; count < MAX_TOOLS ; count++)
		{
		if (m_Aperture_List[count] == NULL ) continue;
		m_Aperture_List[count]->m_Num_Dcode = count + FIRST_DCODE;
		m_Aperture_List[count]->Clear_D_CODE_Data();
		}
}


	/*************************/
	/* Class DCODE: methodes */
	/*************************/

/* Variables locales : */

/* Routines Locales */


D_CODE::D_CODE(int num_dcode)
{
	m_Num_Dcode = num_dcode;
	Clear_D_CODE_Data();
}

D_CODE::~D_CODE()
{
}

void D_CODE::Clear_D_CODE_Data()
{
	m_Size.x = DEFAULT_SIZE;
	m_Size.y = DEFAULT_SIZE;
	m_Shape= GERB_CIRCLE;
	m_Drill.x = m_Drill.y = 0;
	m_DrillShape = 0;
	m_InUse = FALSE;
	m_Defined = FALSE;
}


/******************************************************************************/
int WinEDA_GerberFrame::Read_D_Code_File( const wxString & D_Code_FullFileName)
/******************************************************************************/
/* Routine de Lecture d'un fichier de D Codes.
	Accepte format standard ou ALSPCB
		un ';' demarre un commentaire.

	Format Standard:
	tool,	 Horiz,		  Vert,	  drill, vitesse, acc. ,Type ; [DCODE (commentaire)]
ex:	   1,		  12,		12,		0,		  0,	 0,	  3 ; D10

	Format:
	Ver  ,  Hor , Type , Tool [,Drill]
ex:	0.012, 0.012,  L   , D10

	Classe les caract en buf_tmp sous forme de tableau de structures D_CODE.
	Retourne:
		< 0 si erreur:
			-1 = Fichier non trouve
			-2 = Erreur lecture fichier
		0 si pas de nom de fichier (inits seules)
		1 si OK
*/
{
int current_Dcode, ii, dcode_scale;
char* ptcar;
int dimH, dimV, drill, type_outil, dummy;
float fdimH, fdimV, fdrill;
char c_type_outil[256];
char Line[1024];
wxString msg;
D_CODE * pt_Dcode;
FILE * dest;
int layer = GetScreen()->m_Active_Layer;
D_CODE ** ListeDCode;


	if ( g_GERBER_Descr_List[layer] == NULL )
	{
		g_GERBER_Descr_List[layer] = new GERBER_Descr(layer);
	}

	/* Mise a jour de l'echelle gerber : */
	dcode_scale = 10;	/* ici unit dcode = mil, unit interne = 0.1 mil
							 -> 1 unite dcode = 10 unit PCB */
	current_Dcode = 0;

	if ( D_Code_FullFileName.IsEmpty() ) return 0;

	dest = wxFopen(D_Code_FullFileName, wxT("rt") );

	if (dest == 0)
		{
		msg = _("File ") + D_Code_FullFileName + _(" not found");
		DisplayError(this, msg, 10);
		return -1;
		}

	g_GERBER_Descr_List[layer]->InitToolTable();

	ListeDCode = g_GERBER_Descr_List[layer]->m_Aperture_List;

	while( fgets(Line,255,dest) != NULL)
		{
		if (*Line == ';') continue; /* Commentaire */
		if (strlen(Line) < 10 ) continue ; /* Probablemant ligne vide */

		pt_Dcode = NULL; current_Dcode = 0;
		/* Determination du type de fichier D_Code */
		ptcar = Line ; ii = 0 ;
		while( *ptcar ) if (*(ptcar++) == ',') ii++ ;

		if (ii >= 6 )	/* valeurs en mils */
			{
			sscanf(Line,"%d,%d,%d,%d,%d,%d,%d",&ii,
									&dimH, &dimV, &drill,
									&dummy,&dummy,
									&type_outil);
			dimH  = (int)( (dimH * dcode_scale) + 0.5) ;
			dimV  = (int)( (dimV * dcode_scale) + 0.5) ;
			drill = (int)( (drill * dcode_scale) + 0.5);
			if ( ii < 1 ) ii = 1;
			current_Dcode = ii-1 + FIRST_DCODE;
			}
		else 		/* valeurs en inches a convertir en mils */
			{
			fdrill = 0 ; current_Dcode = 0 ; fdrill = 0 ;
			sscanf(Line,"%f,%f,%1s",&fdimV, &fdimH, c_type_outil);
			ptcar = Line ;
			while( *ptcar )
				{
				if (*ptcar == 'D')
					{
					sscanf(ptcar+1,"%d,%f", &current_Dcode,&fdrill); break ;
					}
				else ptcar++ ;
				}
			dimH  = (int)( (fdimH * dcode_scale * 1000) + 0.5);
			dimV  = (int)( (fdimV * dcode_scale * 1000) + 0.5);
			drill = (int)( (fdrill * dcode_scale * 1000) + 0.5);
			type_outil = -1 ;
			if(c_type_outil[0] == 'L') type_outil = GERB_LINE;
			if(c_type_outil[0] == 'R') type_outil = GERB_RECT;
			if(c_type_outil[0] == 'C') type_outil = GERB_CIRCLE;
			if(c_type_outil[0] == 'O') type_outil = GERB_OVALE;
			if(type_outil == -1) {fclose(dest); return(-2);}

			}
		/* Mise a jour de la liste des d_codes si valeurs lues coherentes*/
		if (current_Dcode < FIRST_DCODE ) continue;
		if (current_Dcode >= MAX_TOOLS ) continue;
		pt_Dcode = ReturnToolDescr(layer, current_Dcode);
		pt_Dcode->m_Size.x = dimH;
		pt_Dcode->m_Size.y = dimV;
		pt_Dcode->m_Shape = type_outil;
		pt_Dcode->m_Drill.x = pt_Dcode->m_Drill.y = drill;
		pt_Dcode->m_Defined = TRUE;
		}
	fclose(dest) ;

	return(1);
}


/***************************************************/
void WinEDA_GerberFrame::CopyDCodesSizeToItems()
/***************************************************/
/* Set Size Items (Lines, Flashes) from DCodes List
*/
{
TRACK * track;
D_CODE * pt_Dcode;	/* Pointeur sur le D code*/

	track = m_Pcb->m_Track;
	for ( ; track != NULL ; track = (TRACK*) track->Pnext )
		{
		pt_Dcode = ReturnToolDescr(track->GetLayer(), track->GetNet());
		pt_Dcode->m_InUse = TRUE;

		if (		// Line Item
			(track->m_Shape == S_SEGMENT ) ||	/* segment rectiligne */
			(track->m_Shape == S_RECT ) ||		/* segment forme rect (i.e. bouts non arrondis) */
			(track->m_Shape == S_ARC ) ||		/* segment en arc de cercle (bouts arrondis)*/
			(track->m_Shape == S_CIRCLE ) ||	/* segment en cercle (anneau)*/
			(track->m_Shape == S_ARC_RECT )	/* segment en arc de cercle (bouts droits) (GERBER)*/
			)
			{
			track->m_Width = pt_Dcode->m_Size.x;
			}

		else		// Spots ( Flashed Items )
			{
			int width, len;
			wxSize size = pt_Dcode->m_Size;

			width = MIN( size.x, size.y );
			len = MAX( size.x, size.y ) - width;

			track->m_Width = width;

			track->m_Start.x = (track->m_Start.x + track->m_End.x) / 2;
			track->m_Start.y = (track->m_Start.y + track->m_End.y) / 2;
			track->m_End = track->m_Start;	// m_Start = m_End = Spot center
			switch (pt_Dcode->m_Shape)
				 {
				case GERB_LINE :	// ne devrait pas etre utilisé ici
				case GERB_CIRCLE :		/* spot rond (for GERBER)*/
					track->m_Shape = S_SPOT_CIRCLE;
					break ;

				case GERB_OVALE :	/* spot ovale (for GERBER)*/
					track->m_Shape = S_SPOT_OVALE;
					break ;

				default:			/* spot rect (for GERBER)*/
					track->m_Shape = S_SPOT_RECT;
					break ;
				 }

			len >>= 1;
			if ( size.x > size.y )
				{
				track->m_Start.x -= len;
				track->m_End.x += len;
				}

			else
				{
				track->m_Start.y -= len;
				track->m_End.y += len;
				}
			}
		}
}


/*********************************************************/
D_CODE * ReturnToolDescr(int layer, int Dcode, int * index)
/*********************************************************/

/* Retourne un pointeur sur la description de l'outil DCode de reference Dcode
	(rappel : Dcode >= 10)
*/
{
GERBER_Descr * DcodeList = g_GERBER_Descr_List[layer];
D_CODE * pt_dcode = NULL;

	if (index) *index = 0;
	if ( DcodeList && Dcode >= FIRST_DCODE && Dcode < MAX_TOOLS)
		{
		pt_dcode = DcodeList->m_Aperture_List[Dcode - FIRST_DCODE];
		if ( index ) *index = Dcode - FIRST_DCODE + 1;
		}
	return pt_dcode;
}


/************************************************/
void WinEDA_GerberFrame::Liste_D_Codes(wxDC * DC)
/************************************************/
{
int ii, jj;
D_CODE * pt_D_code ;
wxString Line;
WinEDA_TextFrame * List;
int scale = 10000;
int curr_layer = GetScreen()->m_Active_Layer;
int layer;
GERBER_Descr * DcodeList;

	/* Construction de la liste des messages */
	List = new WinEDA_TextFrame(this, _("List D codes"));

	for (layer = 0; layer < 32; layer++ )
		{
		DcodeList = g_GERBER_Descr_List[layer];
		if ( DcodeList == NULL ) continue;
		if ( DcodeList->ReturnUsedDcodeNumber() == 0 )
			continue;
		if (layer == curr_layer )
			Line.Printf( wxT("*** Active layer (%2.2d) ***"), layer+1);
		else Line.Printf( wxT("*** layer %2.2d  ***"), layer+1);
		List->Append(Line);

		for ( ii = 0, jj = 1 ; ii < MAX_TOOLS ; ii++ )
			{
			pt_D_code = DcodeList->m_Aperture_List[ii] ;
			if ( pt_D_code == NULL ) continue;
			if ( !pt_D_code->m_InUse && !pt_D_code->m_Defined ) continue;
			Line.Printf( wxT(
				"tool %2.2d:   D%2.2d  V %2.4f  H %2.4f  %s"),
				jj,
				pt_D_code->m_Num_Dcode,
				(float)pt_D_code->m_Size.y /scale,
				(float)pt_D_code->m_Size.x /scale,
				g_GERBER_Tool_Type[pt_D_code->m_Shape] );
			if ( ! pt_D_code->m_Defined )  Line += wxT(" ?");
			if ( ! pt_D_code->m_InUse ) Line += wxT(" *");

			List->Append(Line);
			jj++;
			}
		}
	ii = List->ShowModal(); List->Destroy();
	if (ii < 0) return;

#if 0
	// Mise en surbrillance des éléments correspondant au DCode sélectionné
	if(Etat_Surbrillance) Hight_Light(DrawPanel, DC);
	net_code_Surbrillance = (GetScreen()->m_Active_Layer<< 16) + ii;
	Hight_Light(DrawPanel, DC);
#endif
}


