/***********************************************************/
/*	Module to handle libraries (first part - file and io). */
/***********************************************************/

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

#include "common.h"
#include "trigo.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"

#include "protos.h"

/* Variables Locales */

/* Fonctions locales */
/* pour librairies de composants */
static LibEDA_BaseStruct *GetDrawEntry(WinEDA_DrawFrame * frame, FILE *f,
			char *Line, int *LineNum);
static bool GetLibEntryField(EDA_LibComponentStruct *LibEntry, char * line);
static bool AddAliasNames(EDA_LibComponentStruct *LibEntry, char * line);
static void InsertAlias(PriorQue ** PQ,
						EDA_LibComponentStruct *LibEntry, int *NumOfParts);
static bool ReadLibEntryDateAndTime(EDA_LibComponentStruct * LibEntry, char * Line);
static int AddFootprintFilterList(EDA_LibComponentStruct *LibEntryLibEntry,
	FILE * f, char * Line, int *LineNum);

/* pour doc librairies */


	/****************************************************/
	/* Routines de lecture des librairies de composants */
	/****************************************************/


/*****************************************************************************
* Routine to load the given library name. FullLibName should hold full path  *
* of file name to open, while LibName should hold only its name.			 *
* IF library already exists, it is NOT reloaded.							 *
* return: new lib or NULL													 *
*****************************************************************************/
LibraryStruct * LoadLibraryName(WinEDA_DrawFrame * frame,
				const wxString & FullLibName, const wxString & LibName)
{
int NumOfParts;
FILE *f;
LibraryStruct *NewLib;
PriorQue *Entries;
wxString FullFileName;

	if ( (NewLib = FindLibrary(LibName)) != NULL)
	{
		if ( NewLib->m_FullFileName == FullLibName )
			return NewLib;
		FreeCmpLibrary(frame, LibName);
	}

	NewLib = NULL;

	f = wxFopen(FullLibName, wxT("rt") );
	if (f == NULL)
	{
		wxString msg;
		msg.Printf( _("Library <%s> not found"), FullLibName.GetData());
		DisplayError(frame, msg);
		return NULL;
	}
		
	NewLib = new LibraryStruct(LIBRARY_TYPE_EESCHEMA, LibName, FullLibName);
	Entries = LoadLibraryAux(frame, NewLib, f, &NumOfParts);
	if ( Entries != NULL)
	{
		NewLib->m_Entries = Entries;
		NewLib->m_NumOfParts = NumOfParts;
		
		if ( g_LibraryList == NULL ) g_LibraryList = NewLib;
		else
		{
			LibraryStruct *tmplib = g_LibraryList;
			while ( tmplib->m_Pnext ) tmplib = tmplib->m_Pnext;
			tmplib->m_Pnext = NewLib;
		}

		FullFileName = FullLibName;
		ChangeFileNameExt(FullFileName, DOC_EXT);
		LoadDocLib(frame, FullFileName, NewLib->m_Name);
	}
	else delete NewLib;
		
	fclose(f);
	return NewLib;
}


/******************************************/
void LoadLibraries(WinEDA_DrawFrame * frame)
/******************************************/
/* Delete toutes les librairies chargees et recree toutes les librairies
donnes dans la liste g_LibName_List
*/
{
wxString FullLibName, msg;
wxString LibName;
unsigned ii, iimax = g_LibName_List.GetCount();

	frame->PrintMsg( _("Start loading schematic libs"));

	// Free the unwanted libraries (i.e. not in list) but keep the .cache lib
LibraryStruct *nextlib, *lib = g_LibraryList;
	for (; lib != NULL; lib = nextlib )
	{
		nextlib = lib->m_Pnext;
		if ( lib->m_IsLibCache )  continue;

		wxString libname = lib->m_Name;

		// is this library in "wanted list" g_LibName_List ?
		int test = g_LibName_List.Index(libname);
		if ( test == wxNOT_FOUND ) FreeCmpLibrary(frame, libname);
	}

	// Load missing libraries (if any)
	for ( ii = 0 ; ii < iimax; ii++)
	{
		LibName = g_LibName_List[ii];

		if( LibName.IsEmpty() ) continue;
		FullLibName = MakeFileName(g_RealLibDirBuffer, LibName, g_LibExtBuffer);
		msg = wxT("Loading ") + FullLibName;
		if ( LoadLibraryName(frame, FullLibName, LibName) )
			msg += wxT(" OK");
		else
			msg += wxT(" ->Error");
		frame->PrintMsg( msg );
	}
	
	// reorder the linked list to match the order filename list:
	int NumOfLibs;
	for (NumOfLibs = 0, lib = g_LibraryList; lib != NULL; lib = lib->m_Pnext)
	{
		lib->m_Flags = 0;
		NumOfLibs++;
	}
	if ( NumOfLibs == 0 ) return;
		
	LibraryStruct ** libs =
		(LibraryStruct **) MyZMalloc(sizeof(LibraryStruct *) * (NumOfLibs + 2));

	int jj = 0;
	for (ii = 0; ii < g_LibName_List.GetCount(); ii++)
	{
		if ( jj >= NumOfLibs ) break;
		lib = FindLibrary(g_LibName_List[ii]);
		if ( lib )
		{
			lib->m_Flags = 1;
			libs[jj++] = lib;
		}
	}
	/* Put lib cache at end of list */
	for (lib = g_LibraryList; lib != NULL; lib = lib->m_Pnext)
	{
		if ( lib->m_Flags == 0 ) libs[jj++] = lib;
	}
	libs[jj] = NULL;

	/* Change the linked list pointers */
	for (ii = 0; libs[ii] != NULL; ii++)
		libs[ii]->m_Pnext = libs[ii+1];

	g_LibraryList = libs[0];
	
	MyFree(libs);

	for (lib = g_LibraryList; lib != NULL; lib = lib->m_Pnext)
		lib->m_Flags = 0;
}

