/////////////////////////////////////////////////////////////////////////////
// Name:        sheet.cpp
// Purpose:
// Author:      jean-pierre Charras
// Modified by: Wayne Stambaugh
// Created:     08/02/2006 18:37:02
// RCS-ID:
// Copyright:   License GNU
// License:
/////////////////////////////////////////////////////////////////////////////

#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "class_drawpanel.h"
#include "confirm.h"
#include "gestfich.h"

#include "program.h"
#include "general.h"
#include "protos.h"

#include "dialog_sch_sheet_props.h"


static int     s_PreviousSheetWidth;
static int     s_PreviousSheetHeight;
static wxPoint s_OldPos;  /* Former position for cancellation or move ReSize */


/* Routine to edit the SheetName and the FileName for the sheet "Sheet" */
bool WinEDA_SchematicFrame::EditSheet( SCH_SHEET* aSheet, wxDC* aDC )
{
    bool edit = true;

    if( aSheet == NULL )
        return false;

    /* Get the new texts */
    RedrawOneStruct( DrawPanel, aDC, aSheet, g_XorMode );

    DrawPanel->m_IgnoreMouseEvents = true;

    DIALOG_SCH_SHEET_PROPS dlg( this );

    wxString units = GetUnitsLabel( g_UnitMetric );
    dlg.SetFileName( aSheet->GetFileName() );
    dlg.SetFileNameTextSize( ReturnStringFromValue( g_UnitMetric,
                                                    aSheet->m_FileNameSize,
                                                    m_InternalUnits ) );
    dlg.SetFileNameTextSizeUnits( units );
    dlg.SetSheetName( aSheet->m_SheetName );
    dlg.SetSheetNameTextSize( ReturnStringFromValue( g_UnitMetric,
                                                     aSheet->m_SheetNameSize,
                                                     m_InternalUnits ) );
    dlg.SetSheetNameTextSizeUnits( units );

    /* This ugly hack fixes a bug in wxWidgets 2.8.7 and likely earlier
     * versions for the flex grid sizer in wxGTK that prevents the last
     * column from being sized correctly.  It doesn't cause any problems
     * on win32 so it doesn't need to wrapped in ugly #ifdef __WXGTK__
     * #endif.
     */
    dlg.Layout();
    dlg.Fit();
    dlg.SetMinSize( dlg.GetSize() );

    if( dlg.ShowModal() == wxID_OK )
    {
        wxFileName fileName;
        wxString   msg;

        fileName = dlg.GetFileName();

        if( !fileName.IsOk() )
        {
            DisplayError( this, _( "File name is not valid! Aborted" ) );
            edit = false;
        }
        else
        {
            fileName.SetExt( SchematicFileExtension );

            /* m_CurrentSheet->m_AssociatedScreen must be a valid screen, and the
             * sheet must have a valid associated filename,
             * so we must call m_CurrentSheet->ChangeFileName to set a filename,
             * AND always when a new sheet is created ( when
             * m_CurrentSheet->m_AssociatedScreen is null ),
             * to create or set an Associated Screen
             */
            if( ( fileName.GetFullPath() != aSheet->GetFileName() )
                || ( aSheet->m_AssociatedScreen == NULL ) )
            {
                msg = _( "Changing the sheet file name can change all the schematic \
structures and cannot be undone.\nOk to continue renaming?" );

                if( aSheet->m_AssociatedScreen == NULL || IsOK( NULL, msg ) )
                {
                    // do not prompt on a new sheet. in fact, we should not allow a
                    // sheet to be created without a valid associated filename to be
                    // read from.
                    GetScreen()->ClearUndoRedoList();

                    // set filename and the associated screen
                    aSheet->ChangeFileName( this, fileName.GetFullPath() );
                }
            }

            aSheet->m_FileNameSize = ReturnValueFromString( g_UnitMetric,
                                                            dlg.GetFileNameTextSize(),
                                                            m_InternalUnits );

            aSheet->m_SheetName = dlg.GetSheetName();
            aSheet->m_SheetNameSize = ReturnValueFromString( g_UnitMetric,
                                                             dlg.GetSheetNameTextSize(),
                                                             m_InternalUnits );

            if( aSheet->m_SheetName.IsEmpty() )
            {
                aSheet->m_SheetName.Printf( wxT( "Sheet%8.8lX" ), GetTimeStamp() );
            }
        }
    }
    else
    {
        edit = false;
    }

    DrawPanel->MouseToCursorSchema();
    DrawPanel->m_IgnoreMouseEvents = false;

    RedrawOneStruct( DrawPanel, aDC, aSheet, GR_DEFAULT_DRAWMODE );

    return edit;
}


/* Move selected sheet with the cursor.
 * Callback function use by ManageCurseur.
 */
static void MoveSheet( WinEDA_DrawPanel* aPanel, wxDC* aDC, bool aErase )
{
    wxPoint        moveVector;
    SCH_SHEET_PIN* sheetLabel;
    BASE_SCREEN*   screen = aPanel->GetScreen();
    SCH_SHEET*     sheet = (SCH_SHEET*) screen->GetCurItem();

    if( aErase )
        RedrawOneStruct( aPanel, aDC, sheet, g_XorMode );

    if( sheet->m_Flags & IS_RESIZED )
    {
        sheet->m_Size.x = MAX( s_PreviousSheetWidth, screen->m_Curseur.x - sheet->m_Pos.x );
        sheet->m_Size.y = MAX( s_PreviousSheetHeight, screen->m_Curseur.y - sheet->m_Pos.y );
        sheetLabel = sheet->m_Label;

        while( sheetLabel )
        {
            if( sheetLabel->m_Edge )
                sheetLabel->m_Pos.x = sheet->m_Pos.x + sheet->m_Size.x;
            sheetLabel = sheetLabel->Next();
        }
    }
    else             /* Move Sheet */
    {
        moveVector = screen->m_Curseur - sheet->m_Pos;
        sheet->Move( moveVector );
    }

    RedrawOneStruct( aPanel, aDC, sheet, g_XorMode );
}


