/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2009 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
 * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
 * Copyright (C) 2013 CERN (www.cern.ch)
 * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

/**
 * @file eeschema/files-io.cpp
 */

#include <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <gestfich.h>
#include <wxEeschemaStruct.h>
#include <appl_wxstruct.h>

#include <general.h>
#include <protos.h>
#include <eeschema_id.h>
#include <class_library.h>
#include <libeditframe.h>
#include <sch_sheet.h>
#include <sch_component.h>
#include <wildcards_and_files_ext.h>


bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName, bool aCreateBackupFile )
{
    wxString msg;
    wxFileName schematicFileName;
    FILE* f;
    bool success;

    if( aScreen == NULL )
        aScreen = GetScreen();

    /* If no name exists in the window yet - save as new. */
    if( aScreen->GetFileName().IsEmpty() )
        aSaveUnderNewName = true;

    /* Construct the name of the file to be saved */
    schematicFileName = aScreen->GetFileName();

    if( aSaveUnderNewName )
    {
        wxFileDialog dlg( this, _( "Schematic Files" ), wxGetCwd(),
                schematicFileName.GetFullName(), SchematicFileWildcard,
                wxFD_SAVE | wxFD_OVERWRITE_PROMPT );

        if( dlg.ShowModal() == wxID_CANCEL )
            return false;

        schematicFileName = dlg.GetPath();

        if( schematicFileName.GetExt() != SchematicFileExtension )
            schematicFileName.SetExt( SchematicFileExtension );
    }
    else
    {
        // Sheet file names are relative to the root sheet path which is the current
        // working directory.  The IsWritable funtion expects the path to be set.
        if( schematicFileName.GetPath().IsEmpty() )
            schematicFileName.Assign( wxFileName::GetCwd(), schematicFileName.GetFullName() );
    }

    if( !IsWritable( schematicFileName ) )
        return false;

    /* Create backup if requested */
    if( aCreateBackupFile && schematicFileName.FileExists() )
    {
        wxFileName backupFileName = schematicFileName;

        /* Rename the old file to a '.bak' one: */
        backupFileName.SetExt( SchematicBackupFileExtension );
        if( backupFileName.FileExists() )
            wxRemoveFile( backupFileName.GetFullPath() );

        if( !wxRenameFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) )
        {
            msg.Printf( _( "Could not save backup of file <%s>" ),
                    GetChars( schematicFileName.GetFullPath() ) );
            DisplayError( this, msg );
        }
    }

    /* Save */
    wxLogTrace( traceAutoSave,
                wxT( "Saving file <" ) + schematicFileName.GetFullPath() + wxT( ">" ) );

    if( ( f = wxFopen( schematicFileName.GetFullPath(), wxT( "wt" ) ) ) == NULL )
    {
        msg.Printf( _( "Failed to create file <%s>" ),
                    GetChars( schematicFileName.GetFullPath() ) );
        DisplayError( this, msg );
        return false;
    }

    success = aScreen->Save( f );

    if( success )
    {
        // Delete auto save file.
        wxFileName autoSaveFileName = schematicFileName;
        autoSaveFileName.SetName( wxT( "$" ) + schematicFileName.GetName() );

        if( autoSaveFileName.FileExists() )
        {
            wxLogTrace( traceAutoSave,
                        wxT( "Removing auto save file <" ) + autoSaveFileName.GetFullPath() +
                        wxT( ">" ) );

            wxRemoveFile( autoSaveFileName.GetFullPath() );
        }

        // Update the screen and frame info.
        if( aSaveUnderNewName )
            aScreen->SetFileName( schematicFileName.GetFullPath() );
        aScreen->ClrSave();
        aScreen->ClrModify();

        msg.Printf( _( "File %s saved" ), GetChars( aScreen->GetFileName() ) );
        SetStatusText( msg, 0 );
    }
    else
    {
        DisplayError( this, _( "File write operation failed." ) );
    }

    fclose( f );

    return success;
}


void SCH_EDIT_FRAME::Save_File( wxCommandEvent& event )
{
    int id = event.GetId();

    switch( id )
    {
    case ID_UPDATE_ONE_SHEET:
        SaveEEFile( NULL );
        break;

    case ID_SAVE_ONE_SHEET_UNDER_NEW_NAME:
        if( SaveEEFile( NULL, true ) )
        {
            CreateArchiveLibraryCacheFile( true );
        }
        break;
    }

    UpdateTitle();
}