/*****************************************************************************
* Routine to free a library from the current loaded libraries.				 *
*****************************************************************************/
void FreeCmpLibrary(wxWindow * frame, const wxString & LibName)
{
int NumOfLibs = NumOfLibraries();
LibraryStruct *Lib, *TempLib;

	if (NumOfLibs == 0)
	{
		DisplayError(frame, wxT("No libraries are loaded"),20);
		return;
	}

	 /* Search for this library name: */
	for (Lib = g_LibraryList; Lib != NULL; Lib = Lib->m_Pnext)
	{
		if (LibName == Lib->m_Name) break;
	}

	if (Lib == NULL) return;

	if ( Lib == g_LibraryList) g_LibraryList = Lib->m_Pnext;
	else
	{
		for( TempLib = g_LibraryList; TempLib->m_Pnext != Lib; TempLib=TempLib->m_Pnext);
		TempLib->m_Pnext = TempLib->m_Pnext->m_Pnext;
	}

	delete Lib;

	/* La librairie supprimee est peut etre celle selectee dans libedit */
	if ( Lib == CurrentLib ) CurrentLib = NULL;
}

/******************************/
const wxChar **GetLibNames(void)
/******************************/
/* Routine to return pointers to all library names.
	User is responsible to deallocate memory
*/
{
int ii, NumOfLibs = NumOfLibraries();
const wxChar **Names;
LibraryStruct *Lib;

	Names = (const wxChar **) MyZMalloc(sizeof(wxChar *) * (NumOfLibs + 1));
	for (ii = 0, Lib = g_LibraryList; Lib != NULL; Lib = Lib->m_Pnext, ii++)
	{
		Names[ii] = Lib->m_Name.GetData();
	}
	Names[ii] = NULL;

	return Names;
}


/*****************************************************************************
* Routine to compare two EDA_LibComponentStruct for the PriorQue module.		 *
* Comparison (insensitive  case) is based on Part name.											 *
*****************************************************************************/
int LibraryEntryCompare(EDA_LibComponentStruct *LE1, EDA_LibComponentStruct *LE2)
{
	return LE1->m_Name.m_Text.CmpNoCase(LE2->m_Name.m_Text);
}

/*****************************************************************************
* Routine to load a library from given open file.							 *
*****************************************************************************/
PriorQue *LoadLibraryAux(WinEDA_DrawFrame * frame, LibraryStruct * Library, FILE *libfile, int *NumOfParts)
{
int LineNum = 0;
char Line[1024];
PriorQue *PQ = NULL;
EDA_LibComponentStruct *LibEntry;
wxString msg;
	
wxBusyCursor ShowWait;		// Display a Busy Cursor..

	*NumOfParts = 0;

	if ( GetLine(libfile, Line, &LineNum, sizeof(Line) ) == NULL)
		{
		msg = _("File <") + Library->m_Name + _("> is empty!");
		DisplayError(frame, msg);
		return NULL;
		}

	if( strnicmp(Line, LIBFILE_IDENT, 10) != 0)
		{
		msg = _("File <") + Library->m_Name + _("> is NOT EESCHEMA library!");
		DisplayError(frame, msg);
		return NULL;
		}

	if ( Library ) Library->m_Header = CONV_FROM_UTF8(Line);

	PQInit(&PQ);
	PQCompFunc((PQCompFuncType) LibraryEntryCompare);

	while (GetLine(libfile, Line, &LineNum, sizeof(Line)) )
	{
		if (strnicmp(Line, "$HEADER", 7) == 0)
		{
			if ( Library )
			{
				if ( ! Library->ReadHeader(libfile, &LineNum) )
				{
				msg = _("Library <") + Library->m_Name + _("> header read error");
				DisplayError(frame, msg, 30);
				}
			}
			continue;
		}
		
		if (strnicmp(Line, "DEF", 3) == 0)
		{
			/* Read one DEF/ENDDEF part entry from library: */
			LibEntry = Read_Component_Definition(frame, Line, libfile, &LineNum);
			if ( LibEntry )
			{
				/* If we are here, this part is O.k. - put it in: */
				++*NumOfParts; 
				PQInsert(&PQ, LibEntry);
				InsertAlias(&PQ, LibEntry, NumOfParts);
			}
		}
	}

	return PQ;
}


