/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2007, 2008 Lubo Racko <developer@lura.sk>
 * Copyright (C) 2007, 2008, 2012-2013 Alexander Lunev <al.lunev@yahoo.com>
 * Copyright (C) 2012-2021 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
 */

#include <pcad/pcb.h>

#include <pcad/pcb_keepout.h>
#include <pcad/pcb_footprint.h>
#include <pcad/pcb_net.h>
#include <pcad/pcb_pad.h>
#include <pcad/pcb_text.h>
#include <pcad/pcb_via.h>
#include <pcad/s_expr_loader.h>

#include <board.h>
#include <common.h>
#include <xnode.h>

#include <wx/string.h>

namespace PCAD2KICAD {


PCB_LAYER_ID PCB::GetKiCadLayer( int aPCadLayer ) const
{
    auto it = m_LayersMap.find( aPCadLayer );

    if( it == m_LayersMap.end() )
        THROW_IO_ERROR( wxString::Format( _( "Unknown PCad layer %u" ), unsigned( aPCadLayer ) ) );

    return it->second.KiCadLayer;
}


LAYER_TYPE_T PCB::GetLayerType( int aPCadLayer ) const
{
    auto it = m_LayersMap.find( aPCadLayer );

    if( it == m_LayersMap.end() )
        THROW_IO_ERROR( wxString::Format( _( "Unknown PCad layer %u" ), unsigned( aPCadLayer ) ) );

    return it->second.layerType;
}


wxString PCB::GetLayerNetNameRef( int aPCadLayer ) const
{
    auto it = m_LayersMap.find( aPCadLayer );

    if( it == m_LayersMap.end() )
        THROW_IO_ERROR( wxString::Format( _( "Unknown PCad layer %u" ), unsigned( aPCadLayer ) ) );

    return it->second.netNameRef;
}


PCB::PCB( BOARD* aBoard ) :
        PCB_FOOTPRINT( this, aBoard )
{
    m_DefaultMeasurementUnit = wxT( "mil" );

    for( size_t i = 0; i < 8; ++i )
    {
        TLAYER layer;
        layer.KiCadLayer = F_Mask; // default
        layer.layerType  = LAYER_TYPE_NONSIGNAL; // default
        layer.netNameRef = wxT( "" ); // default

        m_LayersMap.insert( std::make_pair( i, layer ) );
    }

    m_SizeX = 0;
    m_SizeY = 0;

    m_LayersMap[1].KiCadLayer = F_Cu;
    m_LayersMap[1].layerType  = LAYER_TYPE_SIGNAL;

    m_LayersMap[2].KiCadLayer = B_Cu;
    m_LayersMap[2].layerType  = LAYER_TYPE_SIGNAL;

    m_LayersMap[3].KiCadLayer  = Eco2_User;
    m_LayersMap[6].KiCadLayer  = F_SilkS;
    m_LayersMap[7].KiCadLayer  = B_SilkS;
}


PCB::~PCB()
{
    int i;

    for( i = 0; i < (int) m_PcbComponents.GetCount(); i++ )
    {
        delete m_PcbComponents[i];
    }

    for( i = 0; i < (int) m_PcbNetlist.GetCount(); i++ )
    {
        delete m_PcbNetlist[i];
    }
}


int PCB::GetNetCode( const wxString& aNetName ) const
{
    const PCB_NET* net;

    for( int i = 0; i < (int) m_PcbNetlist.GetCount(); i++ )
    {
        net = m_PcbNetlist[i];

        if( net->m_Name == aNetName )
        {
            return net->m_NetCode;
        }
    }

    return 0;
}

XNODE* PCB::FindCompDefName( XNODE* aNode, const wxString& aName ) const
{
    XNODE*      result = nullptr, * lNode;
    wxString    propValue;

    lNode = FindNode( aNode, wxT( "compDef" ) );

    while( lNode )
    {
        if( lNode->GetName().IsSameAs( wxT( "compDef" ), false ) )
        {
            lNode->GetAttribute( wxT( "Name" ), &propValue );

            if( propValue == aName )
            {
                result  = lNode;
                lNode   = nullptr;
            }
        }

        if( lNode )
            lNode = lNode->GetNext();
    }

    return result;
}


void PCB::SetTextProperty( XNODE* aNode, TTEXTVALUE* aTextValue, const wxString& aPatGraphRefName,
                           const wxString& aXmlName, const wxString& aActualConversion )
{
    XNODE*      tNode, * t1Node;
    wxString    n, nnew, pn, propValue, str;

    // aNode is pattern now
    tNode   = aNode;
    t1Node  = aNode;
    n = aXmlName;

    // new file format version
    if( FindNode( tNode, wxT( "patternGraphicsNameRef" ) ) )
    {
        FindNode( tNode,
                  wxT( "patternGraphicsNameRef" ) )->GetAttribute( wxT( "Name" ),
                                                                   &pn );
        pn.Trim( false );
        pn.Trim( true );
        tNode = FindNode( tNode, wxT( "patternGraphicsRef" ) );

        while( tNode )
        {
            if( tNode->GetName().IsSameAs( wxT( "patternGraphicsRef" ), false ) )
            {
                if( FindNode( tNode, wxT( "patternGraphicsNameRef" ) ) )
                {
                    FindNode( tNode,
                              wxT( "patternGraphicsNameRef" ) )->GetAttribute( wxT( "Name" ),
                                                                               &propValue );

                    if( propValue == pn )
                    {
                        t1Node  = tNode; // find correct section with same name.
                        str     = aTextValue->text;
                        str.Trim( false );
                        str.Trim( true );
                        nnew    = n; // new file version
                        n       = n + wxT( ' ' ) + str; // old file version
                        tNode   = nullptr;
                    }
                }
            }

            if( tNode )
                tNode = tNode->GetNext();
        }
    }

    // old version and compatible for both from this point
    tNode = FindNode( t1Node, wxT( "attr" ) );

    while( tNode )
    {
        tNode->GetAttribute( wxT( "Name" ), &propValue );
        propValue.Trim( false );
        propValue.Trim( true );

        if( propValue == n || propValue == nnew )
            break;

        tNode = tNode->GetNext();
    }

    if( tNode )
        SetTextParameters( tNode, aTextValue, m_DefaultMeasurementUnit, aActualConversion );
}


void PCB::DoPCBComponents( XNODE* aNode, wxXmlDocument* aXmlDoc, const wxString& aActualConversion,
                           wxStatusBar* aStatusBar )
{
    XNODE*         lNode, * tNode, * mNode;
    PCB_FOOTPRINT* fp;
    PCB_PAD*       pad;
    PCB_VIA*       via;
    PCB_KEEPOUT*   keepOut;
    wxString       cn, str, propValue;

    lNode = aNode->GetChildren();

    while( lNode )
    {
        fp = nullptr;

        if( lNode->GetName().IsSameAs( wxT( "pattern" ), false ) )
        {
            FindNode( lNode, wxT( "patternRef" ) )->GetAttribute( wxT( "Name" ),
                                                                  &cn );
            cn      = ValidateName( cn );
            tNode   = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "library" ) );

            if( tNode && cn.Len() > 0 )
            {
                tNode = FindModulePatternDefName( tNode, cn );

                if( tNode )
                {
                    fp = new PCB_FOOTPRINT( this, m_board );

                    mNode = FindNode( lNode, wxT( "patternGraphicsNameRef" ) );

                    if( mNode )
                        mNode->GetAttribute( wxT( "Name" ), &fp->m_patGraphRefName );

                    fp->Parse( tNode, aStatusBar, m_DefaultMeasurementUnit, aActualConversion );
                }
            }

            if( fp )
            {
                fp->m_compRef = cn;    // default - in new version of file it is updated later....
                tNode = FindNode( lNode, wxT( "refDesRef" ) );

                if( tNode )
                {
                    tNode->GetAttribute( wxT( "Name" ), &fp->m_name.text );
                    SetTextProperty( lNode, &fp->m_name, fp->m_patGraphRefName, wxT( "RefDes" ),
                                     aActualConversion );
                    SetTextProperty( lNode, &fp->m_Value, fp->m_patGraphRefName, wxT( "Value" ),
                                     aActualConversion );
                }

                tNode = FindNode( lNode, wxT( "pt" ) );

                if( tNode )
                {
                    SetPosition( tNode->GetNodeContent(), m_DefaultMeasurementUnit,
                                 &fp->m_positionX, &fp->m_positionY, aActualConversion );
                }

                tNode = FindNode( lNode, wxT( "rotation" ) );

                if( tNode )
                {
                    str = tNode->GetNodeContent();
                    str.Trim( false );
                    fp->m_rotation = EDA_ANGLE( StrToInt1Units( str ), TENTHS_OF_A_DEGREE_T );
                }

                str = FindNodeGetContent( lNode, wxT( "isFlipped" ) );

                if( str.IsSameAs( wxT( "True" ), false ) )
                    fp->m_Mirror = 1;

                tNode = aNode;

                while( tNode->GetName() != wxT( "www.lura.sk" ) )
                    tNode = tNode->GetParent();

                tNode = FindNode( tNode, wxT( "netlist" ) );

                if( tNode )
                {
                    tNode = FindNode( tNode, wxT( "compInst" ) );

                    while( tNode )
                    {
                        tNode->GetAttribute( wxT( "Name" ), &propValue );

                        if( propValue == fp->m_name.text )
                        {
                            if( FindNode( tNode, wxT( "compValue" ) ) )
                            {
                                FindNode( tNode,
                                          wxT( "compValue" ) )->GetAttribute( wxT( "Name" ),
                                                                              &fp->m_Value.text );
                                fp->m_Value.text.Trim( false );
                                fp->m_Value.text.Trim( true );
                            }

                            if( FindNode( tNode, wxT( "compRef" ) ) )
                            {
                                FindNode( tNode,
                                          wxT( "compRef" ) )->GetAttribute( wxT( "Name" ),
                                                                            &fp->m_compRef );
                                fp->m_compRef.Trim( false );
                                fp->m_compRef.Trim( true );
                            }

                            tNode = nullptr;
                        }
                        else
                        {
                            tNode = tNode->GetNext();
                        }
                    }
                }

                // map pins
                tNode   = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "library" ) );
                tNode   = FindCompDefName( tNode, fp->m_compRef );