bool SCH_EDIT_FRAME::LoadCacheLibrary( const wxString& aFilename )
{
    wxString msg;
    bool LibCacheExist = false;
    wxFileName fn = aFilename;

    /* Loading the project library cache
     * until apr 2009 the lib is named <root_name>.cache.lib
     * and after (due to code change): <root_name>-cache.lib
     * so if the <name>-cache.lib is not found, the old way will be tried
     */
    bool use_oldcachename = false;
    wxString cachename =  fn.GetName() + wxT( "-cache" );

    fn.SetName( cachename );
    fn.SetExt( SchematicLibraryFileExtension );

    if( ! fn.FileExists() )
    {
        fn = aFilename;
        fn.SetExt( wxT( "cache.lib" ) );
        use_oldcachename = true;
    }

    if( fn.FileExists() )
    {
        wxString errMsg;

        wxLogDebug( wxT( "Load schematic cache library file <%s>" ),
                    GetChars( fn.GetFullPath() ) );
        msg = wxT( "Load " ) + fn.GetFullPath();

        CMP_LIBRARY* LibCache = CMP_LIBRARY::LoadLibrary( fn, errMsg );

        if( LibCache )
        {
            LibCache->SetCache();
            msg += wxT( " OK" );

            if ( use_oldcachename )     // set the new name
            {
                fn.SetName( cachename );
                fn.SetExt( SchematicLibraryFileExtension );
                LibCache->SetFileName( fn );
            }

            LibCacheExist = true;
            CMP_LIBRARY::GetLibraryList().push_back( LibCache );
        }
        else
        {
            wxString prompt;

            prompt.Printf( _( "Component library <%s> failed to load.\nError: %s" ),
                           GetChars( fn.GetFullPath() ),
                           GetChars( errMsg ) );
            DisplayError( this, prompt );
            msg += _( " ->Error" );
        }

        PrintMsg( msg );
    }

    return LibCacheExist;
}


bool SCH_EDIT_FRAME::LoadOneEEProject( const wxString& aFileName, bool aIsNew )
{
    SCH_SCREEN* screen;
    wxString    FullFileName, msg;
    bool        LibCacheExist = false;
    SCH_SCREENS ScreenList;

    for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() )
    {
        if( screen->IsModify() )
            break;
    }

    if( screen )
    {
        if( !IsOK( this, _( "Discard changes to the current schematic?" ) ) )
            return false;
    }

    FullFileName = aFileName;

    if( ( FullFileName.IsEmpty() ) && !aIsNew )
    {
        wxFileDialog dlg( this, _( "Open Schematic" ), wxGetCwd(),
                          wxEmptyString, SchematicFileWildcard,
                          wxFD_OPEN | wxFD_FILE_MUST_EXIST );

        if( dlg.ShowModal() == wxID_CANCEL )
            return false;

        FullFileName = dlg.GetPath();
    }

    wxFileName fn = FullFileName;

    if( fn.IsRelative() )
    {
        fn.MakeAbsolute();
        FullFileName = fn.GetFullPath();
    }

    if( !wxGetApp().LockFile( FullFileName ) )
    {
        DisplayError( this, _( "This file is already open." ) );
        return false;
    }

    // Clear the screen before open a new file
    if( g_RootSheet )
    {
        SAFE_DELETE( g_RootSheet );
    }

    CreateScreens();
    screen = GetScreen();

    wxLogDebug( wxT( "Loading schematic " ) + FullFileName );
    wxSetWorkingDirectory( fn.GetPath() );

    screen->SetFileName( FullFileName );
    g_RootSheet->SetFileName( FullFileName );
    SetStatusText( wxEmptyString );
    ClearMsgPanel();

    screen->ClrModify();

    if( aIsNew )
    {
        /* SCH_SCREEN constructor does this now
        screen->SetPageSettings( PAGE_INFO( wxT( "A4" ) ) );
        */

        screen->SetZoom( 32 );
        screen->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId  );

        TITLE_BLOCK tb;
        wxString    title;

        title += NAMELESS_PROJECT;
        title += wxT( ".sch" );
        tb.SetTitle( title );
        screen->SetTitleBlock( tb );

        GetScreen()->SetFileName( title );

        LoadProjectFile( wxEmptyString, true );
        Zoom_Automatique( false );
        SetSheetNumberAndCount();
        m_canvas->Refresh();
        return true;
    }

    // Reloading configuration.
    msg = _( "Ready\nWorking dir: \n" ) + wxGetCwd();
    PrintMsg( msg );

    LoadProjectFile( wxEmptyString, false );

    // Clear (if needed) the current active library in libedit because it could be
    // removed from memory
    LIB_EDIT_FRAME::EnsureActiveLibExists();

    // Delete old caches.
    CMP_LIBRARY::RemoveCacheLibrary();

    LibCacheExist = LoadCacheLibrary( g_RootSheet->GetScreen()->GetFileName() );

    if( !wxFileExists( g_RootSheet->GetScreen()->GetFileName() ) && !LibCacheExist )
    {
        Zoom_Automatique( false );
        msg.Printf( _( "File <%s> not found." ),
                    GetChars( g_RootSheet->GetScreen()->GetFileName() ) );
        DisplayInfoMessage( this, msg );
        return false;
    }

    // load the project.
    g_RootSheet->SetScreen( NULL );
    bool diag = g_RootSheet->Load( this );
    SetScreen( m_CurrentSheet->LastScreen() );

    UpdateFileHistory( g_RootSheet->GetScreen()->GetFileName() );

    /* Redraw base screen (ROOT) if necessary. */
    GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
    Zoom_Automatique( false );
    SetSheetNumberAndCount();
    m_canvas->Refresh( true );
    return diag;
}