/*********************************************************************************************/
EDA_LibComponentStruct * Read_Component_Definition(WinEDA_DrawFrame * frame, char * Line,
		FILE *f, int *LineNum)
/*********************************************************************************************/
/* Routine to Read a DEF/ENDDEF part entry from given open file.
*/
{
int unused;
char  *p, *Name, *Prefix = NULL;
EDA_LibComponentStruct *LibEntry = NULL;
bool Res;
wxString Msg;

	p = strtok(Line, " \t\r\n");

	if (strcmp(p, "DEF") != 0)
	{
		Msg.Printf( wxT("DEF command expected in line %d, aborted."), *LineNum);
		DisplayError(frame, Msg);
		return NULL;
	}

	/* Read DEF line: */
	char drawnum = 0, drawname = 0;
	LibEntry = new EDA_LibComponentStruct( NULL);
	
	if ((Name = strtok(NULL, " \t\n")) == NULL ||	/* Part name: */
		(Prefix = strtok(NULL, " \t\n")) == NULL || /* Prefix name: */
		(p = strtok(NULL, " \t\n")) == NULL ||		/* NumOfPins: */
		sscanf(p, "%d", &unused) != 1 ||
		(p = strtok(NULL, " \t\n")) == NULL ||		/* TextInside: */
		sscanf(p, "%d", &LibEntry->m_TextInside) != 1 ||
		(p = strtok(NULL, " \t\n")) == NULL ||		  /* DrawNums: */
		sscanf(p, "%c", &drawnum) != 1 ||
		(p = strtok(NULL, " \t\n")) == NULL ||		  /* DrawNums: */
		sscanf(p, "%c", &drawname) != 1 ||
		(p = strtok(NULL, " \t\n")) == NULL ||		/* m_UnitCount: */
		sscanf(p, "%d", &LibEntry->m_UnitCount) != 1 )
		{
			Msg.Printf( wxT("Wrong DEF format in line %d, skipped."),*LineNum);
			DisplayError(frame, Msg);
			while (GetLine(f, Line, LineNum, 1024) )
				{
				p = strtok(Line, " \t\n");
				if (stricmp(p, "ENDDEF") == 0) break;
				}
			return NULL;
		}

	else	/* Mise a jour des infos de la ligne "DEF" */
	{
		LibEntry->m_DrawPinNum = (drawnum == 'N') ? FALSE : TRUE;
		LibEntry->m_DrawPinName = (drawname == 'N') ? FALSE : TRUE;
		/* Copy part name and prefix. */
		strupper(Name);
		if(Name[0] != '~') LibEntry->m_Name.m_Text = CONV_FROM_UTF8(Name);
		else
		{
			LibEntry->m_Name.m_Text = CONV_FROM_UTF8(&Name[1]);
			LibEntry->m_Name.m_Attributs |= TEXT_NO_VISIBLE;
		}

		if (strcmp(Prefix, "~") == 0)
		{
			LibEntry->m_Prefix.m_Text.Empty();
			LibEntry->m_Prefix.m_Attributs |= TEXT_NO_VISIBLE;
		}
		else LibEntry->m_Prefix.m_Text = CONV_FROM_UTF8(Prefix);
			
		// Copy optional infos
		if ( (p = strtok(NULL, " \t\n")) != NULL ) // m_UnitSelectionLocked param
		{
			if ( *p == 'L') LibEntry->m_UnitSelectionLocked = TRUE;
		}
		if ( (p = strtok(NULL, " \t\n")) != NULL )		/* Type Of Component */
		{
			if ( *p == 'P') LibEntry->m_Options = ENTRY_POWER;
		}
	}

	/* Analyse lignes suivantes */
	while (GetLine(f, Line, LineNum, 1024) )
	{
		p = strtok(Line, " \t\n");
		Res = TRUE; /* Pour test d'erreur (Res = FALSE = erreur) */

		if( (Line[0] == 'T') && (Line[1] == 'i') )
		{
			Res = ReadLibEntryDateAndTime(LibEntry, Line);
		}

		else if(Line[0] == 'F')
		{
			Res = GetLibEntryField(LibEntry, Line);
		}

		else if (strcmp(p, "ENDDEF") == 0)
		{
			break;
		}

		else if (strcmp(p, "DRAW") == 0)
		{
			LibEntry->m_Drawings = GetDrawEntry(frame, f, Line, LineNum);
		}

		else if(strncmp(p, "ALIAS", 5) == 0 )
		{
			p = strtok(NULL, "\r\n");
			Res = AddAliasNames(LibEntry, p);
		}

		else if(strncmp(p, "$FPLIST", 5) == 0 )
		{
			Res = AddFootprintFilterList(LibEntry, f, Line, LineNum);
		}

		else
		{
			Msg.Printf( wxT("Undefined command \"%s\" in line %d, skipped."), p, * LineNum);
			frame->PrintMsg(Msg);
		}

		/* Fin analyse de la ligne ou block: test de l'info lue */
		if (!Res)
		{			/* Something went wrong there. */
			Msg.Printf( wxT(" Error Line %d, Library not loaded"), *LineNum);
			DisplayError(frame, Msg);
			delete LibEntry;
			return NULL;
		}
	}

	/* If we are here, this part is O.k. - put it in: */
	LibEntry->SortDrawItems();
	return LibEntry;
}



