/////////////////////////////////////////////////////////////////////////////

// Name:        zones_by_polygon.cpp
// Purpose:
// Author:      jean-pierre Charras
// Modified by:
// Created:     25/01/2006 11:35:19
// RCS-ID:
// Copyright:   GNU License
// Licence:     GNU License
/////////////////////////////////////////////////////////////////////////////

// Generated by DialogBlocks (unregistered), 25/01/2006 11:35:19

#if defined (__GNUG__) && !defined (NO_GCC_PRAGMA)
#pragma implementation "dialog_zones_by_polygon.h"
#endif

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

#include "common.h"
#include "pcbnew.h"

#include "autorout.h"
#include "cell.h"
#include "trigo.h"

#include "protos.h"

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

////@begin includes
////@end includes


////@begin XPM images
////@end XPM images

/* Imported functions */
void        Build_Zone( WinEDA_PcbFrame* frame, wxDC* DC, int net_code,
                        bool Zone_Exclude_Pads, bool Zone_Create_Thermal_Relief );

/* Local functions */
static void Display_Zone_Netname( WinEDA_PcbFrame* frame );
static void Exit_Zones( WinEDA_DrawPanel* Panel, wxDC* DC );
static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase );

/* Local variables */
static bool Zone_45_Only      = FALSE;
static bool Zone_Exclude_Pads = TRUE;
static bool s_Zone_Create_Thermal_Relief = TRUE;
static int s_Zone_Layer;			// Layer used to put the current zone
static int  s_NetcodeSelection;		// Net code selection for the current zone
static int s_NetSortingOpt;			// For the net list: sort option (by alphabetic order or bay pad count order

#define ZONE_NET_SORT_OPTION_KEY wxT("Zone_NetSort_Opt")


#include "dialog_zones_by_polygon.cpp"


/**************************************************************/
void WinEDA_PcbFrame::Edit_Zone_Width( wxDC* DC, SEGZONE* aZone )
/**************************************************************/

/* Edite (change la largeur des segments) la zone Zone.
 *  La zone est constituee des segments zones de meme TimeStamp
 */
{
    bool     modify = FALSE;
    double   f_new_width;
    int      w_tmp;
    wxString Line;
    wxString Msg( _( "New zone segment width: " ) );

    if( aZone == NULL )
        return;

    f_new_width = To_User_Unit( g_UnitMetric, aZone->m_Width, GetScreen()->GetInternalUnits() );

    Line.Printf( wxT( "%.4f" ), f_new_width );

    Msg += g_UnitMetric ? wxT( "(mm)" ) : wxT( "(\")" );
    if( Get_Message( Msg, Line, this ) != 0 )
        return;

    w_tmp = g_DesignSettings.m_CurrentTrackWidth;
    Line.ToDouble( &f_new_width );

    g_DesignSettings.m_CurrentTrackWidth = From_User_Unit( g_UnitMetric,
                                                          f_new_width, GetScreen(
                                                              )->GetInternalUnits() );

    for( SEGZONE* zone = m_Pcb->m_Zone;  zone;  zone = zone->Next() )
    {
        if( zone->m_TimeStamp == aZone->m_TimeStamp )
        {
            modify = TRUE;
            Edit_TrackSegm_Width( DC, zone );
        }
    }

    g_DesignSettings.m_CurrentTrackWidth = w_tmp;
    if( modify )
    {
        GetScreen()->SetModify();
        DrawPanel->Refresh();
    }
}


/**********************************************************/
void WinEDA_PcbFrame::Delete_Zone( wxDC* DC, SEGZONE* aZone )
/**********************************************************/

/* Remove the zone which include the segment aZone.
 *  A zone is a group of segments which have the same TimeStamp
 */
{
    if( aZone == NULL )
        return;

    int           nb_segm   = 0;
    bool          modify    = FALSE;
    unsigned long TimeStamp = aZone->m_TimeStamp;   // Save reference time stamp (aZone will be deleted)

    SEGZONE*      next;
    for( SEGZONE* zone = m_Pcb->m_Zone; zone != NULL; zone = next )
    {
        next = zone->Next();
        if( zone->m_TimeStamp == TimeStamp )
        {
            modify = TRUE;

            /* Erase segment from screen */
            Trace_Une_Piste( DrawPanel, DC, zone, nb_segm, GR_XOR );
            /* remove item from linked list and free memory */
            zone->DeleteStructure();
        }
    }

    if( modify )
    {
        GetScreen()->SetModify();
        GetScreen()->SetRefreshReq();
    }
}


