/*************************************************/
/* Functions to Load  from file and save to file */
/* the graphic shapes  used to draw a component  */
/* When using the import/export symbol options	 */
/* files are the *.sym files 					 */
/*************************************************/

/* fichier symbedit.cpp */

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

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

#include "protos.h"


/* Routines locales */
static bool CompareSymbols( LibEDA_BaseStruct* DEntryRef,
                            LibEDA_BaseStruct* DEntryCompare );

/* Variables locales */


/***************************************************/
void WinEDA_LibeditFrame::LoadOneSymbol( wxDC* DC )
/***************************************************/

/* Read a component shape file and add data (graphic items) to the current
 *  component.
 */
{
    int                     NumOfParts;
    PriorQue*               Entries;
    EDA_LibComponentStruct* LibEntry = NULL;
    LibEDA_BaseStruct*      DrawEntry;
    wxString                FullFileName, mask;
    FILE*                   ImportFile;
    wxString                msg;

    if( CurrentDrawItem )
        return;
    if( CurrentLibEntry == NULL )
        return;

    DrawPanel->m_IgnoreMouseEvents = TRUE;

    mask = wxT( "*" ) + g_SymbolExtBuffer;
    FullFileName = EDA_FileSelector( _( "Import symbol drawings:" ),
                                     g_RealLibDirBuffer,    /* Chemin par defaut */
                                     wxEmptyString,         /* nom fichier par defaut */
                                     g_SymbolExtBuffer,     /* extension par defaut */
                                     mask,                  /* Masque d'affichage */
                                     this,
                                     0,
                                     TRUE
                                     );

    GetScreen()->m_Curseur = wxPoint( 0, 0 );
    DrawPanel->MouseToCursorSchema();
    DrawPanel->m_IgnoreMouseEvents = FALSE;

    if( FullFileName.IsEmpty() )
        return;


    /* Load data */
    ImportFile = wxFopen( FullFileName, wxT( "rt" ) );
    if( ImportFile == NULL )
    {
        msg.Printf( _( "Failed to open Symbol File <%s>" ), FullFileName.GetData() );
        DisplayError( this, msg, 20 );
        return;
    }


    Entries = LoadLibraryAux( this, NULL, ImportFile, &NumOfParts );
    fclose( ImportFile );

    if( Entries == NULL )
        return;

    if( NumOfParts > 1 )
        DisplayError( this, _( "Warning: more than 1 part in Symbol File" ), 20 );

    LibEntry = (EDA_LibComponentStruct*) PQFirst( &Entries, FALSE );

    if( LibEntry == NULL )
        DisplayError( this, _( "Symbol File is void" ), 20 );

    else /* add data to the current symbol */
    {
        DrawEntry = LibEntry->m_Drawings;
        while( DrawEntry )
        {
            if( DrawEntry->m_Unit )
                DrawEntry->m_Unit = CurrentUnit;
            if( DrawEntry->m_Convert )
                DrawEntry->m_Convert = CurrentConvert;
            DrawEntry->m_Flags    = IS_NEW;
            DrawEntry->m_Selected = IS_SELECTED;

            if( DrawEntry->Pnext == NULL )
            {   /* Fin de liste trouvee */
                DrawEntry->Pnext = CurrentLibEntry->m_Drawings;
                CurrentLibEntry->m_Drawings = LibEntry->m_Drawings;
                LibEntry->m_Drawings = NULL;
                break;
            }
            DrawEntry = DrawEntry->Next();
        }

        SuppressDuplicateDrawItem( CurrentLibEntry );
		GetScreen()->SetModify();

        // Move (and place ) the new draw items:
        HandleBlockBegin( DC, -1, GetScreen()->m_Curseur );
        HandleBlockEnd( DC );
        RedrawActiveWindow( DC, TRUE );
    }

    PQFreeFunc( Entries, ( void( * ) ( void* ) )FreeLibraryEntry );
}


/********************************************/
void WinEDA_LibeditFrame::SaveOneSymbol()
/********************************************/