/*****************************************************************************
* Routine to load a DRAW definition from given file. Note "DRAW" line has	 *
* been read already. Reads upto and include ENDDRAW, or an error (NULL ret). *
*****************************************************************************/

static LibEDA_BaseStruct *GetDrawEntry(WinEDA_DrawFrame * frame, FILE *f, char *Line, int *LineNum)
{
int i = 0, jj, ll, Unit, Convert, size1, size2;
char *p, Buffer[1024], BufName[256],
		 PinNum[256],
		 chartmp[256], chartmp1[256];
wxString MsgLine;
bool Error = FALSE;
LibEDA_BaseStruct *Tail = NULL,
					*New = NULL,
					*Head = NULL;

	while (TRUE)
		{
		if (GetLine(f, Line, LineNum, 1024 ) == NULL)
			{
			DisplayError(frame, wxT("File ended prematurely"));
			return Head;
			}

		if (strncmp(Line, "ENDDRAW", 7) == 0)
			{
			break;
			}

		New = NULL;

		switch (Line[0])
			{
			case 'A': /* Arc */
				{
				int startx, starty, endx, endy;
				LibDrawArc * Arc = new LibDrawArc();
				New = Arc;
				ll = 0;
				int nbarg = sscanf(&Line[2], "%d %d %d %d %d %d %d %d %s %d %d %d %d",
						&Arc->m_Pos.x, &Arc->m_Pos.y, &Arc->m_Rayon,
						&Arc->t1, &Arc->t2, &Unit, &Convert,
						&Arc->m_Width, chartmp, &startx, &starty, &endx, &endy);
				if ( nbarg < 8 ) Error = TRUE;
				Arc->m_Unit = Unit; Arc->m_Convert = Convert;
				if ( chartmp[0] == 'F') Arc->m_Fill = FILLED_SHAPE;
				if ( chartmp[0] == 'f') Arc->m_Fill = FILLED_WITH_BG_BODYCOLOR;

				NORMALIZE_ANGLE(Arc->t1);
				NORMALIZE_ANGLE(Arc->t2);
				
				if ( nbarg >= 13 ) // Coord reelles des extremites de l'arc lues
				{
					Arc->m_ArcStart.x = startx; Arc->m_ArcStart.y = starty;
					Arc->m_ArcEnd.x = endx; Arc->m_ArcEnd.y = endy;
				}
				else
				{
					Arc->m_ArcStart.x = Arc->m_Rayon; Arc->m_ArcStart.y = 0;
					Arc->m_ArcEnd.x = Arc->m_Rayon; Arc->m_ArcEnd.y = 0;
					RotatePoint( &Arc->m_ArcStart.x, &Arc->m_ArcStart.y, -Arc->t1);
					Arc->m_ArcStart.x += Arc->m_Pos.x; Arc->m_ArcStart.y +=Arc->m_Pos.y;
					RotatePoint( &Arc->m_ArcEnd.x, &Arc->m_ArcEnd.y, -Arc->t2);
					Arc->m_ArcEnd.x += Arc->m_Pos.x; Arc->m_ArcEnd.y +=Arc->m_Pos.y;
				}
				}
				break;

			case 'C': /* Circle */
				{
				LibDrawCircle * Circle = new LibDrawCircle();
				New = Circle; ll = 0;
				Error = sscanf(&Line[2], "%d %d %d %d %d %d %s",
						  &Circle->m_Pos.x, &Circle->m_Pos.y, &Circle->m_Rayon,
							&Unit, &Convert,&Circle->m_Width, chartmp) < 6;
				Circle->m_Unit = Unit;
				Circle->m_Convert = Convert;
				if ( chartmp[0] == 'F') Circle->m_Fill = FILLED_SHAPE;
				if ( chartmp[0] == 'f') Circle->m_Fill = FILLED_WITH_BG_BODYCOLOR;
				}
				break;

			case 'T': /* Text */
				{
				LibDrawText * Text = new LibDrawText();
				New = Text;
				Buffer[0] = 0;
				Error = sscanf(&Line[2], "%d %d %d %d %d %d %d %s",
								  &Text->m_Horiz,
								  &Text->m_Pos.x, &Text->m_Pos.y,
								  &Text->m_Size.x, &Text->m_Type,
							&Unit, &Convert, Buffer) != 8;

				Text->m_Unit = Unit; Text->m_Convert = Convert;
                Text->m_Size.y = Text->m_Size.x;
				if (!Error)
					{	/* Convert '~' to spaces. */
					Text->m_Text = CONV_FROM_UTF8(Buffer);
					Text->m_Text.Replace(wxT("~"), wxT(" "));	// Les espaces sont restitu�s
					}
				}
				break;

			case 'S': /* Square */
				{
				LibDrawSquare * Square = new LibDrawSquare();
				New = Square; ll = 0;
				Error = sscanf(&Line[2], "%d %d %d %d %d %d %d %s",
							  &Square->m_Pos.x, &Square->m_Pos.y,
							  &Square->m_End.x, &Square->m_End.y,
							&Unit, &Convert,&Square->m_Width, chartmp) < 7;
				Square->m_Unit = Unit; Square->m_Convert = Convert;
				if ( chartmp[0] == 'F') Square->m_Fill = FILLED_SHAPE;
				if ( chartmp[0] == 'f') Square->m_Fill = FILLED_WITH_BG_BODYCOLOR;
				}
				break;

			case 'X': /* Pin Description */
				{
				*Buffer = 0;
				LibDrawPin * Pin = new LibDrawPin();
				New = Pin;
				i = sscanf(Line+2, "%s %s %d %d %d %s %d %d %d %d %s %s",
								BufName, PinNum,
								&Pin->m_Pos.x, &Pin->m_Pos.y,
								&ll, chartmp1,
								&size1, &size2,
								&Unit, &Convert, chartmp, Buffer);

				Pin->m_PinNumSize = size1;		/* Parametres type short */
				Pin->m_PinNameSize = size2;
				Pin->m_PinLen = ll;
				Pin->m_Orient = chartmp1[0] & 255;

				Pin->m_Unit = Unit; Pin->m_Convert = Convert;
				strncpy((char*)&Pin->m_PinNum, PinNum, 4);
				Error = (i != 11 && i != 12);

				Pin->m_PinName = CONV_FROM_UTF8(BufName);

				jj = *chartmp & 255;
				switch(jj)
					{
					case 'I':
						Pin->m_PinType = PIN_INPUT; break;
					case 'O':
						Pin->m_PinType = PIN_OUTPUT; break;
					case 'B':
						Pin->m_PinType = PIN_BIDI; break;
					case 'T':
						Pin->m_PinType = PIN_TRISTATE; break;
					case 'P':
						Pin->m_PinType = PIN_PASSIVE; break;
					case 'U':
						Pin->m_PinType = PIN_UNSPECIFIED; break;
					case 'W':
						Pin->m_PinType = PIN_POWER_IN; break;
					case 'w':
						Pin->m_PinType = PIN_POWER_OUT; break;
					case 'C':
						Pin->m_PinType = PIN_OPENCOLLECTOR; break;
					case 'E':
						Pin->m_PinType = PIN_OPENEMITTER; break;
					default:
						MsgLine.Printf( wxT("Unknown Pin Type [%c] line %d"),
										jj, *LineNum);
						 DisplayError(frame, MsgLine);
					}
				if( i == 12 )	/* Special Symbole defined */
				for( jj = strlen(Buffer); jj > 0 ; )
					{
					switch(Buffer[--jj])
						{
						case '~': break;
						case 'N': Pin->m_Attributs |= PINNOTDRAW; break;
						case 'I': Pin->m_PinShape |= INVERT; break;
						case 'C': Pin->m_PinShape |= CLOCK; break;
						case 'L': Pin->m_PinShape |= LOWLEVEL_IN; break;
						case 'V': Pin->m_PinShape |= LOWLEVEL_OUT; break;
						default:
							MsgLine.Printf( wxT("Unknown Pin Shape [%c] line %d"),
												Buffer[jj], *LineNum);
							DisplayError(frame, MsgLine); break;
						}
					}
				}
				break;

			case 'P': /* Polyline */
				{
				LibDrawPolyline * Polyl = new LibDrawPolyline();
				New = Polyl;

				if (sscanf(&Line[2], "%d %d %d %d",
						&Polyl->n, &Unit, &Convert,
						&Polyl->m_Width) == 4 &&
						Polyl->n > 0)
					{
					Polyl->m_Unit = Unit; Polyl->m_Convert = Convert;

					Polyl->PolyList = (int *)
							MyZMalloc(sizeof(int) * Polyl->n * 2);

					p = strtok(&Line[2], " \t\n");
					p = strtok(NULL, " \t\n");
					p = strtok(NULL, " \t\n");
					p = strtok(NULL, " \t\n");

					for (i = 0; i < Polyl->n * 2 && !Error; i++)
						{
						p = strtok(NULL, " \t\n");
						Error = sscanf(p, "%d", &Polyl->PolyList[i]) != 1;
						}
					Polyl->m_Fill = NO_FILL;
					if ( (p = strtok(NULL, " \t\n")) != NULL )
						{
						if ( p[0] == 'F') Polyl->m_Fill = FILLED_SHAPE;
						if ( p[0] == 'f')
							Polyl->m_Fill = FILLED_WITH_BG_BODYCOLOR;
						}
					}

				else  Error = TRUE;
				}
				break;

			default:
				MsgLine.Printf( wxT("Undefined DRAW command in line %d, aborted."),
								*LineNum);
				DisplayError(frame, MsgLine);
				return Head;
			}

		if (Error)
			{
			MsgLine.Printf( wxT("Error in %c DRAW command in line %d, aborted."),
							Line[0], *LineNum);
			DisplayError(frame, MsgLine);
			delete New;
			/* FLush till end of draw: */
			do  {
				if (GetLine(f, Line, LineNum, 1024  ) == NULL)
					{
					DisplayError(frame, wxT("File ended prematurely") );
					return Head;
					}
				}  while (strncmp(Line, "ENDDRAW", 7) != 0);
			return (Head);
			}

		else
			{
			if (Head == NULL) Head = Tail = New;
			else
				{
				Tail->Pnext = New; Tail = New;
				}
			}
		}

	return Head;
}