/*****************************************************************************/
EDGE_ZONE* WinEDA_PcbFrame::Del_SegmEdgeZone( wxDC* DC, EDGE_ZONE* edge_zone )
/*****************************************************************************/
/* Routine d'effacement du segment de limite zone en cours de trace */
{
    EDGE_ZONE* segm;

    if( m_Pcb->m_CurrentLimitZone )
        segm = m_Pcb->m_CurrentLimitZone;
    else
        segm = edge_zone;

    if( segm == NULL )
        return NULL;

    Trace_DrawSegmentPcb( DrawPanel, DC, segm, GR_XOR );

    m_Pcb->m_CurrentLimitZone = segm->Next();
    delete segm;

    segm = m_Pcb->m_CurrentLimitZone;
    SetCurItem( segm );

    if( segm )
    {
        segm->Pback = NULL;
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, TRUE );
    }
    else
    {
        DrawPanel->ManageCurseur = NULL;
        DrawPanel->ForceCloseManageCurseur = NULL;
        SetCurItem( NULL );
    }
    return segm;
}


/*********************************************/
void WinEDA_PcbFrame::CaptureNetName( wxDC* DC )
/*********************************************/

/* routine permettant de capturer le nom net net (netcode) d'un pad
 *  ou d'une piste pour l'utiliser comme netcode de zone
 */
{
    D_PAD*  pt_pad = 0;
    TRACK*  adrpiste;
    MODULE* Module;
    int     masquelayer = g_TabOneLayerMask[GetScreen()->m_Active_Layer];
    int     netcode;

    netcode = -1;
    MsgPanel->EraseMsgBox();
    adrpiste = Locate_Pistes( m_Pcb->m_Track, masquelayer, CURSEUR_OFF_GRILLE );
    if( adrpiste == NULL )
    {
        pt_pad = Locate_Any_Pad( m_Pcb, CURSEUR_OFF_GRILLE );

        if( pt_pad )  /* Verif qu'il est bien sur la couche active */
        {
            Module = (MODULE*) pt_pad->m_Parent;
            pt_pad = Locate_Pads( Module, g_TabOneLayerMask[GetScreen()->m_Active_Layer],
                                  CURSEUR_OFF_GRILLE );
        }
        if( pt_pad )
        {
            pt_pad->Display_Infos( this );
            netcode = pt_pad->GetNet();
        }
    }
    else
    {
        adrpiste->Display_Infos( this );
        netcode = adrpiste->GetNet();
    }

    // Mise en surbrillance du net
    if( g_HightLigt_Status )
        Hight_Light( DC );

    g_HightLigth_NetCode = netcode;
    if( g_HightLigth_NetCode >= 0 )
    {
        Hight_Light( DC );
    }

    /* Affichage du net selectionne pour la zone a tracer */
    Display_Zone_Netname( this );
}


/*******************************************************/
static void Display_Zone_Netname( WinEDA_PcbFrame* frame )
/*******************************************************/

/*
 *  Affiche le net_code et le nom de net couramment selectionne
 */
{
    EQUIPOT* pt_equipot;
    wxString line;

    pt_equipot = frame->m_Pcb->m_Equipots;

    if( g_HightLigth_NetCode > 0 )
    {
        for( ; pt_equipot != NULL; pt_equipot = (EQUIPOT*) pt_equipot->Pnext )
        {
            if( pt_equipot->GetNet() == g_HightLigth_NetCode )
                break;
        }

        if( pt_equipot )
        {
            line.Printf( wxT( "Zone: Net[%d] <%s>" ), g_HightLigth_NetCode,
                        pt_equipot->m_Netname.GetData() );
        }
        else
            line.Printf( wxT( "Zone: NetCode[%d], Equipot not found" ),
                         g_HightLigth_NetCode );
    }

    line = _( "Zone: No net selected" );

    frame->Affiche_Message( line );
}


/********************************************************/
static void Exit_Zones( WinEDA_DrawPanel* Panel, wxDC* DC )
/********************************************************/

/**
 * Function Exit_Zones
 * cancels the Begin_Zone state if at least one EDGE_ZONE has been created.
 */
{
    WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) Panel->m_Parent;

    if( pcbframe->m_Pcb->m_CurrentLimitZone )
    {
        if( Panel->ManageCurseur )  // trace in progress
        {
            Panel->ManageCurseur( Panel, DC, 0 );
        }
        pcbframe->DelLimitesZone( DC, TRUE );
    }

    Panel->ManageCurseur = NULL;
    Panel->ForceCloseManageCurseur = NULL;
    pcbframe->SetCurItem( NULL );
}


