/****************************************/
/*	Module to load/save EESchema files.	*/
/****************************************/

#include "fctsys.h"
#include "confirm.h"
#include "kicad_string.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"
#include "protos.h"

/* in read_from_file_schematic_items_description.cpp */
SCH_ITEM* ReadTextDescr( FILE* aFile, wxString& aMsgDiag, char* aLine,
                         int aBufsize, int* aLineNum, int aSchematicFileVersion );
int       ReadSheetDescr( wxWindow*    frame,
                          char*        Line,
                          FILE*        f,
                          wxString&    aMsgDiag,
                          int*         aLineNum,
                          BASE_SCREEN* Window );
int ReadSchemaDescr( wxWindow*    frame,
                     char*        Line,
                     FILE*        f,
                     wxString&    aMsgDiag,
                     int*         aLineNum,
                     BASE_SCREEN* Window );

int ReadPartDescr( wxWindow*    frame,
                   char*        Line,
                   FILE*        f,
                   wxString&    aMsgDiag,
                   int*         aLineNum,
                   BASE_SCREEN* Window );

/* Fonctions locales */
static void LoadLayers( FILE* f, int* linecnt );

/************************************************************************************************/
bool WinEDA_SchematicFrame::LoadOneEEFile( SCH_SCREEN* screen, const wxString& FullFileName )
/************************************************************************************************/