/*****************************************************************************
* Routine to find the library given its name.								 *
*****************************************************************************/
LibraryStruct *FindLibrary(const wxString & Name)
{
LibraryStruct *Lib = g_LibraryList;

	while (Lib)
	{
		if (Lib->m_Name == Name ) return Lib;
		Lib = Lib->m_Pnext;
	}
	return NULL;
}

/*****************************************************************************
* Routine to find the number of libraries currently loaded.					 *
*****************************************************************************/
int NumOfLibraries(void)
{
int ii;
LibraryStruct *Lib = g_LibraryList;

	for (ii = 0; Lib != NULL; Lib = Lib->m_Pnext) ii++;
	return ii;
}


/*****************************************************************************/
static bool GetLibEntryField(EDA_LibComponentStruct *LibEntry, char * line)
/*****************************************************************************/
/* Analyse la ligne de description du champ de la forme:
	Fn "CA3130" 150 -200 50 H V
	ou n = 0 (REFERENCE), 1 (VALUE) , 2 .. 11 = autres champs, facultatifs
*/
{
int posx, posy, size, orient, hjustify, vjustify;
bool draw;
char *Text,
	Char1[256], Char2[256],
	Char3[256], Char4[256],
	FieldUserName[1024];
int NumOfField, nbparam;
LibDrawField * Field = NULL;

	if( sscanf(line+1, "%d", &NumOfField) != 1) return(0);

	/* Recherche du debut des donnees (debut du texte suivant) */
	while(*line != 0) line++;
	while(*line == 0) line++;

	/* recherche du texte */
	while ( *line && (*line != '"') ) line++;
	if ( *line == 0 ) return(0);
	line ++; Text = line;

	/* recherche fin de texte */
	while ( *line && (*line != '"') ) line++;
	if ( *line == 0 ) return(0);
	*line = 0; line++;

	FieldUserName[0] = 0;
	nbparam = sscanf(line, " %d %d %d %c %c %c %c",
		&posx, &posy, &size, Char1, Char2, Char3, Char4);
	orient = TEXT_ORIENT_HORIZ; if(Char1[0] == 'V') orient = TEXT_ORIENT_VERT;
	draw = TRUE; if(Char2[0] == 'I') draw = FALSE;
	hjustify = GR_TEXT_HJUSTIFY_CENTER;
	vjustify = GR_TEXT_VJUSTIFY_CENTER;
	if ( nbparam >= 6 )
	{
		if ( *Char3 == 'L' ) hjustify = GR_TEXT_HJUSTIFY_LEFT;
		else if ( *Char3 == 'R' ) hjustify = GR_TEXT_HJUSTIFY_RIGHT;
		if ( *Char4 == 'B' ) vjustify = GR_TEXT_VJUSTIFY_BOTTOM;
		else if ( *Char4 == 'T' ) vjustify = GR_TEXT_VJUSTIFY_TOP;
	}
	switch(NumOfField)
		{
		case REFERENCE:
			Field = &LibEntry->m_Prefix;
			Field->m_FieldId = REFERENCE;
			break;

		case VALUE:
			Field = &LibEntry->m_Name;
			Field->m_FieldId = VALUE;
			 break;

		default:
			if(NumOfField >= NUMBER_OF_FIELDS ) break;
			Field = new LibDrawField(NumOfField);
			Field->Pnext = LibEntry->Fields;
			LibEntry->Fields = Field;
			break;
		}

	if ( Field == NULL ) return FALSE;

	Field->m_Pos.x = posx; Field->m_Pos.y = posy;
	Field->m_Orient = orient;
	if( draw == FALSE ) Field->m_Attributs |= TEXT_NO_VISIBLE;
	Field->m_Size.x = Field->m_Size.y = size;
	Field->m_Text = CONV_FROM_UTF8(Text);
	if ( NumOfField >= FIELD1 )
	{
		ReadDelimitedText(FieldUserName,line, sizeof(FieldUserName) );
		Field->m_Name = CONV_FROM_UTF8(FieldUserName);
	}
	Field->m_HJustify = hjustify;
	Field->m_VJustify = vjustify;
	return(TRUE);
}