/* Save in file the current symbol
 *  file format is like the standard libraries, but there is only one symbol
 *  Invisible pins are not saved
 */
{
    EDA_LibComponentStruct* LibEntry = CurrentLibEntry;
    int Unit = CurrentUnit, convert = CurrentConvert;
    int SymbUnit, SymbConvert;
    LibEDA_BaseStruct*      DrawEntry;
    wxString FullFileName, mask;
    wxString msg;
    FILE*    ExportFile;

    if( LibEntry->m_Drawings == NULL )
        return;

    /* Creation du fichier symbole */
    mask = wxT( "*" ) + g_SymbolExtBuffer;
    FullFileName = EDA_FileSelector( _( "Export symbol drawings:" ),
                                     g_RealLibDirBuffer,    /* Chemin par defaut */
                                     wxEmptyString,         /* nom fichier par defaut */
                                     g_SymbolExtBuffer,     /* extension par defaut */
                                     mask,                  /* Masque d'affichage */
                                     this,
                                     wxFD_SAVE,
                                     TRUE
                                     );
    if( FullFileName.IsEmpty() )
        return;

    ExportFile = wxFopen( FullFileName, wxT( "wt" ) );
    if( ExportFile == NULL )
    {
        msg.Printf( _( "Unable to create <%s>" ), FullFileName.GetData() );
        DisplayError( this, msg );
        return;
    }

    msg.Printf( _( "Save Symbol in [%s]" ), FullFileName.GetData() );
    Affiche_Message( msg );

    /* Creation de l'entete de la librairie */
    char Line[256];
    fprintf( ExportFile, "%s %d.%d  %s  Date: %s\n", LIBFILE_IDENT,
            LIB_VERSION_MAJOR, LIB_VERSION_MINOR,
            "SYMBOL", DateAndTime( Line ) );

    /* Creation du commentaire donnant le nom du composant */
    fprintf( ExportFile, "# SYMBOL %s\n#\n",
            (const char*) LibEntry->m_Name.m_Text.GetData() );

    /* Generation des lignes utiles */
    fprintf( ExportFile, "DEF %s", (const char*) LibEntry->m_Name.m_Text.GetData() );
    if( !LibEntry->m_Prefix.m_Text.IsEmpty() )
        fprintf( ExportFile, " %s", (const char*) LibEntry->m_Prefix.m_Text.GetData() );
    else
        fprintf( ExportFile, " ~" );

    fprintf( ExportFile, " %d %d %c %c %d %d %c\n",
             0, /* unused */
             LibEntry->m_TextInside,
             LibEntry->m_DrawPinNum ? 'Y' : 'N',
             LibEntry->m_DrawPinName ? 'Y' : 'N',
             1, 0 /* unused */, 'N' );

    /* Position / orientation / visibilite des champs */
    LibEntry->m_Prefix.WriteDescr( ExportFile );
    LibEntry->m_Name.WriteDescr( ExportFile );

    DrawEntry = LibEntry->m_Drawings;
    if( DrawEntry )
    {
        fprintf( ExportFile, "DRAW\n" );
        for( ; DrawEntry != NULL; DrawEntry = DrawEntry->Next() )
        {
            /* Elimination des elements non relatifs a l'unite */
            if( Unit && DrawEntry->m_Unit && (DrawEntry->m_Unit != Unit) )
                continue;
            if( convert && DrawEntry->m_Convert && (DrawEntry->m_Convert != convert) )
                continue;

            /* .Unit , . Convert est laisse a 0 ou mis a 1 */
            SymbUnit    = DrawEntry->m_Unit; if( SymbUnit > 1 )
                SymbUnit = 1;
            SymbConvert = DrawEntry->m_Convert;
            if( SymbConvert > 1 )
                SymbConvert = 1;

            switch( DrawEntry->Type() )
            {
            case COMPONENT_ARC_DRAW_TYPE:
                #define DRAWSTRUCT ( (LibDrawArc*) DrawEntry )
                DRAWSTRUCT->WriteDescr( ExportFile );
                break;

            case COMPONENT_CIRCLE_DRAW_TYPE:
                #undef DRAWSTRUCT
                #define DRAWSTRUCT ( (LibDrawCircle*) DrawEntry )
                DRAWSTRUCT->WriteDescr( ExportFile );
                break;

            case COMPONENT_GRAPHIC_TEXT_DRAW_TYPE:
                #undef DRAWSTRUCT
                #define DRAWSTRUCT ( (LibDrawText*) DrawEntry )
                DRAWSTRUCT->WriteDescr( ExportFile );
                break;

            case COMPONENT_RECT_DRAW_TYPE:
                #undef DRAWSTRUCT
                #define DRAWSTRUCT ( (LibDrawSquare*) DrawEntry )
                DRAWSTRUCT->WriteDescr( ExportFile );
                break;

            case COMPONENT_PIN_DRAW_TYPE:
                #undef DRAWSTRUCT
                #define DRAWSTRUCT ( (LibDrawPin*) DrawEntry )
                if( DRAWSTRUCT->m_Attributs & PINNOTDRAW )
                    break;
                DRAWSTRUCT->WriteDescr( ExportFile );
                break;

            case COMPONENT_POLYLINE_DRAW_TYPE:
                #undef DRAWSTRUCT
                #define DRAWSTRUCT ( (LibDrawPolyline*) DrawEntry )
                DRAWSTRUCT->WriteDescr( ExportFile );
                break;

            default:
                ;
            }
        }
        fprintf( ExportFile, "ENDDRAW\n" );
    }
    fprintf( ExportFile, "ENDDEF\n" );
    fclose( ExportFile );
}