/* Routine to load an EESchema file.
 *  Returns true if file has been loaded (at least partially.)
 */
{
    char                 Line[1024], * SLine;
    char                 Name1[256],
                         Name2[256];
    int                  ii, layer;
    wxPoint              pos;
    bool                 Failed = FALSE;
    SCH_ITEM*            Phead, * Pnext;
    DrawJunctionStruct*  ConnectionStruct;
    DrawPolylineStruct*  PolylineStruct;
    EDA_DrawLineStruct*  SegmentStruct;
    DrawBusEntryStruct*  RaccordStruct;
    DrawMarkerStruct*    MarkerStruct;
    DrawNoConnectStruct* NoConnectStruct;
    int                  LineCount; /* Decompte de num de ligne lue dans eeload() */
    wxString             MsgDiag;   /* Error and log messages */

    FILE*                f;

    if( screen == NULL )
        return FALSE;
    if( FullFileName.IsEmpty() )
        return FALSE;

    screen->SetCurItem( NULL );
    screen->m_FileName = FullFileName;

    LineCount = 1;
    if( ( f = wxFopen( FullFileName, wxT( "rt" ) ) ) == NULL )
    {
        MsgDiag = _( "Failed to open " ) + FullFileName;
        DisplayError( this, MsgDiag );
        return FALSE;
    }

    MsgDiag = _( "Loading " ) + screen->m_FileName;
    PrintMsg( MsgDiag );

    if( fgets( Line, 1024 - 1, f ) == NULL
        || strncmp( Line + 9, SCHEMATIC_HEAD_STRING, sizeof(SCHEMATIC_HEAD_STRING) - 1 )
        != 0 )
    {
        MsgDiag = FullFileName + _( " is NOT an EESchema file!" );
        DisplayError( this, MsgDiag );
        fclose( f );
        return FALSE;
    }

    //get the file version here. TODO: Support version numbers > 9
    char version = Line[9 + sizeof(SCHEMATIC_HEAD_STRING)];
    int  ver     = version - '0';
    if( ver > EESCHEMA_VERSION )
    {
        MsgDiag = FullFileName + _(
            " was created by a more recent version of EESchema and may not load correctly. Please consider updating!" );
        DisplayInfo( this, MsgDiag );
    }

#if 0

    // Compile it if the new version is unreadable by previous eeschema versions
    else if( ver < EESCHEMA_VERSION )
    {
        MsgDiag = FullFileName + _(
            " was created by an older version of EESchema. It will be stored in the new file format when you save this file again." );
        DisplayInfo( this, MsgDiag );
    }
#endif

    LineCount++;
    if( fgets( Line, 1024 - 1, f ) == NULL || strncmp( Line, "LIBS:", 5 ) != 0 )
    {
        MsgDiag = FullFileName + _( " is NOT an EESchema file!" );
        DisplayError( this, MsgDiag );
        fclose( f );
        return FALSE;
    }

    LoadLayers( f, &LineCount );

    while( !feof( f ) && GetLine( f, Line, &LineCount, sizeof(Line) ) != NULL )
    {
        SLine = Line;
        while( (*SLine != ' ' ) && *SLine )
            SLine++;

        switch( Line[0] )
        {
        case '$':           /* identification de bloc */
            if( Line[1] == 'C' )
                Failed = ReadPartDescr( this, Line, f, MsgDiag, &LineCount, screen );

            else if( Line[1] == 'S' )
                Failed = ReadSheetDescr( this, Line, f, MsgDiag, &LineCount, screen );

            else if( Line[1] == 'D' )
                Failed = ReadSchemaDescr( this, Line, f, MsgDiag, &LineCount, screen );

            break;

        case 'L':           /* Its a library item. */
            Failed = ReadPartDescr( this, Line, f, MsgDiag, &LineCount, screen );
            break;          /* Fin lecture 1 composant */


        case 'W':        /* Its a Segment (WIRE or BUS) item. */
            if( sscanf( SLine, "%s %s", Name1, Name2 ) != 2  )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file Segment struct error at line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
                break;
            }
            layer = LAYER_NOTES;
            if( Name1[0] == 'W' )
                layer = LAYER_WIRE;
            if( Name1[0] == 'B' )
                layer = LAYER_BUS;

            SegmentStruct = new EDA_DrawLineStruct( wxPoint( 0, 0 ), layer );

            LineCount++;
            if( fgets( Line, 256 - 1, f ) == NULL
                || sscanf( Line, "%d %d %d %d ",
                    &SegmentStruct->m_Start.x, &SegmentStruct->m_Start.y,
                    &SegmentStruct->m_End.x, &SegmentStruct->m_End.y ) != 4 )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file Segment struct error at line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
                SAFE_DELETE( SegmentStruct );
                break;
            }

            if( !Failed )
            {
                SegmentStruct->SetNext( screen->EEDrawList );
                screen->EEDrawList = SegmentStruct;
            }
            break;


        case 'E':        /* Its a Raccord (WIRE or BUS) item. */
            if( sscanf( SLine, "%s %s", Name1, Name2 ) != 2  )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file record struct error at line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
                break;
            }

            ii = WIRE_TO_BUS;
            if( Name1[0] == 'B' )
                ii = BUS_TO_BUS;
            RaccordStruct = new DrawBusEntryStruct( wxPoint( 0, 0 ), '\\', ii );

            LineCount++;
            if( fgets( Line, 256 - 1, f ) == NULL
                || sscanf( Line, "%d %d %d %d ",
                    &RaccordStruct->m_Pos.x, &RaccordStruct->m_Pos.y,
                    &RaccordStruct->m_Size.x, &RaccordStruct->m_Size.y ) != 4 )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file Bus Entry struct error at line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
                SAFE_DELETE( RaccordStruct );
                break;
            }

            if( !Failed )
            {
                RaccordStruct->m_Size.x -= RaccordStruct->m_Pos.x;
                RaccordStruct->m_Size.y -= RaccordStruct->m_Pos.y;
                RaccordStruct->SetNext( screen->EEDrawList );
                screen->EEDrawList = RaccordStruct;
            }
            break;

        case 'P':        /* Its a polyline item. */
            if( sscanf( SLine, "%s %s %d", Name1, Name2, &ii ) != 3 )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file polyline struct error at line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
                break;
            }
            layer = LAYER_NOTES;
            if( Name2[0] == 'W' )
                layer = LAYER_WIRE;
            if( Name2[0] == 'B' )
                layer = LAYER_BUS;

            PolylineStruct = new DrawPolylineStruct( layer );
            for( unsigned jj = 0; jj < (unsigned)ii; jj++ )
            {
                LineCount++;
                wxPoint point;
                if( fgets( Line, 256 - 1, f ) == NULL
                    || sscanf( Line, "%d %d", &point.x, &point.y ) != 2 )
                {
                    MsgDiag.Printf(
                        wxT( "EESchema file polyline struct error at line %d, aborted" ),
                        LineCount );
                    MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                    Failed = true;
                    SAFE_DELETE( PolylineStruct );
                    break;
                }

                PolylineStruct->AddPoint( point );
            }

            if( !Failed )
            {
                PolylineStruct->SetNext( screen->EEDrawList );
                screen->EEDrawList = PolylineStruct;
            }
            break;

        case 'C':                       /* It is a connection item. */
            ConnectionStruct = new DrawJunctionStruct( wxPoint( 0, 0 ) );

            if( sscanf( SLine, "%s %d %d", Name1,
                    &ConnectionStruct->m_Pos.x,
                    &ConnectionStruct->m_Pos.y ) != 3 )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file connection struct error at line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
                SAFE_DELETE( ConnectionStruct );
            }
            else
            {
                ConnectionStruct->SetNext( screen->EEDrawList );
                screen->EEDrawList = ConnectionStruct;
            }
            break;

        case 'N':                       /* It is a NoConnect item. */
            if( sscanf( SLine, "%s %d %d", Name1, &pos.x, &pos.y ) != 3 )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file NoConnect struct error at line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
            }
            else
            {
                NoConnectStruct = new DrawNoConnectStruct( pos );

                NoConnectStruct->SetNext( screen->EEDrawList );
                screen->EEDrawList = NoConnectStruct;
            }
            break;

        case 'K':                       /* It is a Marker item. */
            if( sscanf( SLine, "%s %d %d", Name1, &pos.x, &pos.y ) != 3 )
            {
                MsgDiag.Printf(
                    wxT( "EESchema file marker struct error line %d, aborted" ),
                    LineCount );
                MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
                Failed = true;
            }
            else
            {
                char* text;
                char  BufLine[1024];
                MarkerStruct = new DrawMarkerStruct( pos, wxEmptyString );

                ii = ReadDelimitedText( BufLine, Line, 256 );
                MarkerStruct->m_Type = (TypeMarker) ( (Name1[0] & 255) - 'A' );
                if( MarkerStruct->m_Type < 0 )
                    MarkerStruct->m_Type = MARQ_UNSPEC;
                if( ii )
                    MarkerStruct->m_Comment = CONV_FROM_UTF8( BufLine );
                text = strstr( Line, " F=" );
                if( text )
                {
                    sscanf( text + 3, "%X", &ii );
                    MarkerStruct->m_MarkFlags = ii;
                }
                MarkerStruct->SetNext( screen->EEDrawList );
                screen->EEDrawList = MarkerStruct;
            }
            break;

        case 'T':                       /* It is a text item. */
        {
            SCH_ITEM* Struct;
            Struct = ReadTextDescr( f, MsgDiag, Line, sizeof(Line), &LineCount, version);
            if( Struct )
            {
                Struct->SetNext( screen->EEDrawList );
                screen->EEDrawList = Struct;
            }
            else
                Failed = true;
            break;
        }

        default:
            Failed = true;
            MsgDiag.Printf(
                wxT( "EESchema file undef structdef at line %d, aborted" ),
                LineCount );
            MsgDiag << wxT( "\n" ) << CONV_FROM_UTF8( Line );
            break;
        }

        if( Failed )
        {
            DisplayError( this, MsgDiag );
            break;
        }
    }

    /* EEDrawList was constructed in reverse order - reverse it back: */
    Phead = NULL;
    while( screen->EEDrawList )
    {
        Pnext = screen->EEDrawList;
        screen->EEDrawList = screen->EEDrawList->Next();
        Pnext->SetNext( Phead );
        Phead = Pnext;
    }

    screen->EEDrawList = Phead;