/*  Complete sheet move.  */
static void ExitSheet( WinEDA_DrawPanel* aPanel, wxDC* aDC )
{
    SCH_SCREEN* screen = (SCH_SCREEN*) aPanel->GetScreen();
    SCH_SHEET*  sheet  = (SCH_SHEET*) screen->GetCurItem();

    if( sheet == NULL )
        return;

    if( sheet->m_Flags & IS_NEW )
    {
        RedrawOneStruct( aPanel, aDC, sheet, g_XorMode );
        SAFE_DELETE( sheet );
    }
    else if( sheet->m_Flags & IS_RESIZED )
    {
        /* Resize in progress, cancel move. */
        RedrawOneStruct( aPanel, aDC, sheet, g_XorMode );
        sheet->m_Size.x = s_OldPos.x;
        sheet->m_Size.y = s_OldPos.y;
        RedrawOneStruct( aPanel, aDC, sheet, GR_DEFAULT_DRAWMODE );
        sheet->m_Flags = 0;
    }
    else if( sheet->m_Flags & IS_MOVED )
    {
        wxPoint curspos = screen->m_Curseur;
        aPanel->GetScreen()->m_Curseur = s_OldPos;
        MoveSheet( aPanel, aDC, true );
        RedrawOneStruct( aPanel, aDC, sheet, GR_DEFAULT_DRAWMODE );
        sheet->m_Flags = 0;
        screen->m_Curseur = curspos;
    }
    else
    {
        sheet->m_Flags = 0;
    }

    screen->SetCurItem( NULL );
    aPanel->ManageCurseur = NULL;
    aPanel->ForceCloseManageCurseur = NULL;
}


#define SHEET_MIN_WIDTH  500
#define SHEET_MIN_HEIGHT 150


/* Create hierarchy sheet.  */
SCH_SHEET* WinEDA_SchematicFrame::CreateSheet( wxDC* aDC )
{
    g_ItemToRepeat = NULL;

    SCH_SHEET* sheet = new SCH_SHEET( GetScreen()->m_Curseur );

    sheet->m_Flags     = IS_NEW | IS_RESIZED;
    sheet->m_TimeStamp = GetTimeStamp();
    sheet->SetParent( GetScreen() );
    sheet->m_AssociatedScreen = NULL;
    s_PreviousSheetWidth = SHEET_MIN_WIDTH;
    s_PreviousSheetHeight = SHEET_MIN_HEIGHT;

    // need to check if this is being added to the EEDrawList.
    // also need to update the hierarchy, if we are adding
    // a sheet to a screen that already has multiple instances (!)
    GetScreen()->SetCurItem( sheet );

    DrawPanel->ManageCurseur = MoveSheet;
    DrawPanel->ForceCloseManageCurseur = ExitSheet;

    DrawPanel->ManageCurseur( DrawPanel, aDC, false );

    return sheet;
}


void WinEDA_SchematicFrame::ReSizeSheet( SCH_SHEET* aSheet, wxDC* aDC )
{
    SCH_SHEET_PIN* sheetLabel;

    if( aSheet == NULL || aSheet->m_Flags & IS_NEW )
        return;

    if( aSheet->Type() != DRAW_SHEET_STRUCT_TYPE )
    {
        DisplayError( this,
                      wxT( "WinEDA_SchematicFrame::ReSizeSheet: Bad SructType" ) );
        return;
    }

    GetScreen()->SetModify();
    aSheet->m_Flags |= IS_RESIZED;

    s_OldPos.x = aSheet->m_Size.x;
    s_OldPos.y = aSheet->m_Size.y;

    s_PreviousSheetWidth = SHEET_MIN_WIDTH;
    s_PreviousSheetHeight = SHEET_MIN_HEIGHT;
    sheetLabel   = aSheet->m_Label;

    while( sheetLabel )
    {
        s_PreviousSheetWidth = MAX( s_PreviousSheetWidth,
                                    (int) ( ( sheetLabel->GetLength() + 1 ) *
                                            sheetLabel->m_Size.x ) );
        s_PreviousSheetHeight = MAX( s_PreviousSheetHeight,
                                     sheetLabel->m_Pos.y - aSheet->m_Pos.y );
        sheetLabel   = sheetLabel->Next();
    }

    DrawPanel->ManageCurseur = MoveSheet;
    DrawPanel->ForceCloseManageCurseur = ExitSheet;
    DrawPanel->ManageCurseur( DrawPanel, aDC, true );
}


void WinEDA_SchematicFrame::StartMoveSheet( SCH_SHEET* aSheet, wxDC* aDC )
{
    if( ( aSheet == NULL ) || ( aSheet->Type() != DRAW_SHEET_STRUCT_TYPE ) )
        return;

    DrawPanel->CursorOff( aDC );
    GetScreen()->m_Curseur = aSheet->m_Pos;
    DrawPanel->MouseToCursorSchema();

    s_OldPos = aSheet->m_Pos;
    aSheet->m_Flags |= IS_MOVED;
    DrawPanel->ManageCurseur = MoveSheet;
    DrawPanel->ForceCloseManageCurseur = ExitSheet;
    DrawPanel->ManageCurseur( DrawPanel, aDC, true );
    DrawPanel->CursorOn( aDC );
}