/*****************************************************************/
void SuppressDuplicateDrawItem( EDA_LibComponentStruct* LibEntry )
/*****************************************************************/

/* Delete redundant graphic items.
 *  Useful after loading asymbole from a file symbol, because some graphic items
 *  can be duplicated.
 */
{
    LibEDA_BaseStruct* DEntryRef, * DEntryCompare;
    bool deleted;
    wxDC* DC = NULL;

    DEntryRef = LibEntry->m_Drawings;
    while( DEntryRef )
    {
        if( DEntryRef->Pnext == NULL )
            return;
        DEntryCompare = DEntryRef->Next();
        if( DEntryCompare == NULL )
            return;
        deleted = 0;
        while( DEntryCompare )
        {
            if( CompareSymbols( DEntryRef, DEntryCompare ) == TRUE )
            {
                DeleteOneLibraryDrawStruct( NULL, DC, LibEntry, DEntryRef, 1 );
                deleted = TRUE;
                break;
            }
            DEntryCompare = DEntryCompare->Next();
        }

        if( !deleted )
            DEntryRef = DEntryRef->Next();
        else
            DEntryRef = LibEntry->m_Drawings;
    }
}


/********************************************************************/
static bool CompareSymbols( LibEDA_BaseStruct* DEntryRef,
                            LibEDA_BaseStruct* DEntryCompare )
/********************************************************************/

/* Compare 2 graphic items (arc, lines ...).
 *  return FALSE si different
 *          TRUE si they are identical, and therefore redundant
 */
{
    int ii;
    int* ptref, * ptcomp;

    /* Comparaison des proprietes generales */
    if( DEntryRef->Type() != DEntryCompare->Type() )
        return FALSE;
    if( DEntryRef->m_Unit != DEntryCompare->m_Unit )
        return FALSE;
    if( DEntryRef->m_Convert != DEntryCompare->m_Convert )
        return FALSE;

    switch( DEntryRef->Type() )
    {
    case COMPONENT_ARC_DRAW_TYPE:
        #undef REFSTRUCT
        #undef CMPSTRUCT
        #define REFSTRUCT ( (LibDrawArc*) DEntryRef )
        #define CMPSTRUCT ( (LibDrawArc*) DEntryCompare )
        if( REFSTRUCT->m_Pos.x != CMPSTRUCT->m_Pos.x )
            return FALSE;
        if( REFSTRUCT->m_Pos.y != CMPSTRUCT->m_Pos.y )
            return FALSE;
        if( REFSTRUCT->t1 != CMPSTRUCT->t1 )
            return FALSE;
        if( REFSTRUCT->t2 != CMPSTRUCT->t2 )
            return FALSE;
        break;

    case COMPONENT_CIRCLE_DRAW_TYPE:
        #undef REFSTRUCT
        #undef CMPSTRUCT
        #define REFSTRUCT ( (LibDrawCircle*) DEntryRef )
        #define CMPSTRUCT ( (LibDrawCircle*) DEntryCompare )
        if( REFSTRUCT->m_Pos.x != CMPSTRUCT->m_Pos.x )
            return FALSE;
        if( REFSTRUCT->m_Pos.y != CMPSTRUCT->m_Pos.y )
            return FALSE;
        if( REFSTRUCT->m_Rayon != CMPSTRUCT->m_Rayon )
            return FALSE;
        break;

    case COMPONENT_GRAPHIC_TEXT_DRAW_TYPE:
        #undef REFSTRUCT
        #undef CMPSTRUCT
        #define REFSTRUCT ( (LibDrawText*) DEntryRef )
        #define CMPSTRUCT ( (LibDrawText*) DEntryCompare )
        if( REFSTRUCT->m_Pos != CMPSTRUCT->m_Pos )
            return FALSE;
        if( REFSTRUCT->m_Size != CMPSTRUCT->m_Size )
            return FALSE;
        if( REFSTRUCT->m_Text != CMPSTRUCT->m_Text )
            return FALSE;
        break;

    case COMPONENT_RECT_DRAW_TYPE:
        #undef REFSTRUCT
        #undef CMPSTRUCT
        #define REFSTRUCT ( (LibDrawSquare*) DEntryRef )
        #define CMPSTRUCT ( (LibDrawSquare*) DEntryCompare )
        if( REFSTRUCT->m_Pos != CMPSTRUCT->m_Pos )
            return FALSE;
        if( REFSTRUCT->m_End != CMPSTRUCT->m_End )
            return FALSE;
        break;

    case COMPONENT_PIN_DRAW_TYPE:
        #undef REFSTRUCT
        #undef CMPSTRUCT
        #define REFSTRUCT ( (LibDrawPin*) DEntryRef )
        #define CMPSTRUCT ( (LibDrawPin*) DEntryCompare )
        if( REFSTRUCT->m_Pos != CMPSTRUCT->m_Pos )
            return FALSE;
        break;

    case COMPONENT_POLYLINE_DRAW_TYPE:
        #undef REFSTRUCT
        #undef CMPSTRUCT
        #define REFSTRUCT ( (LibDrawPolyline*) DEntryRef )
        #define CMPSTRUCT ( (LibDrawPolyline*) DEntryCompare )
        if( REFSTRUCT->m_CornersCount != CMPSTRUCT->m_CornersCount )
            return FALSE;
        ptref  = REFSTRUCT->m_PolyList;
        ptcomp = CMPSTRUCT->m_PolyList;
        for( ii = 2 * REFSTRUCT->m_CornersCount; ii > 0; ii-- )
        {
            if( *ptref != *ptcomp )
                return FALSE;
            ptref++; ptcomp++;
        }

        break;

    default:
        ;
    }

    return TRUE;
}