bool SCH_EDIT_FRAME::AppendOneEEProject()
{
    SCH_SCREEN* screen;
    wxString    FullFileName;
    wxString msg;

    screen = GetScreen();

    if( !screen )
    {
        wxLogError( wxT("Document not ready, cannot import") );
        return false;
    }

    // open file chooser dialog
    wxFileDialog dlg( this, _( "Import Schematic" ), wxGetCwd(),
                      wxEmptyString, SchematicFileWildcard,
                      wxFD_OPEN | wxFD_FILE_MUST_EXIST );

    if( dlg.ShowModal() == wxID_CANCEL )
        return false;

    FullFileName = dlg.GetPath();

    wxFileName fn = FullFileName;

    if( fn.IsRelative() )
    {
        fn.MakeAbsolute();
        FullFileName = fn.GetFullPath();
    }

    LoadCacheLibrary( FullFileName );

    wxLogDebug( wxT( "Importing schematic " ) + FullFileName );

    // load the project
    bool success = LoadOneEEFile( screen, FullFileName, true );
    if( success )
    {
        // load sub-sheets
        EDA_ITEM* bs = screen->GetDrawItems();
        while( bs )
        {
            // do not append hierarchical sheets
            if( bs->Type() ==  SCH_SHEET_T )
            {
                screen->Remove( (SCH_SHEET*) bs );
            }
            // clear annotation and init new time stamp for the new components
            else if( bs->Type() == SCH_COMPONENT_T )
            {
                ( (SCH_COMPONENT*) bs )->SetTimeStamp( GetNewTimeStamp() );
                ( (SCH_COMPONENT*) bs )->ClearAnnotation( NULL );
                // Clear flags, which are set by these previous modifications:
                bs->ClearFlags();
            }

            bs = bs->Next();
        }
    }

    // redraw base screen (ROOT) if necessary
    GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
    Zoom_Automatique( false );
    SetSheetNumberAndCount();
    m_canvas->Refresh( true );
    return success;
}


void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
{
    wxString msg = _( "This operation cannot be undone. "
            "Besides, take into account that hierarchical sheets will not be appended.\n\n"
            "Do you want to save the current document before proceeding?" );

    if( IsOK( this, msg ) )
        OnSaveProject( event );

    AppendOneEEProject();
}


void SCH_EDIT_FRAME::OnSaveProject( wxCommandEvent& aEvent )
{
    SCH_SCREEN* screen;
    wxFileName  fn;
    wxFileName  tmp;
    SCH_SCREENS ScreenList;

    fn = g_RootSheet->GetFileName();

    // Ensure a path exists. if no path, assume the cwd is used
    // The IsWritable function expects the path to be set
    if( !fn.GetPath().IsEmpty() )
        tmp.AssignDir( fn.GetPath() );
    else
        tmp.AssignDir( wxGetCwd() );
    tmp.AssignDir( fn.GetPath() );

    if( !IsWritable( tmp ) )
        return;

    for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() )
        SaveEEFile( screen );

    CreateArchiveLibraryCacheFile();

    UpdateTitle();
}


bool SCH_EDIT_FRAME::doAutoSave()
{
    wxFileName tmpFileName = g_RootSheet->GetFileName();
    wxFileName fn = tmpFileName;
    wxFileName  tmp;
    SCH_SCREENS screens;
    bool autoSaveOk = true;

    tmp.AssignDir( fn.GetPath() );

    if( !IsWritable( tmp ) )
        return false;

    for( SCH_SCREEN* screen = screens.GetFirst(); screen != NULL; screen = screens.GetNext() )
    {
        // Only create auto save files for the schematics that have been modified.
        if( !screen->IsSave() )
            continue;

        tmpFileName = fn = screen->GetFileName();

        // Auto save file name is the normal file name prefixed with $.
        fn.SetName( wxT( "$" ) + fn.GetName() );

        screen->SetFileName( fn.GetFullPath() );

        if( SaveEEFile( screen, false, NO_BACKUP_FILE ) )
            screen->SetModify();
        else
            autoSaveOk = false;

        screen->SetFileName( tmpFileName.GetFullPath() );
    }

    if( autoSaveOk )
        m_autoSaveState = false;

    return autoSaveOk;
}