/********************************************************************/
static bool AddAliasNames(EDA_LibComponentStruct *LibEntry, char * line)
/********************************************************************/
/* Read the alias names (in buffer line) and add them in alias list
	names are separated by spaces
*/
{
char * text;
wxString name;	
	text = strtok(line, " \t\r\n");

	while ( text )
	{
		name = CONV_FROM_UTF8(text);
		LibEntry->m_AliasList.Add(name);
		text = strtok(NULL, " \t\r\n");
	}
	return( TRUE );
}

/********************************************************************/
static void InsertAlias(PriorQue ** PQ, EDA_LibComponentStruct *LibEntry,
				int *NumOfParts)
/********************************************************************/
/* create in library (in list PQ) aliases of the "root" component LibEntry*/
{
EDA_LibCmpAliasStruct *AliasEntry;
unsigned ii;
	
	if(LibEntry->m_AliasList.GetCount() == 0)
		return; /* No alias for this component */

	for( ii = 0; ii < LibEntry->m_AliasList.GetCount(); ii++ )
	{
		AliasEntry = new EDA_LibCmpAliasStruct(LibEntry->m_AliasList[ii],
				LibEntry->m_Name.m_Text.GetData());
		++*NumOfParts;
		PQInsert(PQ, AliasEntry);
	}
}

	/*******************************************************/
	/* Routines de lecture des Documentation de composants */
	/*******************************************************/