                if( tNode )
                {
                    tNode = FindPinMap( tNode );

                    if( tNode )
                    {
                        mNode = tNode->GetChildren();

                        while( mNode )
                        {
                            if( mNode->GetName().IsSameAs( wxT( "padNum" ), false ) )
                            {
                                str     = mNode->GetNodeContent();
                                mNode   = mNode->GetNext();

                                if( !mNode )
                                    break;

                                mNode->GetAttribute( wxT( "Name" ), &propValue );
                                fp->SetName( str, propValue );
                                mNode = mNode->GetNext();
                            }
                            else
                            {
                                mNode = mNode->GetNext();

                                if( !mNode )
                                    break;

                                mNode = mNode->GetNext();
                            }
                        }
                    }
                }

                m_PcbComponents.Add( fp );
            }
        }
        else if( lNode->GetName().IsSameAs( wxT( "pad" ), false ) )
        {
            pad = new PCB_PAD( this, m_board );
            pad->Parse( lNode, m_DefaultMeasurementUnit, aActualConversion );
            m_PcbComponents.Add( pad );
        }
        else if( lNode->GetName().IsSameAs( wxT( "via" ), false ) )
        {
            via = new PCB_VIA( this, m_board );
            via->Parse( lNode, m_DefaultMeasurementUnit, aActualConversion );
            m_PcbComponents.Add( via );
        }
        else if( lNode->GetName().IsSameAs( wxT( "polyKeepOut" ), false ) )
        {
            keepOut = new PCB_KEEPOUT( m_callbacks, m_board, 0 );

            if( keepOut->Parse( lNode, m_DefaultMeasurementUnit, aActualConversion ) )
                m_PcbComponents.Add( keepOut );
            else
                delete keepOut;
        }

        lNode = lNode->GetNext();
    }
}


