/**********************************************************/
/*	EECLASS.CPP											  */
/* fonctions relatives aux classes definies dans EESCHEMA */
/**********************************************************/

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

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

#include "protos.h"

/******************************************************************/
void SetStructFather( EDA_BaseStruct* Struct, BASE_SCREEN* Screen )
/******************************************************************/
{
    switch( Struct->m_StructType )
    {
    case DRAW_POLYLINE_STRUCT_TYPE:
    case DRAW_JUNCTION_STRUCT_TYPE:
    case DRAW_TEXT_STRUCT_TYPE:
    case DRAW_LABEL_STRUCT_TYPE:
    case DRAW_GLOBAL_LABEL_STRUCT_TYPE:
    case DRAW_LIB_ITEM_STRUCT_TYPE:
    case DRAW_SEGMENT_STRUCT_TYPE:
    case DRAW_BUSENTRY_STRUCT_TYPE:
    case DRAW_SHEET_STRUCT_TYPE:
    case DRAW_MARKER_STRUCT_TYPE:
    case DRAW_NOCONNECT_STRUCT_TYPE:
        Struct->m_Parent = Screen;
        break;

    case DRAW_SHEETLABEL_STRUCT_TYPE:
    case DRAW_PICK_ITEM_STRUCT_TYPE:
        break;

    default:
        break;
    }
}


/***************************************************************/
void EDA_BaseStruct::Place( WinEDA_DrawFrame* frame, wxDC* DC )
/***************************************************************/

/* place the struct in EEDrawList.
 *  if it is a new item, it it also put in undo list
 *  for an "old" item, saving it in undo list must be done before editiing, and not here!
 */
{
    if( m_Flags & IS_NEW )
    {
        Pnext = frame->m_CurrentScreen->EEDrawList;
        frame->m_CurrentScreen->EEDrawList = this;
        g_ItemToRepeat = this;
        if( frame->m_Ident == SCHEMATIC_FRAME )
            ( (WinEDA_SchematicFrame*) frame )->SaveCopyInUndoList( this, IS_NEW );
    }

    m_Flags = 0;
    frame->GetScreen()->SetModify();
    frame->GetScreen()->SetCurItem( NULL );
    frame->DrawPanel->ManageCurseur = NULL;
    frame->DrawPanel->ForceCloseManageCurseur = NULL;

    if( DC )
    {
        frame->DrawPanel->CursorOff( DC );      // Erase schematic cursor
        RedrawOneStruct( frame->DrawPanel, DC, this, GR_DEFAULT_DRAWMODE );
        frame->DrawPanel->CursorOn( DC );       // Display schematic cursor
    }
}


/***********************************************************************/
/* Class SCH_SCREEN: classe de gestion d'un affichage pour schematique */
/***********************************************************************/
static int table_zoom[] = { 1, 2, 4, 8, 16, 32, 64, 128, 0 }; /* Valeurs standards du zoom */

/* Constructeur de SCREEN */
SCH_SCREEN::SCH_SCREEN( int screentype ) : BASE_SCREEN( screentype )
{
    EEDrawList = NULL;                  /* Schematic items list */
    m_Zoom = 32;
    m_Grid = wxSize( 50, 50 );          /* pas de la grille */
    SetZoomList( table_zoom );
    SetGridList( g_GridList );
    m_UndoRedoCountMax = 10;
}


/****************************/
SCH_SCREEN::~SCH_SCREEN( void )
/****************************/
{
    ClearUndoRedoList();
    FreeDrawList();
}


/***********************************/
void SCH_SCREEN::FreeDrawList( void )
/***********************************/

/* Routine to clear (free) EESchema drawing list of a screen.
 */
{
    EDA_BaseStruct* DrawStruct;

    while( EEDrawList != NULL )
    {
        DrawStruct = EEDrawList;
        EEDrawList = EEDrawList->Pnext;
        delete DrawStruct;
    }
}


/**************************************************************/
void SCH_SCREEN::RemoveFromDrawList( EDA_BaseStruct* DrawStruct )
/**************************************************************/

/* If found in EEDrawList, remove DrawStruct from EEDrawList.
 *  DrawStruct is not deleted or modified
 */
{
    if( DrawStruct == EEDrawList )
        EEDrawList = EEDrawList->Pnext;
    else
    {
        EDA_BaseStruct* DrawList = EEDrawList;
        while( DrawList->Pnext )
        {
            if( DrawList->Pnext == DrawStruct )
            {
                DrawList->Pnext = DrawList->Pnext->Pnext;
                break;
            }
            DrawList = DrawList->Pnext;
        }
    }
}


/*********************************************************************/
/* Class EDA_ScreenList to handle the list of screens in a hierarchy */
/*********************************************************************/

