	/******************************/
	/* PCBNEW: echange de modules */
	/******************************/

	/* Fichier xchmod.cpp */

#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "pcbnew.h"
#include "autorout.h"

#include "protos.h"

/* variables locales */

enum id_ExchangeModule
{
	ID_EXEC_EXCHANGE_MODULE = 1900,
	ID_EXEC_EXCHANGE_ID_MODULES,
	ID_EXEC_EXCHANGE_ID_MODULE_AND_VALUE,
	ID_EXEC_EXCHANGE_ALL_MODULES,
	ID_CLOSE_EXCHANGE_MODULE,
	ID_BROWSE_LIB_MODULES
};

	/************************************/
	/* class WinEDA_ExchangeModuleFrame */
	/************************************/

class WinEDA_ExchangeModuleFrame: public wxDialog
{
private:

	WinEDA_BasePcbFrame * m_Parent;
	wxDC * m_DC;
	MODULE * m_CurrentModule;
	WinEDA_EnterText * m_OldModule;
	WinEDA_EnterText * m_OldValue;
	WinEDA_EnterText * m_NewModule;
	wxTextCtrl * m_WinMsg;

public:
	// Constructor and destructor
	WinEDA_ExchangeModuleFrame(WinEDA_BasePcbFrame *parent,
							MODULE * Module, wxDC * DC, const wxPoint & pos);
	~WinEDA_ExchangeModuleFrame(void)
		{
		}

private:
	void OnQuit(wxCommandEvent& event);
	void Change_Module(wxCommandEvent& event);
	void Change_ModuleId(wxCommandEvent& event);
	void Change_ModuleAll(wxCommandEvent& event);
	int Maj_ListeCmp(
				const wxString & reference,
				const wxString & old_name,
				const wxString & new_name, bool ShowError);
	MODULE * Change_1_Module(MODULE * Module, const wxString& new_module, bool ShowError);
	void Sel_NewMod_By_Liste(wxCommandEvent& event);


	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(WinEDA_ExchangeModuleFrame, wxDialog)
	EVT_BUTTON(ID_EXEC_EXCHANGE_MODULE, WinEDA_ExchangeModuleFrame::Change_Module)
	EVT_BUTTON(ID_EXEC_EXCHANGE_ID_MODULES, WinEDA_ExchangeModuleFrame::Change_ModuleId)
	EVT_BUTTON(ID_EXEC_EXCHANGE_ID_MODULE_AND_VALUE, WinEDA_ExchangeModuleFrame::Change_ModuleId)
	EVT_BUTTON(ID_EXEC_EXCHANGE_ALL_MODULES, WinEDA_ExchangeModuleFrame::Change_ModuleAll)
	EVT_BUTTON(ID_CLOSE_EXCHANGE_MODULE, WinEDA_ExchangeModuleFrame::OnQuit)
	EVT_BUTTON(ID_BROWSE_LIB_MODULES, WinEDA_ExchangeModuleFrame::Sel_NewMod_By_Liste)
END_EVENT_TABLE()


WinEDA_ExchangeModuleFrame::WinEDA_ExchangeModuleFrame(WinEDA_BasePcbFrame *parent,
				MODULE * Module,wxDC * DC,
				const wxPoint & framepos):
		wxDialog(parent, -1, _("Exchange Modules"), framepos, wxSize(360, 460),
					DIALOG_STYLE)
{
wxButton * Button;

	m_Parent = parent;
	SetFont(*g_DialogFont);
	m_DC = DC;
	Centre();

	m_CurrentModule = Module;

	wxBoxSizer * MainBoxSizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(MainBoxSizer);
	wxBoxSizer * UpperBoxSizer = new wxBoxSizer(wxHORIZONTAL);
	MainBoxSizer->Add(UpperBoxSizer, 0, wxGROW|wxALL, 5);
	wxBoxSizer * LeftBoxSizer = new wxBoxSizer(wxVERTICAL);
	wxBoxSizer * RightBoxSizer = new wxBoxSizer(wxVERTICAL);
	UpperBoxSizer->Add(LeftBoxSizer, 0, wxGROW|wxALL, 5);
	UpperBoxSizer->Add(RightBoxSizer, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

	/* Creation des boutons de commande */
	Button = new wxButton(this, ID_EXEC_EXCHANGE_MODULE,
						_("Change module"));
	Button->SetForegroundColour(*wxBLUE);
	RightBoxSizer->Add(Button, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5);

	Button = new wxButton(this, ID_EXEC_EXCHANGE_ID_MODULES,
						_("Change same modules"));
	Button->SetForegroundColour(*wxRED);
	RightBoxSizer->Add(Button, 0, wxGROW|wxLEFT|wxRIGHT, 5);

	Button = new wxButton(this, ID_EXEC_EXCHANGE_ID_MODULE_AND_VALUE,
						_("Ch. same module+value"));
	Button->SetForegroundColour(*wxRED);
	RightBoxSizer->Add(Button, 0, wxGROW|wxLEFT|wxRIGHT, 5);

	Button = new wxButton(this, ID_EXEC_EXCHANGE_ALL_MODULES,
						_("Change all"));
	Button->SetForegroundColour(*wxRED);
	RightBoxSizer->Add(Button, 0, wxGROW|wxLEFT|wxRIGHT, 5);

	Button = new wxButton(this, ID_BROWSE_LIB_MODULES,
						_("Browse Libs modules"));
	Button->SetForegroundColour(wxColour(0,100,0) );
	RightBoxSizer->Add(Button, 0, wxGROW|wxLEFT|wxRIGHT, 5);

	Button = new wxButton(this, ID_CLOSE_EXCHANGE_MODULE,
						_("Close"));
	Button->SetForegroundColour(*wxBLUE);
	RightBoxSizer->Add(Button, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5);

	m_OldModule = new WinEDA_EnterText(this, _("Current Module"),
			m_CurrentModule ? m_CurrentModule->m_LibRef.GetData() :wxEmptyString,
			LeftBoxSizer, wxSize( 150,-1) );
	m_OldModule->Enable(FALSE);

	m_OldValue = new WinEDA_EnterText(this, _("Current Value"),
			m_CurrentModule ? m_CurrentModule->m_Value->m_Text.GetData() :wxEmptyString,
			LeftBoxSizer, wxSize( 150,-1) );
	m_OldValue->Enable(FALSE);

	m_NewModule = new WinEDA_EnterText(this, _("New Module"),
			m_OldModule->GetValue(), LeftBoxSizer, wxSize( 150,-1) );

	m_WinMsg = new wxTextCtrl(this, -1,wxEmptyString, wxDefaultPosition, wxSize(340, 230),
					wxTE_READONLY|wxTE_MULTILINE);
	MainBoxSizer->Add(m_WinMsg, 0, wxGROW|wxALL, 5);

	GetSizer()->Fit(this);
    GetSizer()->SetSizeHints(this);
}


/*********************************************************************/
void WinEDA_BasePcbFrame::InstallExchangeModuleFrame( MODULE * Module,
					wxDC * DC, const wxPoint & pos)
/*********************************************************************/
{
	WinEDA_ExchangeModuleFrame * frame = new WinEDA_ExchangeModuleFrame(this,
					 Module, DC, pos);
	frame->ShowModal(); frame->Destroy();
}


/**********************************************************************/
void  WinEDA_ExchangeModuleFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
/**********************************************************************/
{
    Close(true);    // true is to force the frame to close
}



/************************************************************************/
int WinEDA_ExchangeModuleFrame::Maj_ListeCmp(
				const wxString & reference,
				const wxString & old_name,
				const wxString & new_name, bool ShowError)
/************************************************************************/
/*
	Met a jour le fichier name.CMP (s'il existe) apres un echange de module
	(par la commande changeMod), si les modules sont geres par ce fichier

	Si ShowError != 0 affiche message d'erreur si le fichier .cmp n'est pas
	trouve.
	Retoure 1 si erreur
*/
{
wxString FileNameCmp, tmpfile;
FILE * FichCmp, *NewFile;
char Line[1024];
wxString msg;
	
	if ( old_name == new_name )	return(0);	/* pas de changement de nom */

	/* Calcul nom fichier CMP par changement de l'extension du nom netliste */
	if ( NetNameBuffer == wxEmptyString )
		FileNameCmp = m_Parent->m_CurrentScreen->m_FileName;
	else FileNameCmp = NetNameBuffer;
	ChangeFileNameExt(FileNameCmp,NetCmpExtBuffer) ;

	// Modification du fichier .cmp correcpondant
	FichCmp = wxFopen(FileNameCmp, wxT("rt"));
	if( FichCmp == NULL )
	{
		if ( ShowError )
		{
			msg.Printf( _("file %s not found"), FileNameCmp.GetData());
			m_WinMsg->WriteText(msg);
		}
		return(1);
	}

	/* Analyse du fichier et modif */
	tmpfile = FileNameCmp;
	ChangeFileNameExt(tmpfile, wxT(".$$$"));
	NewFile = wxFopen(tmpfile, wxT("wt"));
	if( NewFile == NULL )
	{
		if ( ShowError )
		{
			msg.Printf( _("Unable to create file %s"), tmpfile.GetData());
			m_WinMsg->WriteText(msg);
		}
		return(1);
	}

	fgets(Line, sizeof(Line), FichCmp);
	fprintf(NewFile,"Cmp-Mod V01 Genere par PcbNew le %s\n", DateAndTime(Line) );

	bool start_descr = FALSE;
	while( fgets(Line, sizeof(Line), FichCmp) != NULL )
	{
		if ( strnicmp( Line, "Reference = ", 9 ) == 0 )
		{
			char buf[1024];
			strcpy( buf, Line + 12);
			strtok(buf,";\n\r");
			if ( stricmp( buf , CONV_TO_UTF8(reference) ) == 0 )
			{
				start_descr = TRUE;
			}
		}

		if ( (strnicmp( Line, "Begin", 5 ) == 0) ||
			 (strnicmp( Line, "End", 3) == 0) )
		{
			start_descr = FALSE;
		}

		if ( start_descr && strnicmp( Line, "IdModule", 8 ) == 0 )
		{
			sprintf(Line + 8,"  = %s;\n", CONV_TO_UTF8(new_name) );

			msg.Printf( wxT(" * in %s\n"), FileNameCmp.GetData());
			m_WinMsg->WriteText(msg);

			start_descr = FALSE;
		}
		fputs(Line, NewFile);
	}

	fclose(FichCmp);
	fclose(NewFile);
	wxRemoveFile(FileNameCmp);
	wxRenameFile(tmpfile, FileNameCmp);
	return(0);
}



/********************************************************************/
void WinEDA_ExchangeModuleFrame::Change_Module(wxCommandEvent& event)
/********************************************************************/

/* Routine de changement d'un module:
	Change le module pointe par la souris, par un autre en conservant
		- meme orientation
		- meme position
		- memes textes valeur et ref
		- memes netnames pour pads de meme nom
*/
{
wxString newmodulename = m_NewModule->GetValue();

	if( newmodulename == wxEmptyString ) return;

	if( Change_1_Module(m_CurrentModule, newmodulename,TRUE ) )
		{
		m_Parent->m_Pcb->m_Status_Pcb = 0;
		m_Parent->build_liste_pads();
		}
}

/*********************************************************************/
void WinEDA_ExchangeModuleFrame::Change_ModuleId(wxCommandEvent& event)
/**********************************************************************/

/* Routine de changement de tous les modules de meme nom lib que celui
	selectionne, en conservant
		- meme orientation
		- meme position
		- memes textes valeur et ref
		- memes netnames pour pads de meme nom
	et en remplacant l'ancien module par le noveau module
	Attention: m_CurrentModule ne pointe plus sur le module de reference
	puisque celui ci a ete change!!
*/
{
wxString msg;
MODULE * PtModule, *PtBack;
bool change = FALSE;
wxString newmodulename = m_NewModule->GetValue();
wxString value, lib_reference;	// pour memo Reflib et value de reference
bool check_module_value = FALSE;
int ShowErr = 5;	// Affiche 5 messages d'err maxi

	if( m_Parent->m_Pcb->m_Modules == NULL ) return;
	if( newmodulename == wxEmptyString ) return;

	lib_reference = m_CurrentModule->m_LibRef;
	if ( event.GetId() == ID_EXEC_EXCHANGE_ID_MODULE_AND_VALUE )
		{
		check_module_value = TRUE;
		value = m_CurrentModule->m_Value->m_Text;
		msg.Printf( _("Change modules <%s> -> <%s> (val = %s)?"),
				m_CurrentModule->m_LibRef.GetData(),
				newmodulename.GetData(),
				m_CurrentModule->m_Value->m_Text.GetData() );
		}

	else
		{
		msg.Printf( _("Change modules <%s> -> <%s> ?"),
				lib_reference.GetData(), newmodulename.GetData() );
		}

	if( !IsOK(this, msg) ) return;

	/* Le changement s'effectue a partir du dernier module car la routine
		Change_1_Module() modifie le dernier module de la liste
	*/

	PtModule = m_Parent->m_Pcb->m_Modules;
	for ( ; PtModule != NULL; PtModule = (MODULE*)PtModule->Pnext)
	{
		if(PtModule->Pnext == NULL) break;
	}

	/* Ici PtModule pointe le dernier module de la liste */
	for ( ; PtModule != (MODULE*)m_Parent->m_Pcb; PtModule = PtBack)
		{
		MODULE * module;
		PtBack = (MODULE*)PtModule->Pback;
		if( lib_reference.CmpNoCase(PtModule->m_LibRef) != 0 )
			continue;
		if ( check_module_value )
			{
			if( value.CmpNoCase(PtModule->m_Value->m_Text) != 0 )
				continue;
			}
		module = Change_1_Module(PtModule, newmodulename.GetData(), ShowErr);
		if ( module ) change = TRUE;
		else if (ShowErr) ShowErr--;
		}
	if( change )
		{
		m_Parent->m_Pcb->m_Status_Pcb = 0;
		m_Parent->build_liste_pads();
		}
}

/***********************************************************************/
void WinEDA_ExchangeModuleFrame::Change_ModuleAll(wxCommandEvent& event)
/***********************************************************************/
/* Routine de changement de tous les modules par les modules de meme nom lib:
	en conservant
		- meme orientation
		- meme position
		- memes textes valeur et ref
		- memes netnames pour pads de meme nom
*/
{
MODULE * PtModule, *PtBack;
bool change = FALSE;
int ShowErr = 5;	// Affiche 5 messages d'err maxi

	if(m_Parent->m_Pcb->m_Modules == NULL) return;

	if( !IsOK(this, _("Change ALL modules ?")) ) return;

	/* Le changement s'effectue a partir du dernier module car la routine
		Change_1_Module() modifie le dernier module de la liste
	*/

	PtModule = (MODULE*) m_Parent->m_Pcb->m_Modules;
	for ( ; PtModule != NULL; PtModule = (MODULE*)PtModule->Pnext)
	{
		if(PtModule->Pnext == NULL) break;
	}

	/* Ici PtModule pointe le dernier module de la liste */
	for ( ; PtModule != (MODULE*)(m_Parent->m_Pcb); PtModule = PtBack)
	{
		PtBack = (MODULE*)PtModule->Pback;
		if ( Change_1_Module(PtModule, PtModule->m_LibRef.GetData(), ShowErr) )
			change = TRUE;
		else if (ShowErr) ShowErr--;
	}

	if( change)
	{
		m_Parent->m_Pcb->m_Status_Pcb = 0;
		m_Parent->build_liste_pads();
	}
}

/******************************************************************/
MODULE * WinEDA_ExchangeModuleFrame::Change_1_Module(MODULE * PtModule,
		const wxString& new_module, bool ShowError)
/*******************************************************************/
/* Routine de changement d'un module:
	Change le module de numero empr, avec le module de nom new_module
		- meme orientation
		- meme position
		- memes textes valeur et ref
		- memes netnames pour pads de meme nom
	Retourne :
		0 si pas de changement ( si le nouveau module n'est pas en libr)
		1 si OK
*/
{
wxString namecmp, oldnamecmp;
MODULE * NewModule;
wxString Line;

	if(PtModule == NULL) return(NULL);

wxBusyCursor dummy;

	/* Memorisation des parametres utiles de l'ancien module */
	oldnamecmp = PtModule->m_LibRef;
	namecmp = new_module;

	/* Chargement du module */
	Line.Printf( _("Change module %s (%s)  "),
		PtModule->m_Reference->m_Text.GetData(), oldnamecmp.GetData());
	m_WinMsg->WriteText(Line);

	namecmp.Trim(TRUE);
	namecmp.Trim(FALSE);
	NewModule = m_Parent->Get_Librairie_Module(this, wxEmptyString, namecmp, ShowError);
	if( NewModule == NULL)	/* Nouveau module NON trouve, reaffichage de l'ancien */
		{
		m_WinMsg->WriteText( wxT("No\n"));
		return(NULL);
		}

	if ( PtModule == m_CurrentModule ) m_CurrentModule = NewModule;
	m_WinMsg->WriteText( wxT("Ok\n"));

	/* Effacement a l'ecran de l'ancien module */
	PtModule->Draw(m_Parent->DrawPanel, m_DC, wxPoint(0,0), GR_XOR);

	m_Parent->Exchange_Module(this, PtModule, NewModule);

	/* Affichage du nouveau module */
	NewModule->Draw(m_Parent->DrawPanel, m_DC, wxPoint(0,0),GR_OR);

	Maj_ListeCmp(NewModule->m_Reference->m_Text, oldnamecmp, namecmp, ShowError);

	return(NewModule);
}

/***********************************************************************************/
MODULE * WinEDA_BasePcbFrame::Exchange_Module(wxWindow * winaff,
					MODULE * OldModule, MODULE * NewModule)
/***********************************************************************************/
/*
Remplace le module OldModule par le module NewModule (en conservant position, orientation..)
OldModule est supprimé de la memoire.
*/
{
wxPoint oldpos;	/* memorisation temporaire pos curseur */
D_PAD * pt_pad, * pt_old_pad;


	if ( (OldModule->m_StructType != TYPEMODULE) || (NewModule->m_StructType != TYPEMODULE) )
		{
		DisplayError(winaff, wxT("WinEDA_BasePcbFrame::Exchange_Module() StuctType error" ));
		}

	NewModule->m_Parent = m_Pcb;

	m_Pcb->m_Status_Pcb = 0 ;
	oldpos = m_CurrentScreen->m_Curseur;
	m_CurrentScreen->m_Curseur = OldModule->m_Pos;
	Place_Module(NewModule, NULL);
	m_CurrentScreen->m_Curseur = oldpos;

	/* Changement eventuel de couche */
	if( OldModule->m_Layer != NewModule->m_Layer)
		{
		Change_Side_Module(NewModule, NULL);
		}

	/* Rotation eventuelle du module */
	if( OldModule->m_Orient != NewModule->m_Orient )
		{
		Rotate_Module(NULL, NewModule,OldModule->m_Orient, FALSE );
		}

	/* Mise a jour des textes ref et val */
	NewModule->m_Reference->m_Text = OldModule->m_Reference->m_Text;
	NewModule->m_Value->m_Text = OldModule->m_Value->m_Text;

	/* Mise a jour des autres parametres */
	NewModule->m_TimeStamp = OldModule->m_TimeStamp;

	/* mise a jour des netnames ( lorsque c'est possible) */
	pt_pad = NewModule->m_Pads;
	for( ; pt_pad != NULL; pt_pad = (D_PAD*)pt_pad->Pnext)
		{
		pt_pad->m_Netname = wxEmptyString;
		pt_pad->m_NetCode = 0;
		pt_old_pad = OldModule->m_Pads;
		for( ; pt_old_pad != NULL; pt_old_pad = (D_PAD*)pt_old_pad->Pnext )
			{
			if(strnicmp( pt_pad->m_Padname, pt_old_pad->m_Padname,sizeof(pt_pad->m_Padname)) == 0)
				{
				pt_pad->m_Netname = pt_old_pad->m_Netname;
				pt_pad->m_NetCode = pt_old_pad->m_NetCode;
				}
			}
		}

	/* Effacement de l'ancien module */
	DeleteStructure(OldModule);

	m_Pcb->m_Status_Pcb = 0;
	NewModule->m_Flags = 0;
	m_CurrentScreen->SetModify();

	return NewModule;
}


/***************************************************************************/
void WinEDA_ExchangeModuleFrame::Sel_NewMod_By_Liste(wxCommandEvent& event)
/***************************************************************************/
/*affiche la liste des modules en librairie et selectione 1 nom */
{
wxString newname;

	newname = m_Parent->Select_1_Module_From_List( m_Parent, wxEmptyString, wxEmptyString, wxEmptyString);
	if ( newname != wxEmptyString )
		m_NewModule->SetValue(newname);
}


/***************************************************/
bool WinEDA_PcbFrame::RecreateCmpFileFromBoard(void)
/***************************************************/
{
wxString FullFileNameCmp, mask;
FILE * FichCmp;
char Line[1024];
MODULE * Module = m_Pcb->m_Modules;
wxString msg;
	
	if ( Module == NULL)
	{
		DisplayError(this, _("No Modules!") );
		return FALSE;
	}

	/* Calcul nom fichier CMP par changement de l'extension du nom netliste */
	if ( NetNameBuffer == wxEmptyString )
		FullFileNameCmp = m_CurrentScreen->m_FileName;
	else FullFileNameCmp = NetNameBuffer;
	ChangeFileNameExt(FullFileNameCmp,NetCmpExtBuffer) ;

	mask = wxT("*") + NetCmpExtBuffer;
	FullFileNameCmp = EDA_FileSelector( _("Cmp files:"),
				wxEmptyString,						/* Chemin par defaut */
				FullFileNameCmp,	 	/* nom fichier par defaut */
				NetCmpExtBuffer,		/* extension par defaut */
				mask,					/* Masque d'affichage */
				this,
				wxFD_SAVE,
				FALSE
				);
	if ( FullFileNameCmp.IsEmpty() ) return FALSE;


	FichCmp = wxFopen(FullFileNameCmp, wxT("wt"));
	if( FichCmp == NULL )
	{
		msg = _("Unable to create file ") + FullFileNameCmp;
		DisplayError(this, msg);
		return FALSE;
	}

	fgets(Line, sizeof(Line), FichCmp);
	fprintf(FichCmp,"Cmp-Mod V01 Genere par PcbNew le %s\n", DateAndTime(Line) );

	for( ; Module != NULL; Module = (MODULE*) Module->Pnext )
		{
		fprintf(FichCmp,"\nBeginCmp\n" );
		fprintf(FichCmp,"TimeStamp = %8.8lX\n", Module->m_TimeStamp);
		fprintf(FichCmp,"Reference = %s;\n",
			! Module->m_Reference->m_Text.IsEmpty() ?
					CONV_TO_UTF8(Module->m_Reference->m_Text) : "[NoRef]" );
		fprintf(FichCmp,"ValeurCmp = %s;\n",
			!Module->m_Value->m_Text.IsEmpty() ?
					CONV_TO_UTF8(Module->m_Value->m_Text) : "[NoVal]" );
		fprintf(FichCmp,"IdModule  = %s;\n", CONV_TO_UTF8(Module->m_LibRef));
		fprintf(FichCmp,"EndCmp\n" );
		}

	fprintf(FichCmp,"\nEndListe\n");
	fclose(FichCmp);

	return TRUE;
}