/**************************************************************/
void WinEDA_BasePcbFrame::DelLimitesZone( wxDC* DC, bool Redraw )
/**************************************************************/
{
    EDGE_ZONE* segment;
    EDGE_ZONE* next;

    if( m_Pcb->m_CurrentLimitZone == NULL )
        return;

    if( !IsOK( this, _( "Delete Current Zone Edges" ) ) )
        return;

    // erase the old zone border, one segment at a time
    for( segment = m_Pcb->m_CurrentLimitZone; segment; segment = next )
    {
        next = segment->Next();

        if( Redraw && DC )
            Trace_DrawSegmentPcb( DrawPanel, DC, segment, GR_XOR );

        delete segment;
    }

    m_Pcb->m_CurrentLimitZone = NULL;

    SetCurItem( NULL );
}


/**
 * Function Begin_Zone
 * either initializes the first segment of a new zone, or adds an
 * intermediate segment.
 */
EDGE_ZONE* WinEDA_PcbFrame::Begin_Zone()
{
    EDGE_ZONE* oldedge;
    EDGE_ZONE* newedge = NULL;

    oldedge = m_Pcb->m_CurrentLimitZone;

    // if first segment
    if( (m_Pcb->m_CurrentLimitZone == NULL )    /* debut reel du trace */
       || (DrawPanel->ManageCurseur == NULL) )  /* reprise d'un trace complementaire */
    {
        newedge = new EDGE_ZONE( m_Pcb );
        newedge->m_Flags = IS_NEW | STARTPOINT | IS_MOVED;
        newedge->m_Start = newedge->m_End = GetScreen()->m_Curseur;
        newedge->SetLayer( GetScreen()->m_Active_Layer );

        // link into list:
        newedge->Pnext = oldedge;

        if( oldedge )
            oldedge->Pback = newedge;

        m_Pcb->m_CurrentLimitZone = newedge;

        DrawPanel->ManageCurseur = Show_Zone_Edge_While_MoveMouse;
        DrawPanel->ForceCloseManageCurseur = Exit_Zones;
    }
    // edge in progress:
    else    /* piste en cours : les coord du point d'arrivee ont ete mises
             *  a jour par la routine Show_Zone_Edge_While_MoveMouse*/
    {
        if( oldedge->m_Start != oldedge->m_End )
        {
            oldedge->m_Flags &= ~(IS_NEW | IS_MOVED);

            newedge = new EDGE_ZONE( oldedge );
            newedge->m_Flags = IS_NEW | IS_MOVED;
            newedge->m_Start = newedge->m_End = oldedge->m_End;
            newedge->SetLayer( GetScreen()->m_Active_Layer );

            // link into list:
            newedge->Pnext = oldedge;
            oldedge->Pback = newedge;
            m_Pcb->m_CurrentLimitZone = newedge;
        }
    }

    return newedge;
}


/*********************************************/
void WinEDA_PcbFrame::End_Zone( wxDC* DC )
/*********************************************/

/*
 *  Routine de fin de trace d'une zone (succession de segments)
 */
{
    EDGE_ZONE* edge;

    if( m_Pcb->m_CurrentLimitZone )
    {
        Begin_Zone();

        /* le dernier point genere est de longueur tj nulle donc inutile. */
        /* il sera raccorde au point de depart */
        edge = m_Pcb->m_CurrentLimitZone;
        edge->m_Flags &= ~(IS_NEW | IS_MOVED);

        while( edge && edge->Next() )
        {
            edge = edge->Next();
            if( edge->m_Flags & STARTPOINT )
                break;

            edge->m_Flags &= ~(IS_NEW | IS_MOVED);
        }

        if( edge )
        {
            edge->m_Flags &= ~(IS_NEW | IS_MOVED);
            m_Pcb->m_CurrentLimitZone->m_End = edge->m_Start;
        }
        Trace_DrawSegmentPcb( DrawPanel, DC, m_Pcb->m_CurrentLimitZone, GR_XOR );
    }

    DrawPanel->ManageCurseur = NULL;
    DrawPanel->ForceCloseManageCurseur = NULL;
}


/******************************************************************************************/
static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase )
/******************************************************************************************/