void PCB::ConnectPinToNet( const wxString& aCompRef, const wxString& aPinRef,
                           const wxString& aNetName )
{
    PCB_FOOTPRINT* footprint;
    PCB_PAD*       cp;
    int            i, j;

    for( i = 0; i < (int) m_PcbComponents.GetCount(); i++ )
    {
        footprint = (PCB_FOOTPRINT*) m_PcbComponents[i];

        if( footprint->m_objType == wxT( 'M' ) && footprint->m_name.text == aCompRef )
        {
            for( j = 0; j < (int) footprint->m_FootprintItems.GetCount(); j++ )
            {
                if( footprint->m_FootprintItems[j]->m_objType == wxT( 'P' ) )
                {
                    cp = (PCB_PAD*) footprint->m_FootprintItems[j];

                    if( cp->m_name.text == aPinRef )
                        cp->m_net = aNetName;
                }
            }
        }
    }
}


int PCB::FindLayer( const wxString& aLayerName ) const
{
    for( int i = 0; i < (int) m_layersStackup.size(); ++i )
    {
        if( m_layersStackup[i].first == aLayerName )
            return i;
    }

    return -1;
}


void PCB::MapLayer( XNODE* aNode )
{
    wxString     lName, layerType;
    PCB_LAYER_ID KiCadLayer;
    long         num = 0;

    aNode->GetAttribute( wxT( "Name" ), &lName );
    lName = lName.MakeUpper();

    if( lName == wxT( "TOP ASSY" ) )
    {
        KiCadLayer = F_Fab;
    }
    else if( lName == wxT( "TOP SILK" ) )
    {
        KiCadLayer = F_SilkS;
    }
    else if( lName == wxT( "TOP PASTE" ) )
    {
        KiCadLayer = F_Paste;
    }
    else if( lName == wxT( "TOP MASK" ) )
    {
        KiCadLayer = F_Mask;
    }
    else if( lName == wxT( "TOP" ) )
    {
        KiCadLayer = F_Cu;
    }
    else if( lName == wxT( "BOTTOM" ) )
    {
        KiCadLayer = B_Cu;
    }
    else if( lName == wxT( "BOT MASK" ) )
    {
        KiCadLayer = B_Mask;
    }
    else if( lName == wxT( "BOT PASTE" ) )
    {
        KiCadLayer = B_Paste;
    }
    else if( lName == wxT( "BOT SILK" ) )
    {
        KiCadLayer = B_SilkS;
    }
    else if( lName == wxT( "BOT ASSY" ) )
    {
        KiCadLayer = B_Fab;
    }
    else if( lName == wxT( "BOARD" ) )
    {
        KiCadLayer = Edge_Cuts;
    }
    else
    {
        int layernum = FindLayer( lName );

        if( layernum == -1 )
            KiCadLayer = Dwgs_User;    // default
        else
            KiCadLayer = ToLAYER_ID( layernum );
    }

    if( FindNode( aNode, wxT( "layerNum" ) ) )
        FindNode( aNode, wxT( "layerNum" ) )->GetNodeContent().ToLong( &num );

    if( num < 0 )
        THROW_IO_ERROR( wxString::Format( wxT( "layerNum = %ld is out of range" ), num ) );

    TLAYER newlayer;
    newlayer.KiCadLayer = KiCadLayer;

    if( FindNode( aNode, wxT( "layerType" ) ) )
    {
        layerType = FindNode( aNode, wxT( "layerType" ) )->GetNodeContent().Trim( false );

        if( layerType.IsSameAs( wxT( "NonSignal" ), false ) )
            newlayer.layerType = LAYER_TYPE_NONSIGNAL;

        if( layerType.IsSameAs( wxT( "Signal" ), false ) )
            newlayer.layerType = LAYER_TYPE_SIGNAL;

        if( layerType.IsSameAs( wxT( "Plane" ), false ) )
            newlayer.layerType = LAYER_TYPE_PLANE;
    }

    m_LayersMap.insert( std::make_pair( num, newlayer ) );

    if( FindNode( aNode, wxT( "netNameRef" ) ) )
    {
        FindNode( aNode, wxT( "netNameRef" ) )->GetAttribute( wxT( "Name" ),
                                                              &m_LayersMap[(int) num].netNameRef );
    }
}