EDA_ScreenList::EDA_ScreenList( EDA_BaseStruct* DrawStruct )

/* create the list of screens (i.e hierarchycal sheets) found in DrawStruct
 *  if DrawStruct == NULL: start from root sheet and the root screen is included in list
 */
{
    m_Count = 0;
    m_List  = NULL;
    m_Index = 0;

    /* Count the number of screens */
    BuildScreenList( NULL, DrawStruct, &m_Count );
    if( m_Count > NB_MAX_SHEET )
    {
        wxString msg;
        msg.Printf( wxT( "ReturnScreenList: Error: screen count > %d" ), NB_MAX_SHEET );
        DisplayError( NULL, msg );
    }


    m_List = (SCH_SCREEN**) MyZMalloc( sizeof(SCH_SCREEN *) * (m_Count + 2) );
    /* Fill the list */
    BuildScreenList( m_List, DrawStruct, &m_Count );
}


/*****************************************/
EDA_ScreenList::~EDA_ScreenList()
/*****************************************/
{
    if( m_List )
        free( m_List );
    m_List = NULL;
}


/*****************************************/
SCH_SCREEN* EDA_ScreenList::GetFirst( void )
/*****************************************/
{
    m_Index = 0;
    if( m_List )
        return *m_List;
    else
        return NULL;
}


/*****************************************/
SCH_SCREEN* EDA_ScreenList::GetNext( void )
/*****************************************/
{
    if( m_Index < m_Count )
        m_Index++;

    if( (m_Index < m_Count) && m_List )
    {
        return m_List[m_Index];
    }
    else
        return NULL;
}


/************************************************/
SCH_SCREEN* EDA_ScreenList::GetScreen( int index )
/************************************************/

/* return the m_List[index] item
 */
{
    SCH_SCREEN* screen = NULL;

    if( (index >= 0) && index < m_Count )
        screen = m_List[index];

    return screen;
}


/**************************************************/
void EDA_ScreenList::UpdateSheetNumberAndDate( void )
/**************************************************/

/* Update the sheet number, the sheet count and the date for all sheets in list
 */
{
    int         SheetNumber = 1;
    SCH_SCREEN* screen;
    wxString    sheet_date = GenDate();

    for( screen = GetFirst(); screen != NULL; screen = GetNext() )
    {
        screen->m_SheetNumber   = SheetNumber++;    /* Update the sheet number */
        screen->m_NumberOfSheet = m_Count;          /* Update the number of sheets */
        screen->m_Date = sheet_date;                /* Update the sheet date */
    }
}


/************************************************************************/
SCH_SCREEN** EDA_ScreenList::BuildScreenList( SCH_SCREEN** ScreenList,
                                              EDA_BaseStruct* DrawStruct, int* Count )
/************************************************************************/

/* Count the Hierachical sheet number (ScreenList == NULL )
 *  or fill the screen pointer buffer (ScreenList != NULL )
 *  If DrawStruct = NULL, search starts from Root screen, and puts root screen in list
 *  Recursive function !
 */
{
    bool            HasSubhierarchy = FALSE;
    EDA_BaseStruct* CurrStruct;

    if( *Count > NB_MAX_SHEET )
    {
        return ScreenList;
    }

    /* Read the current list and put Screen pointers in list */
    if( DrawStruct == NULL )
    {
        DrawStruct = ScreenSch->EEDrawList;
        if( ScreenList )
        {
            *ScreenList = ScreenSch;
            ScreenList++;
        }
        else
            (*Count)++;
    }

    CurrStruct = DrawStruct;
    while( CurrStruct )
    {
        if( CurrStruct->m_StructType == DRAW_SHEET_STRUCT_TYPE )
        {
            HasSubhierarchy = TRUE;
            if( ScreenList )
            {
                *ScreenList = (SCH_SCREEN*) CurrStruct;
                ScreenList++;
            }
            else
                (*Count)++;
        }
        CurrStruct = CurrStruct->Pnext;
    }

    if( !HasSubhierarchy )
        return ScreenList;

    /* Read the Sub Hierarchies  */
    CurrStruct = DrawStruct;
    while( CurrStruct )
    {
        if( CurrStruct->m_StructType == DRAW_SHEET_STRUCT_TYPE )
        {
            SCH_SCREEN* Screen = (SCH_SCREEN*) CurrStruct;

            /* Go to Subhierachy if needed
             *  (warning: BuildScreenList must not called with a NULL parameter
             *  for DrawStruct, because BuildScreenList restart from the root screen
             *  when DrawStruct == NULL */
            if( Screen->EEDrawList )
                ScreenList = BuildScreenList( ScreenList, Screen->EEDrawList, Count );
        }
        CurrStruct = CurrStruct->Pnext;
    }

    return ScreenList;
}