#if 0 && defined (DEBUG)
    screen->Show( 0, std::cout );
#endif

    fclose( f );

    TestDanglingEnds( screen->EEDrawList, NULL );

    MsgDiag = _( "Done Loading " ) + screen->m_FileName;
    PrintMsg( MsgDiag );

    return true;    /* Although it may be that file is only partially loaded. */
}


/***********************************/
static void LoadLayers( FILE* f, int* linecnt )
/***********************************/

/* Load the Layer Struct from a file
 */
{
    int  cnt = 0, Number;
    char Line[1024];

//int Mode,Color,Layer;
    char Name[256];

    GetLine( f, Line, NULL, sizeof(Line) );       /* read line */
    (*linecnt)++;
    sscanf( Line, "%s %d %d", Name, &Number, &g_LayerDescr.CurrentLayer );
    if( strcmp( Name, "EELAYER" ) !=0 )
    {
        /* error : init par defaut */
        Number = MAX_LAYER;
    }
    if( Number <= 0 )
        Number = MAX_LAYER;
    if( Number > MAX_LAYER )
        Number = MAX_LAYER;

    g_LayerDescr.NumberOfLayers = Number;

    while( GetLine( f, Line, NULL, sizeof(Line) ) )
    {
        (*linecnt)++;
        if( strnicmp( Line, "EELAYER END", 11 ) == 0 )
            break;
        cnt++;
    }
}