int PCB::FindOutlinePoint( const VERTICES_ARRAY* aOutline, wxRealPoint aPoint ) const
{
    int i;

    for( i = 0; i < (int) aOutline->GetCount(); i++ )
    {
        if( *((*aOutline)[i]) == aPoint )
            return i;
    }

    return -1;
}


double PCB::GetDistance( const wxRealPoint* aPoint1, const wxRealPoint* aPoint2 ) const
{
    return sqrt(  ( aPoint1->x - aPoint2->x ) * ( aPoint1->x - aPoint2->x ) +
                  ( aPoint1->y - aPoint2->y ) * ( aPoint1->y - aPoint2->y ) );
}

void PCB::GetBoardOutline( wxXmlDocument* aXmlDoc, const wxString& aActualConversion )
{
    XNODE*       iNode, *lNode, *pNode;
    long         PCadLayer = 0;
    int          x, y, i, j, targetInd;
    wxRealPoint* xchgPoint;
    double       minDistance, distance;

    iNode = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "pcbDesign" ) );

    if( iNode )
    {
        // COMPONENTS AND OBJECTS
        iNode = iNode->GetChildren();

        while( iNode )
        {
            // objects
            if( iNode->GetName().IsSameAs( wxT( "layerContents" ), false ) )
            {
                if( FindNode( iNode, wxT( "layerNumRef" ) ) )
                    FindNode( iNode, wxT( "layerNumRef" ) )->GetNodeContent().ToLong( &PCadLayer );

                if( GetKiCadLayer( PCadLayer ) == Edge_Cuts )
                {
                    lNode = iNode->GetChildren();

                    while( lNode )
                    {
                        if( lNode->GetName().IsSameAs( wxT( "line" ), false ) )
                        {
                            pNode = FindNode( lNode, wxT( "pt" ) );

                            if( pNode )
                            {
                                SetPosition( pNode->GetNodeContent(), m_DefaultMeasurementUnit,
                                             &x, &y, aActualConversion );

                                if( FindOutlinePoint( &m_BoardOutline, wxRealPoint( x, y) ) == -1 )
                                    m_BoardOutline.Add( new wxRealPoint( x, y ) );
                            }

                            if( pNode )
                                pNode = pNode->GetNext();

                            if( pNode )
                            {
                                SetPosition( pNode->GetNodeContent(), m_DefaultMeasurementUnit,
                                             &x, &y, aActualConversion );

                                if( FindOutlinePoint( &m_BoardOutline, wxRealPoint( x, y) ) == -1 )
                                    m_BoardOutline.Add( new wxRealPoint( x, y ) );
                            }
                        }

                        lNode = lNode->GetNext();
                    }

                    //m_boardOutline.Sort( cmpFunc );
                    // sort vertices according to the distances between them
                    if( m_BoardOutline.GetCount() > 3 )
                    {
                        for( i = 0; i < (int) m_BoardOutline.GetCount() - 1; i++ )
                        {
                            minDistance = GetDistance( m_BoardOutline[i], m_BoardOutline[ i + 1] );
                            targetInd = i + 1;

                            for( j = i + 2; j < (int) m_BoardOutline.GetCount(); j++ )
                            {
                                distance = GetDistance( m_BoardOutline[i], m_BoardOutline[j] );

                                if( distance < minDistance )
                                {
                                    minDistance = distance;
                                    targetInd = j;
                                }
                            }

                            xchgPoint = m_BoardOutline[ i + 1];
                            m_BoardOutline[ i + 1] = m_BoardOutline[targetInd];
                            m_BoardOutline[targetInd] = xchgPoint;
                        }
                    }

                    break;
                }
            }

            iNode = iNode->GetNext();
        }
    }
}