/***************************************************************************/
/* Routine de placement du point d'ancrage ( reference des coordonnes pour */
/* le trace) du composant courant											  */
/*	Toutes les coord apparaissant dans les structures sont modifiees		  */
/*	pour repositionner le point repere par le curseur souris au point	  */
/*	d'ancrage ( coord 0,0 ).                                               */
/***************************************************************************/

void WinEDA_LibeditFrame::PlaceAncre()
{
    int ii, * ptsegm;
    int dx, dy;         /* Offsets de deplacement */
    EDA_LibComponentStruct* LibEntry;
    LibEDA_BaseStruct* DrawEntry;

	dx = -( GetScreen()->m_Curseur.x );
	dy = GetScreen()->m_Curseur.y;

    LibEntry = CurrentLibEntry;
    if( LibEntry == NULL )
        return;

	GetScreen()->SetModify();

    LibEntry->m_Name.m_Pos.x   += dx; LibEntry->m_Name.m_Pos.y += dy;
    LibEntry->m_Prefix.m_Pos.x += dx; LibEntry->m_Prefix.m_Pos.y += dy;

    DrawEntry = LibEntry->m_Drawings;
    while( DrawEntry )
    {
        switch( DrawEntry->Type() )
        {
        case COMPONENT_ARC_DRAW_TYPE:
            #undef STRUCT
            #define STRUCT ( (LibDrawArc*) DrawEntry )
            STRUCT->m_Pos.x      += dx;
            STRUCT->m_Pos.y      += dy;
            STRUCT->m_ArcStart.x += dx;
            STRUCT->m_ArcStart.y += dy;
            STRUCT->m_ArcEnd.x   += dx;
            STRUCT->m_ArcEnd.y   += dy;
            break;

        case COMPONENT_CIRCLE_DRAW_TYPE:
            #undef STRUCT
            #define STRUCT ( (LibDrawCircle*) DrawEntry )
            STRUCT->m_Pos.x += dx;
            STRUCT->m_Pos.y += dy;
            break;

        case COMPONENT_GRAPHIC_TEXT_DRAW_TYPE:
            #undef STRUCT
            #define STRUCT ( (LibDrawText*) DrawEntry )
            STRUCT->m_Pos.x += dx;
            STRUCT->m_Pos.y += dy;
            break;

        case COMPONENT_RECT_DRAW_TYPE:
            #undef STRUCT
            #define STRUCT ( (LibDrawSquare*) DrawEntry )
            STRUCT->m_Pos.x += dx;
            STRUCT->m_Pos.y += dy;
            STRUCT->m_End.x += dx;
            STRUCT->m_End.y += dy;
            break;

        case COMPONENT_PIN_DRAW_TYPE:
            #undef STRUCT
            #define STRUCT ( (LibDrawPin*) DrawEntry )
            STRUCT->m_Pos.x += dx;
            STRUCT->m_Pos.y += dy;
            break;

        case COMPONENT_POLYLINE_DRAW_TYPE:
            #undef STRUCT
            #define STRUCT ( (LibDrawPolyline*) DrawEntry )
            ptsegm = STRUCT->m_PolyList;
            for( ii = STRUCT->m_CornersCount; ii > 0; ii-- )
            {
                *ptsegm += dx; ptsegm++;
                *ptsegm += dy; ptsegm++;
            }
            break;

        default:
            ;
        }
        DrawEntry = DrawEntry->Next();
    }

    /* Redraw the symbol */
	GetScreen()->m_Curseur.x = GetScreen()->m_Curseur.y = 0;
    Recadre_Trace( TRUE );
	GetScreen()->SetRefreshReq();
}