/**********************************************************************************************/
int LoadDocLib(WinEDA_DrawFrame * frame, const wxString & FullDocLibName, const wxString & Libname)
/**********************************************************************************************/
/* Routine to load a library from given open file.*/
{
int LineNum = 0;
char Line[1024], *Name, *Text;
EDA_LibComponentStruct * Entry;
FILE * f;
wxString msg;
	
	f = wxFopen(FullDocLibName, wxT("rt") );
	if (f == NULL) return(0);

	if ( GetLine(f, Line, &LineNum, sizeof(Line) ) == NULL)
	{	/* pas de lignes utiles */
		fclose(f);
		return 0;
	}

	if( strnicmp(Line, DOCFILE_IDENT, 10) != 0)
	{
		DisplayError(frame, wxT("File is NOT EESCHEMA doclib!") );
		fclose(f);
		return 0;
	}

	while (GetLine(f, Line, &LineNum, sizeof(Line)) )
	{
		if (strncmp(Line, "$CMP",4) != 0)
		{
			msg.Printf( wxT("$CMP command expected in line %d, aborted."), LineNum);
			DisplayError(frame, msg);
			fclose(f);
			return 0;
		}

		/* Read one $CMP/$ENDCMP part entry from library: */
		Name = strtok(Line + 5,"\n\r");
		wxString cmpname; cmpname = CONV_FROM_UTF8(Name);
		Entry = FindLibPart(cmpname.GetData(),Libname,FIND_ALIAS);
		while( GetLine(f, Line, &LineNum, sizeof(Line)) )
		{
			if( strncmp(Line, "$ENDCMP",7) == 0) break;
			Text = strtok(Line + 2,"\n\r");
			switch ( Line[0] )
			{
				case 'D':
					if(Entry) Entry->m_Doc = CONV_FROM_UTF8(Text);
			   	break;

				case 'K':
					if(Entry) Entry->m_KeyWord = CONV_FROM_UTF8(Text);
					break;

				case 'F':
					if(Entry) Entry->m_DocFile = CONV_FROM_UTF8(Text);
					break;
			}
		}
	}
	fclose(f);
	return 1;
}


/*********************************************************************************/
static bool ReadLibEntryDateAndTime(EDA_LibComponentStruct * LibEntry, char * Line)
/*********************************************************************************/
/* lit date et time de modif composant sous le format:
	"Ti yy/mm/jj hh:mm:ss"
*/
{
int year,mon,day,hour,min,sec;
char * text;

	year = mon = day = hour = min = sec = 0;
	text = strtok(Line," \r\t\n");
	text = strtok(NULL," \r\t\n");	// text pointe donnees utiles

	sscanf(Line,"%d/%d/%d %d:%d:%d",&year,&mon,&day,&hour,&min,&sec);

	LibEntry->m_LastDate = (sec & 63)
				+ ((min & 63) << 6)
				+ ((hour & 31) << 12)
				+ ((day & 31) << 17)
				+ ((mon & 15) << 22)
				+ ((year-1990) << 26);

	return TRUE;
}

/*******************************************/
static int SortItemsFct(const void * ref, const void * item);
void EDA_LibComponentStruct::SortDrawItems(void)
/*******************************************/
/* Trie les �l�ments graphiques d'un composant lib pour am�liorer
le trac�:
items remplis en premier, pins en dernier
En cas de superposition d'items, c'est plus lisible
*/
{
LibEDA_BaseStruct ** Bufentry, ** BufentryBase, *Entry = m_Drawings;
int ii, nbitems;

	if(Entry == NULL ) return; /* Pas d'alias pour ce composant */
	/* calcul du nombre d'items */
	for( nbitems = 0; Entry != NULL; Entry = Entry->Next()) nbitems++;

	BufentryBase =
		(LibEDA_BaseStruct **) MyZMalloc( (nbitems+1) * sizeof(LibEDA_BaseStruct *));
	/* memorisation du chainage : */
	for( Entry = m_Drawings, ii = 0; Entry != NULL; Entry = Entry->Next())
		BufentryBase[ii++] = Entry;

	/* Tri du chainage */
	qsort(BufentryBase, nbitems, sizeof(LibEDA_BaseStruct *), SortItemsFct);

	/* Mise a jour du chainage. Remarque:
	le dernier element de BufEntryBase (BufEntryBase[nbitems]) est NULL*/
	m_Drawings = * BufentryBase;
	Bufentry = BufentryBase;
	for (ii = 0 ; ii < nbitems; ii++)
	{
		(* Bufentry)->Pnext = * (Bufentry+1);
		Bufentry++;
	}

	MyFree(BufentryBase);
}