void PCB::ParseBoard( wxStatusBar* aStatusBar, wxXmlDocument* aXmlDoc,
                      const wxString& aActualConversion )
{
    XNODE*          aNode;//, *aaNode;
    PCB_NET*        net;
    PCB_COMPONENT*  comp;
    PCB_FOOTPRINT*  footprint;
    wxString        compRef, pinRef, layerName, layerType;
    int             i, j, netCode;

    // Default measurement units
    aNode = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "asciiHeader" ) );

    if( aNode )
    {
        aNode = FindNode( aNode, wxT( "fileUnits" ) );

        if( aNode )
        {
            m_DefaultMeasurementUnit = aNode->GetNodeContent().Lower();
            m_DefaultMeasurementUnit.Trim( true );
            m_DefaultMeasurementUnit.Trim( false );
        }
    }

    // Determine layers stackup
    aNode = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "pcbDesign" ) );

    /*if( aNode )
    {
        aNode = FindNode( aNode, wxT( "layersStackup" ) );

        if( aNode )
        {
            aNode = FindNode( aNode, wxT( "layerStackupData" ) );

            while( aNode )
            {
                if( aNode->GetName() == wxT( "layerStackupData" ) )
                {
                    aaNode = FindNode( aNode, wxT( "layerStackupName" ) );

                    if( aaNode ) {
                        aaNode->GetAttribute( wxT( "Name" ), &layerName );
                        layerName = layerName.MakeUpper();
                        m_layersStackup.Add( layerName );
                    }
                }

                aNode = aNode->GetNext();
            }
        }
    }*/

    if( aNode )
    {
        aNode = FindNode( aNode, wxT( "layerDef" ) );

        while( aNode )
        {
            if( aNode->GetName().IsSameAs( wxT( "layerDef" ), false ) )
            {
                if( FindNode( aNode, wxT( "layerType" ) ) )
                {
                    long num = -1;

                    if( FindNode( aNode, wxT( "layerNum" ) ) )
                        FindNode( aNode, wxT( "layerNum" ) )->GetNodeContent().ToLong( &num );

                    layerType = FindNode( aNode, wxT( "layerType" ) )->GetNodeContent().Trim(
                            false );

                    if( num > 0 && ( layerType.IsSameAs( wxT( "Signal" ), false )
                            || layerType.IsSameAs( wxT( "Plane" ), false ) ) )
                    {
                        aNode->GetAttribute( wxT( "Name" ), &layerName );
                        layerName = layerName.MakeUpper();
                        m_layersStackup.emplace_back( layerName, num );

                        if( m_layersStackup.size() > 32 )
                            THROW_IO_ERROR( _( "KiCad only supports 32 signal layers." ) );
                    }
                }
            }

            aNode = aNode->GetNext();
        }

        // Ensure that the layers are properly mapped to their order with the bottom
        // copper (layer 2 in PCAD) at the end
        std::sort( m_layersStackup.begin(), m_layersStackup.end(),
        [&]( const std::pair<wxString, long>& a, const std::pair<wxString, long>& b ) {
            long lhs = a.second == 2 ? std::numeric_limits<long>::max() : a.second;
            long rhs = b.second == 2 ? std::numeric_limits<long>::max() : b.second;

            return lhs < rhs;
        } );
    }

    // Layers mapping
    aNode = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "pcbDesign" ) );

    if( aNode )
    {
        aNode = FindNode( aNode, wxT( "layerDef" ) );

        while( aNode )
        {
            if( aNode->GetName().IsSameAs( wxT( "layerDef" ), false ) )
                MapLayer( aNode );

            aNode = aNode->GetNext();
        }
    }

    GetBoardOutline( aXmlDoc, aActualConversion );

    // NETLIST
    // aStatusBar->SetStatusText( wxT( "Loading NETLIST " ) );

    aNode = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "netlist" ) );

    if( aNode )
    {
        aNode = FindNode( aNode, wxT( "net" ) );

        netCode = 1;

        while( aNode )
        {
            net = new PCB_NET( netCode++ );
            net->Parse( aNode );
            m_PcbNetlist.Add( net );

            aNode = aNode->GetNext();
        }
    }

    // BOARD FILE
    // aStatusBar->SetStatusText( wxT( "Loading BOARD DEFINITION " ) );

    aNode = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "pcbDesign" ) );

    if( aNode )
    {
        // COMPONENTS AND OBJECTS
        aNode = aNode->GetChildren();

        while( aNode )
        {
            // Components/footprints
            if( aNode->GetName().IsSameAs( wxT( "multiLayer" ), false ) )
                DoPCBComponents( aNode, aXmlDoc, aActualConversion, aStatusBar );

            // objects
            if( aNode->GetName().IsSameAs( wxT( "layerContents" ), false ) )
                DoLayerContentsObjects( aNode, nullptr, &m_PcbComponents, aStatusBar,
                                        m_DefaultMeasurementUnit, aActualConversion );

            aNode = aNode->GetNext();
        }

        // POSTPROCESS -- SET NETLIST REFERENCES
        // aStatusBar->SetStatusText( wxT( "Processing NETLIST " ) );

        for( i = 0; i < (int) m_PcbNetlist.GetCount(); i++ )
        {
            net = m_PcbNetlist[i];

            for( j = 0; j < (int) net->m_NetNodes.GetCount(); j++ )
            {
                compRef = net->m_NetNodes[j]->m_CompRef;
                compRef.Trim( false );
                compRef.Trim( true );
                pinRef = net->m_NetNodes[j]->m_PinRef;
                pinRef.Trim( false );
                pinRef.Trim( true );
                ConnectPinToNet( compRef, pinRef, net->m_Name );
            }
        }

        // POSTPROCESS -- FLIP COMPONENTS
        for( i = 0; i < (int) m_PcbComponents.GetCount(); i++ )
        {
            if( m_PcbComponents[i]->m_objType == wxT( 'M' ) )
                ( (PCB_FOOTPRINT*) m_PcbComponents[i] )->Flip();
        }

        // POSTPROCESS -- SET/OPTIMIZE NEW PCB POSITION
        // aStatusBar->SetStatusText( wxT( "Optimizing BOARD POSITION " ) );

        m_SizeX = 10000000;
        m_SizeY = 0;

        for( i = 0; i < (int) m_PcbComponents.GetCount(); i++ )
        {
            comp = m_PcbComponents[i];

            if( comp->m_positionY < m_SizeY )
                m_SizeY = comp->m_positionY; // max Y

            if( comp->m_positionX < m_SizeX && comp->m_positionX > 0 )
                m_SizeX = comp->m_positionX; // Min X
        }

        m_SizeY -= 10000;
        m_SizeX -= 10000;
        // aStatusBar->SetStatusText( wxT( " POSITIONING POSTPROCESS " ) );

        for( i = 0; i < (int) m_PcbComponents.GetCount(); i++ )
            m_PcbComponents[i]->SetPosOffset( -m_SizeX, -m_SizeY );

        m_SizeX = 0;
        m_SizeY = 0;

        for( i = 0; i < (int) m_PcbComponents.GetCount(); i++ )
        {
            comp = m_PcbComponents[i];

            if( comp->m_positionY < m_SizeY )
                m_SizeY = comp->m_positionY; // max Y

            if( comp->m_positionX > m_SizeX )
                m_SizeX = comp->m_positionX; // Min X
        }

        // SHEET SIZE CALCULATION
        m_SizeY = -m_SizeY;    // it is in absolute units
        m_SizeX += 10000;
        m_SizeY += 10000;

        // A4 is minimum $Descr A4 11700 8267
        if( m_SizeX < 11700 )
            m_SizeX = 11700;

        if( m_SizeY < 8267 )
            m_SizeY = 8267;
    }
    else
    {
        // LIBRARY FILE
        // aStatusBar->SetStatusText( wxT( "Processing LIBRARY FILE " ) );

        aNode = FindNode( (XNODE *)aXmlDoc->GetRoot(), wxT( "library" ) );

        if( aNode )
        {
            aNode = FindNode( aNode, wxT( "compDef" ) );

            while( aNode )
            {
                // aStatusBar->SetStatusText( wxT( "Processing COMPONENTS " ) );

                if( aNode->GetName().IsSameAs( wxT( "compDef" ), false ) )
                {
                    footprint = new PCB_FOOTPRINT( this, m_board );
                    footprint->Parse( aNode, aStatusBar, m_DefaultMeasurementUnit,
                                      aActualConversion );
                    m_PcbComponents.Add( footprint );
                }

                aNode = aNode->GetNext();
            }
        }
    }
}


void PCB::AddToBoard()
{
    int i;
    PCB_NET* net;

    m_board->SetCopperLayerCount( m_layersStackup.size() );

    for( i = 0; i < (int) m_PcbNetlist.GetCount(); i++ )
    {
        net = m_PcbNetlist[i];

        m_board->Add( new NETINFO_ITEM( m_board, net->m_Name, net->m_NetCode ) );
    }

    for( i = 0; i < (int) m_PcbComponents.GetCount(); i++ )
    {
        m_PcbComponents[i]->AddToBoard();
    }
}

} // namespace PCAD2KICAD