/* redessin du contour de la piste  lors des deplacements de la souris
 */
{
    EDGE_ZONE*       edge;
    EDGE_ZONE*       currentEdge;
    WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) panel->m_Parent;

    if( pcbframe->m_Pcb->m_CurrentLimitZone == NULL )
        return;

    /* efface ancienne position si elle a ete deja dessinee */
    if( erase )
    {
        edge = pcbframe->m_Pcb->m_CurrentLimitZone;

        // for( ;  edge; edge = edge->Next() )
        {
            Trace_DrawSegmentPcb( panel, DC, edge, GR_XOR );
        }
    }

    /* mise a jour de la couche */
    for( edge = pcbframe->m_Pcb->m_CurrentLimitZone; edge; edge = edge->Next() )
    {
        edge->SetLayer( pcbframe->GetScreen()->m_Active_Layer );
    }

    /* dessin de la nouvelle piste : mise a jour du point d'arrivee */
    currentEdge = pcbframe->m_Pcb->m_CurrentLimitZone;
    if( Zone_45_Only )
    {
        // Calcul de l'extremite de la piste pour orientations permises:
        // horiz,vertical ou 45 degre
        currentEdge->m_End = pcbframe->GetScreen()->m_Curseur;
        Calcule_Coord_Extremite_45( currentEdge->m_Start.x, currentEdge->m_Start.y,
                                    &currentEdge->m_End.x, &currentEdge->m_End.y );
    }
    else    /* ici l'angle d'inclinaison est quelconque */
    {
        currentEdge->m_End = pcbframe->GetScreen()->m_Curseur;
    }

    // for( ; currentEdge;  currentEdge = currentEdge->Next() )
    {
        Trace_DrawSegmentPcb( panel, DC, currentEdge, GR_XOR );
    }
}


/**********************************************/
void WinEDA_PcbFrame::Fill_Zone( wxDC* DC )
/**********************************************/

/** Function Fill_Zone()
 *  Init the zone filling
 *  If a zone edge is found, it is used.
 *  Otherwise the whole board is filled by the zone
 *  The zone edge is a frontier, and can be complex. So non filled zones can be achieved
 *  The zone is put on the active layer
 *  If a net is hightlighted, the zone will be attached to this net
 *  The filling start from a starting point.
 *  If a net is selected, all tracks attached to this net are also starting points
 */
{
    EQUIPOT* pt_equipot;
    wxPoint  ZoneStartFill;
    wxString msg;

    MsgPanel->EraseMsgBox();
    if( m_Pcb->ComputeBoundaryBox() == FALSE )
    {
        DisplayError( this, wxT( "Board is empty!" ), 10 );
        return;
    }

    if( m_Parent && m_Parent->m_EDA_Config )
    {
        s_NetSortingOpt = m_Parent->m_EDA_Config->Read( ZONE_NET_SORT_OPTION_KEY, (long) BOARD::PAD_CNT_SORT );
	}
	int NetSortingOptImg = s_NetSortingOpt;
    DrawPanel->m_IgnoreMouseEvents = TRUE;
    WinEDA_ZoneFrame* frame = new WinEDA_ZoneFrame( this );

    int abrd = frame->ShowModal();
    frame->Destroy();
    DrawPanel->MouseToCursorSchema();
    DrawPanel->m_IgnoreMouseEvents = FALSE;

    if( (NetSortingOptImg != s_NetSortingOpt ) && m_Parent && m_Parent->m_EDA_Config )
    {
        m_Parent->m_EDA_Config->Write( ZONE_NET_SORT_OPTION_KEY, (long) s_NetSortingOpt );
	}	

    if( abrd )
        return;

    // set all the EDGE_ZONEs to the currently active layer and redraw them
    // on that layer.
    GetScreen()->m_Active_Layer = s_Zone_Layer;
    EDGE_ZONE* PtLim = m_Pcb->m_CurrentLimitZone;
    for( ; PtLim != NULL; PtLim = PtLim->Next() )
    {
        Trace_DrawSegmentPcb( DrawPanel, DC, PtLim, GR_XOR );
        PtLim->SetLayer( s_Zone_Layer );
        Trace_DrawSegmentPcb( DrawPanel, DC, PtLim, GR_XOR );
    }

    /* Show the NetName */
    if( (g_HightLigth_NetCode > 0) && (g_HightLigth_NetCode != s_NetcodeSelection) )
    {
        Hight_Light( DC );
        g_HightLigth_NetCode = s_NetcodeSelection;
        Hight_Light( DC );
    }
    g_HightLigth_NetCode = s_NetcodeSelection;

    if( g_HightLigth_NetCode > 0 )
    {
        pt_equipot = m_Pcb->FindNet( g_HightLigth_NetCode );
        if( pt_equipot == NULL )
        {
            if( g_HightLigth_NetCode > 0 )
                DisplayError( this, wxT( "Unable to find Net name" ) );
        }
        else
            msg = pt_equipot->m_Netname;
    }
    else
        msg = _( "No Net" );

    Affiche_1_Parametre( this, 22, _( "NetName" ), msg, RED );

    Build_Zone( this, DC, g_HightLigth_NetCode, Zone_Exclude_Pads, s_Zone_Create_Thermal_Relief );

    GetScreen()->SetModify();
}