int SortItemsFct(const void * ref, const void * item)
{
#define Ref (*(LibEDA_BaseStruct **)(ref))
#define Item (*(LibEDA_BaseStruct **)(item))
#define BEFORE -1
#define AFTER 1

int fill_ref = 0, fill_item = 0;

	switch (Ref->m_StructType)
	{
		case COMPONENT_ARC_DRAW_TYPE:
		{
			const LibDrawArc * draw = (const LibDrawArc *) Ref;
			fill_ref = draw->m_Fill;
			break;
		}

		case COMPONENT_CIRCLE_DRAW_TYPE:
		{
			const LibDrawCircle * draw = (const LibDrawCircle *) Ref;
			fill_ref = draw->m_Fill;
			break;
		}

		case COMPONENT_RECT_DRAW_TYPE:
		{
			const LibDrawSquare * draw = (const LibDrawSquare *) Ref;
			fill_ref = draw->m_Fill;
			break;
		}

		case COMPONENT_POLYLINE_DRAW_TYPE:
		{
			const LibDrawPolyline * draw = (const LibDrawPolyline *) Ref;
			fill_ref = draw->m_Fill;
			break;
		}

		case COMPONENT_GRAPHIC_TEXT_DRAW_TYPE:
			if ( Item->m_StructType == COMPONENT_PIN_DRAW_TYPE ) return BEFORE;
			if ( Item->m_StructType == COMPONENT_GRAPHIC_TEXT_DRAW_TYPE ) return 0;
			return 1;
			break;

		case COMPONENT_PIN_DRAW_TYPE:
			if ( Item->m_StructType == COMPONENT_PIN_DRAW_TYPE )
			{
				int ii;
				// We sort the pins by orientation
				ii = ((LibDrawPin *) Ref)->m_Orient - ((LibDrawPin *) Item)->m_Orient;
				if ( ii ) return ii;
					
				/* We sort the pins by position (x or y).
				note: at this point, most of pins have same x pos or y pos,
				because they are sorted by orientation and generally are vertically or
				horizontally aligned */
				wxPoint pos_ref, pos_tst;
				pos_ref = ((LibDrawPin *) Ref)->m_Pos;
				pos_tst = ((LibDrawPin *) Item)->m_Pos;
				if ( (ii = pos_ref.x - pos_tst.x) ) return ii;
				ii = pos_ref.y - pos_tst.y;
				return ii;
			}
			else return AFTER;
			break;
	}

	/* Test de l'item */
	switch (Item->m_StructType)
	{
		case COMPONENT_ARC_DRAW_TYPE:
		{
			const LibDrawArc * draw = (const LibDrawArc *) Item;
			fill_item = draw->m_Fill;
			break;
		}

		case COMPONENT_CIRCLE_DRAW_TYPE:
		{
			const LibDrawCircle * draw = (const LibDrawCircle *) Item;
			fill_item = draw->m_Fill;
			break;
		}

		case COMPONENT_RECT_DRAW_TYPE:
		{
			const LibDrawSquare * draw = (const LibDrawSquare *) Item;
			fill_item = draw->m_Fill;
			break;
		}

		case COMPONENT_POLYLINE_DRAW_TYPE:
		{
			const LibDrawPolyline * draw = (const LibDrawPolyline *) Item;
			fill_item = draw->m_Fill;
			break;
		}

		case COMPONENT_GRAPHIC_TEXT_DRAW_TYPE:
			return BEFORE;
			break;

		case COMPONENT_PIN_DRAW_TYPE:
			return BEFORE;
			break;
	}

	if ( fill_ref & fill_item ) return 0;
	if ( fill_ref ) return BEFORE;
	return AFTER;
}


/*****************************************************************************/
int AddFootprintFilterList(EDA_LibComponentStruct *LibEntryLibEntry, FILE * f,
		char * Line, int *LineNum)
/******************************************************************************/
/* read the FootprintFilter List stating with:
	FPLIST
	and ending with:
	ENDFPLIST
*/
{
	for ( ; ; )
	{
		if (GetLine(f, Line, LineNum, 1024 ) == NULL)
		{
			DisplayError(NULL, wxT("File ended prematurely"));
			return 0;
		}
		
		if ( stricmp(Line, "$ENDFPLIST") == 0 )
		{
			break;	/*normal exit on end of list */
		}
		
		LibEntryLibEntry->m_FootprintList.Add(CONV_FROM_UTF8(Line+1));
	}
	
	return 1;
}