/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2017 CERN
 * Copyright (C) 2017-2019 Kicad Developers, see AUTHORS.txt for contributors.
 *
 * @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
 * @author Maciej Suminski <maciej.suminski@cern.ch>
 * @author Russell Oliver <roliver8143@gmail.com>
 *
 * 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 3
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include <sch_eagle_plugin.h>

#include <kiway.h>
#include <properties.h>

#include <algorithm>
#include <memory>
#include <wx/filename.h>
#include <wx/tokenzr.h>

#include <class_libentry.h>
#include <class_library.h>
#include <eagle_parser.h>
#include <gr_text.h>
#include <lib_arc.h>
#include <lib_circle.h>
#include <lib_id.h>
#include <lib_item.h>
#include <lib_pin.h>
#include <lib_polyline.h>
#include <lib_rectangle.h>
#include <lib_text.h>
#include <project.h>
#include <sch_bus_entry.h>
#include <sch_component.h>
#include <sch_connection.h>
#include <sch_edit_frame.h>
#include <sch_junction.h>
#include <sch_legacy_plugin.h>
#include <sch_marker.h>
#include <sch_screen.h>
#include <sch_sheet.h>
#include <sch_sheet_path.h>
#include <sch_text.h>
#include <symbol_lib_table.h>
#include <template_fieldnames.h>
#include <wildcards_and_files_ext.h>
#include <ws_draw_item.h>


// Eagle schematic axes are aligned with x increasing left to right and Y increasing bottom to top
// Kicad schematic axes are aligned with x increasing left to right and Y increasing top to bottom.

using namespace std;

/**
 * Map of EAGLE pin type values to KiCad pin type values
 */
static const std::map<wxString, ELECTRICAL_PINTYPE> pinDirectionsMap = {
    { "sup",    ELECTRICAL_PINTYPE::PT_POWER_IN },
    { "pas",    ELECTRICAL_PINTYPE::PT_PASSIVE },
    { "out",    ELECTRICAL_PINTYPE::PT_OUTPUT },
    { "in",     ELECTRICAL_PINTYPE::PT_INPUT },
    { "nc",     ELECTRICAL_PINTYPE::PT_NC },
    { "io",     ELECTRICAL_PINTYPE::PT_BIDI },
    { "oc",     ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR },
    { "hiz",    ELECTRICAL_PINTYPE::PT_TRISTATE },
    { "pwr",    ELECTRICAL_PINTYPE::PT_POWER_IN },
};


/**
 * Provides an easy access to the children of an XML node via their names.
 * @param aCurrentNode is a pointer to a wxXmlNode, whose children will be mapped.
 * @param aName the name of the specific child names to be counted.
 * @return number of children with the give node name.
 */
static int countChildren( wxXmlNode* aCurrentNode, const wxString& aName )
{
    // Map node_name -> node_pointer
    int count = 0;

    // Loop through all children counting them if they match the given name
    aCurrentNode = aCurrentNode->GetChildren();

    while( aCurrentNode )
    {
        if( aCurrentNode->GetName() == aName )
            count++;

        // Get next child
        aCurrentNode = aCurrentNode->GetNext();
    }

    return count;
}


///> Computes a bounding box for all items in a schematic sheet
static EDA_RECT getSheetBbox( SCH_SHEET* aSheet )
{
    EDA_RECT bbox;

    for( auto item : aSheet->GetScreen()->Items() )
        bbox.Merge( item->GetBoundingBox() );

    return bbox;
}


///> Extracts the net name part from a pin name (e.g. return 'GND' for pin named 'GND@2')
static inline wxString extractNetName( const wxString& aPinName )
{
    return aPinName.BeforeFirst( '@' );
}


wxString SCH_EAGLE_PLUGIN::getLibName()
{
    if( m_libName.IsEmpty() )
    {
        // Try to come up with a meaningful name
        m_libName = m_kiway->Prj().GetProjectName();

        if( m_libName.IsEmpty() )
        {
            wxFileName fn( m_rootSheet->GetFileName() );
            m_libName = fn.GetName();
        }

        if( m_libName.IsEmpty() )
            m_libName = "noname";

        m_libName += "-eagle-import";
        m_libName = LIB_ID::FixIllegalChars( m_libName, LIB_ID::ID_SCH, true );
    }

    return m_libName;
}


wxFileName SCH_EAGLE_PLUGIN::getLibFileName()
{
    wxFileName fn( m_kiway->Prj().GetProjectPath(), getLibName(), SchematicLibraryFileExtension );

    return fn;
}


void SCH_EAGLE_PLUGIN::loadLayerDefs( wxXmlNode* aLayers )
{
    std::vector<ELAYER> eagleLayers;

    // Get the first layer and iterate
    wxXmlNode* layerNode = aLayers->GetChildren();

    while( layerNode )
    {
        ELAYER elayer( layerNode );
        eagleLayers.push_back( elayer );

        layerNode = layerNode->GetNext();
    }

    // match layers based on their names
    for( const auto& elayer : eagleLayers )
    {
        /**
         * Layers in Kicad schematics are not actually layers, but abstract groups mainly used to
         * decide item colours.
         *
         * <layers>
         *     <layer number="90" name="Modules" color="5" fill="1" visible="yes" active="yes"/>
         *     <layer number="91" name="Nets" color="2" fill="1" visible="yes" active="yes"/>
         *     <layer number="92" name="Busses" color="1" fill="1" visible="yes" active="yes"/>
         *     <layer number="93" name="Pins" color="2" fill="1" visible="no" active="yes"/>
         *     <layer number="94" name="Symbols" color="4" fill="1" visible="yes" active="yes"/>
         *     <layer number="95" name="Names" color="7" fill="1" visible="yes" active="yes"/>
         *     <layer number="96" name="Values" color="7" fill="1" visible="yes" active="yes"/>
         *     <layer number="97" name="Info" color="7" fill="1" visible="yes" active="yes"/>
         *     <layer number="98" name="Guide" color="6" fill="1" visible="yes" active="yes"/>
         * </layers>
         */


        if( elayer.name == "Nets" )
        {
            m_layerMap[elayer.number] = LAYER_WIRE;
        }
        else if( elayer.name == "Info" || elayer.name == "Guide" )
        {
            m_layerMap[elayer.number] = LAYER_NOTES;
        }
        else if( elayer.name == "Busses" )
        {
            m_layerMap[elayer.number] = LAYER_BUS;
        }
    }
}


SCH_LAYER_ID SCH_EAGLE_PLUGIN::kiCadLayer( int aEagleLayer )
{
    auto it = m_layerMap.find( aEagleLayer );
    return it == m_layerMap.end() ? LAYER_NOTES : it->second;
}


// Return the kicad component orientation based on eagle rotation degrees.
static COMPONENT_ORIENTATION_T kiCadComponentRotation( float eagleDegrees )
{
    int roti = int( eagleDegrees );

    switch( roti )
    {
    default:
        wxASSERT_MSG( false, wxString::Format( "Unhandled orientation (%d degrees)", roti ) );

    case 0:
        return CMP_ORIENT_0;

    case 90:
        return CMP_ORIENT_90;

    case 180:
        return CMP_ORIENT_180;

    case 270:
        return CMP_ORIENT_270;
    }

    return CMP_ORIENT_0;
}


// Calculate text alignment based on the given Eagle text alignment parameters.
static void eagleToKicadAlignment( EDA_TEXT* aText, int aEagleAlignment, int aRelDegress,
        bool aMirror, bool aSpin, int aAbsDegress )
{
    int align = aEagleAlignment;

    if( aRelDegress == 90 )
    {
        aText->SetTextAngle( 900 );
    }
    else if( aRelDegress == 180 )
        align = -align;
    else if( aRelDegress == 270 )
    {
        aText->SetTextAngle( 900 );
        align = -align;
    }

    if( aMirror == true )
    {
        if( aAbsDegress == 90 || aAbsDegress == 270 )
        {
            if( align == ETEXT::BOTTOM_RIGHT )
                align = ETEXT::TOP_RIGHT;
            else if( align == ETEXT::BOTTOM_LEFT )
                align = ETEXT::TOP_LEFT;
            else if( align == ETEXT::TOP_LEFT )
                align = ETEXT::BOTTOM_LEFT;
            else if( align == ETEXT::TOP_RIGHT )
                align = ETEXT::BOTTOM_RIGHT;
        }
        else if( aAbsDegress == 0 || aAbsDegress == 180 )
        {
            if( align == ETEXT::BOTTOM_RIGHT )
                align = ETEXT::BOTTOM_LEFT;
            else if( align == ETEXT::BOTTOM_LEFT )
                align = ETEXT::BOTTOM_RIGHT;
            else if( align == ETEXT::TOP_LEFT )
                align = ETEXT::TOP_RIGHT;
            else if( align == ETEXT::TOP_RIGHT )
                align = ETEXT::TOP_LEFT;
            else if( align == ETEXT::CENTER_LEFT )
                align = ETEXT::CENTER_RIGHT;
            else if( align == ETEXT::CENTER_RIGHT )
                align = ETEXT::CENTER_LEFT;
        }
    }

    switch( align )
    {
    case ETEXT::CENTER:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
        break;

    case ETEXT::CENTER_LEFT:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
        break;

    case ETEXT::CENTER_RIGHT:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
        break;

    case ETEXT::TOP_CENTER:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
        break;

    case ETEXT::TOP_LEFT:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
        break;

    case ETEXT::TOP_RIGHT:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
        break;

    case ETEXT::BOTTOM_CENTER:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
        break;

    case ETEXT::BOTTOM_LEFT:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
        break;

    case ETEXT::BOTTOM_RIGHT:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
        break;

    default:
        aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
        aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
        break;
    }
}


SCH_EAGLE_PLUGIN::SCH_EAGLE_PLUGIN()
{
    m_kiway        = nullptr;
    m_rootSheet    = nullptr;
    m_currentSheet = nullptr;
}


SCH_EAGLE_PLUGIN::~SCH_EAGLE_PLUGIN()
{
}


const wxString SCH_EAGLE_PLUGIN::GetName() const
{
    return "EAGLE";
}


const wxString SCH_EAGLE_PLUGIN::GetFileExtension() const
{
    return "sch";
}


const wxString SCH_EAGLE_PLUGIN::GetLibraryFileExtension() const
{
    return "lbr";
}


int SCH_EAGLE_PLUGIN::GetModifyHash() const
{
    return 0;
}


SCH_SHEET* SCH_EAGLE_PLUGIN::Load( const wxString& aFileName, KIWAY* aKiway, SCH_SHEET* aAppendToMe,
        const PROPERTIES* aProperties )
{
    wxASSERT( !aFileName || aKiway != NULL );
    LOCALE_IO toggle; // toggles on, then off, the C locale.

    // Load the document
    wxXmlDocument xmlDocument;

    m_filename = aFileName;
    m_kiway    = aKiway;

    if( !xmlDocument.Load( m_filename.GetFullPath() ) )
        THROW_IO_ERROR(
                wxString::Format( _( "Unable to read file \"%s\"" ), m_filename.GetFullPath() ) );

    // Delete on exception, if I own m_rootSheet, according to aAppendToMe
    unique_ptr<SCH_SHEET> deleter( aAppendToMe ? nullptr : m_rootSheet );

    if( aAppendToMe )
    {
        m_rootSheet = aAppendToMe->GetRootSheet();
    }
    else
    {
        m_rootSheet = new SCH_SHEET();
        m_rootSheet->SetFileName( aFileName );
    }

    if( !m_rootSheet->GetScreen() )
    {
        SCH_SCREEN* screen = new SCH_SCREEN( aKiway );
        screen->SetFileName( aFileName );
        m_rootSheet->SetScreen( screen );
    }

    SYMBOL_LIB_TABLE* libTable = m_kiway->Prj().SchSymbolLibTable();

    wxCHECK_MSG( libTable, NULL, "Could not load symbol lib table." );

    m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
    m_properties                                        = std::make_unique<PROPERTIES>();
    ( *m_properties )[SCH_LEGACY_PLUGIN::PropBuffering] = "";

    /// @note No check is being done here to see if the existing symbol library exists so this
    ///       will overwrite the existing one.
    if( !libTable->HasLibrary( getLibName() ) )
    {
        // Create a new empty symbol library.
        m_pi->CreateSymbolLib( getLibFileName().GetFullPath() );
        wxString libTableUri = "${KIPRJMOD}/" + getLibFileName().GetFullName();

        // Add the new library to the project symbol library table.
        libTable->InsertRow(
                new SYMBOL_LIB_TABLE_ROW( getLibName(), libTableUri, wxString( "Legacy" ) ) );

        // Save project symbol library table.
        wxFileName fn(
                m_kiway->Prj().GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );

        // So output formatter goes out of scope and closes the file before reloading.
        {
            FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
            libTable->Format( &formatter, 0 );
        }

        // Relaod the symbol library table.
        m_kiway->Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL );
        m_kiway->Prj().SchSymbolLibTable();
    }

    // Retrieve the root as current node
    wxXmlNode* currentNode = xmlDocument.GetRoot();

    // If the attribute is found, store the Eagle version;
    // otherwise, store the dummy "0.0" version.
    m_version = currentNode->GetAttribute( "version", "0.0" );

    // Map all children into a readable dictionary
    NODE_MAP children = MapChildren( currentNode );

    // Load drawing
    loadDrawing( children["drawing"] );

    m_pi->SaveLibrary( getLibFileName().GetFullPath() );

    return m_rootSheet;
}


void SCH_EAGLE_PLUGIN::loadDrawing( wxXmlNode* aDrawingNode )
{
    // Map all children into a readable dictionary
    NODE_MAP drawingChildren = MapChildren( aDrawingNode );

    // Board nodes should not appear in .sch files
    // wxXmlNode* board = drawingChildren["board"]

    // wxXmlNode* grid = drawingChildren["grid"]

    auto layers = drawingChildren["layers"];

    if( layers )
        loadLayerDefs( layers );

    // wxXmlNode* library = drawingChildren["library"]

    // wxXmlNode* settings = drawingChildren["settings"]


    // Load schematic
    auto schematic = drawingChildren["schematic"];
    if( schematic )
        loadSchematic( schematic );
}


void SCH_EAGLE_PLUGIN::countNets( wxXmlNode* aSchematicNode )
{
    // Map all children into a readable dictionary
    NODE_MAP schematicChildren = MapChildren( aSchematicNode );
    // Loop through all the sheets

    wxXmlNode* sheetNode = getChildrenNodes( schematicChildren, "sheets" );

    while( sheetNode )
    {
        NODE_MAP sheetChildren = MapChildren( sheetNode );
        // Loop through all nets
        // From the DTD: "Net is an electrical connection in a schematic."
        wxXmlNode* netNode = getChildrenNodes( sheetChildren, "nets" );

        while( netNode )
        {
            wxString netName = netNode->GetAttribute( "name" );

            if( m_netCounts.count( netName ) )
                m_netCounts[netName] = m_netCounts[netName] + 1;
            else
                m_netCounts[netName] = 1;

            // Get next net
            netNode = netNode->GetNext();
        }

        sheetNode = sheetNode->GetNext();
    }
}


void SCH_EAGLE_PLUGIN::loadSchematic( wxXmlNode* aSchematicNode )
{
    // Map all children into a readable dictionary
    NODE_MAP schematicChildren = MapChildren( aSchematicNode );
    auto     partNode          = getChildrenNodes( schematicChildren, "parts" );
    auto     libraryNode       = getChildrenNodes( schematicChildren, "libraries" );
    auto     sheetNode         = getChildrenNodes( schematicChildren, "sheets" );

    if( !partNode || !libraryNode || !sheetNode )
        return;

    while( partNode )
    {
        std::unique_ptr<EPART> epart( new EPART( partNode ) );

        // N.B. Eagle parts are case-insensitive in matching but we keep the display case
        m_partlist[epart->name.Upper()] = std::move( epart );
        partNode                        = partNode->GetNext();
    }

    // Loop through all the libraries
    while( libraryNode )
    {
        // Read the library name
        wxString libName = libraryNode->GetAttribute( "name" );

        EAGLE_LIBRARY* elib = &m_eagleLibs[libName];
        elib->name          = libName;

        loadLibrary( libraryNode, &m_eagleLibs[libName] );

        libraryNode = libraryNode->GetNext();
    }

    m_pi->SaveLibrary( getLibFileName().GetFullPath() );

    // find all nets and count how many sheets they appear on.
    // local labels will be used for nets found only on that sheet.
    countNets( aSchematicNode );

    // Loop through all the sheets
    int sheet_count = countChildren( sheetNode->GetParent(), "sheet" );

    // If eagle schematic has multiple sheets then create corresponding subsheets on the root sheet
    if( sheet_count > 1 )
    {
        int x, y, i;
        i = 1;
        x = 1;
        y = 1;

        while( sheetNode )
        {
            wxPoint                    pos = wxPoint( x * Mils2iu( 1000 ), y * Mils2iu( 1000 ) );
            std::unique_ptr<SCH_SHEET> sheet( new SCH_SHEET( pos ) );
            SCH_SCREEN*                screen = new SCH_SCREEN( m_kiway );

            sheet->SetParent( m_rootSheet->GetScreen() );
            sheet->SetScreen( screen );
            sheet->GetScreen()->SetFileName( sheet->GetFileName() );

            m_currentSheet = sheet.get();
            loadSheet( sheetNode, i );
            m_rootSheet->GetScreen()->Append( sheet.release() );

            sheetNode = sheetNode->GetNext();
            x += 2;

            if( x > 10 ) // start next row
            {
                x = 1;
                y += 2;
            }

            i++;
        }
    }
    else
    {
        while( sheetNode )
        {
            m_currentSheet = m_rootSheet;
            loadSheet( sheetNode, 0 );
            sheetNode = sheetNode->GetNext();
        }
    }


    // Handle the missing component units that need to be instantiated
    // to create the missing implicit connections

    // Calculate the already placed items bounding box and the page size to determine
    // placement for the new components
    wxSize   pageSizeIU = m_rootSheet->GetScreen()->GetPageSettings().GetSizeIU();
    EDA_RECT sheetBbox  = getSheetBbox( m_rootSheet );
    wxPoint  newCmpPosition( sheetBbox.GetLeft(), sheetBbox.GetBottom() );
    int      maxY = sheetBbox.GetY();

    SCH_SHEET_PATH sheetpath;
    m_rootSheet->LocatePathOfScreen( m_rootSheet->GetScreen(), &sheetpath );

    for( auto& cmp : m_missingCmps )
    {
        const SCH_COMPONENT* origCmp = cmp.second.cmp;

        for( auto unitEntry : cmp.second.units )
        {
            if( unitEntry.second == false )
                continue; // unit has been already processed

            // Instantiate the missing component unit
            int                            unit      = unitEntry.first;
            const wxString                 reference = origCmp->GetField( REFERENCE )->GetText();
            std::unique_ptr<SCH_COMPONENT> component( (SCH_COMPONENT*) origCmp->Duplicate() );
            component->SetUnitSelection( &sheetpath, unit );
            component->SetUnit( unit );
            component->SetOrientation( 0 );
            component->AddHierarchicalReference( sheetpath.Path(), reference, unit );

            // Calculate the placement position
            EDA_RECT cmpBbox = component->GetBoundingBox();
            int      posY    = newCmpPosition.y + cmpBbox.GetHeight();
            component->SetPosition( wxPoint( newCmpPosition.x, posY ) );
            newCmpPosition.x += cmpBbox.GetWidth();
            maxY = std::max( maxY, posY );

            if( newCmpPosition.x >= pageSizeIU.GetWidth() )            // reached the page boundary?
                newCmpPosition = wxPoint( sheetBbox.GetLeft(), maxY ); // then start a new row

            // Add the global net labels to recreate the implicit connections
            addImplicitConnections( component.get(), m_rootSheet->GetScreen(), false );
            m_rootSheet->GetScreen()->Append( component.release() );
        }
    }

    m_missingCmps.clear();
}


void SCH_EAGLE_PLUGIN::loadSheet( wxXmlNode* aSheetNode, int aSheetIndex )
{
    // Map all children into a readable dictionary
    NODE_MAP sheetChildren = MapChildren( aSheetNode );

    // Get description node
    wxXmlNode* descriptionNode = getChildrenNodes( sheetChildren, "description" );

    wxString    des;
    std::string filename;
    SCH_FIELD&  sheetNameField = m_currentSheet->GetFields()[SHEETNAME];
    SCH_FIELD&  filenameField = m_currentSheet->GetFields()[SHEETFILENAME];

    if( descriptionNode )
    {
        des = descriptionNode->GetContent();
        des.Replace( "\n", "_", true );
        sheetNameField.SetText( des );
        filename = des.ToStdString();
    }
    else
    {
        filename = wxString::Format( "%s_%d", m_filename.GetName(), aSheetIndex );
        sheetNameField.SetText( filename );
    }

    ReplaceIllegalFileNameChars( &filename );
    replace( filename.begin(), filename.end(), ' ', '_' );

    wxString fn = wxString( filename + ".sch" );
    filenameField.SetText( fn );
    wxFileName fileName( fn );
    m_currentSheet->GetScreen()->SetFileName( fileName.GetFullPath() );

    // Loop through all buses
    // From the DTD: "Buses receive names which determine which signals they include.
    // A bus is a drawing object. It does not create any electrical connections.
    // These are always created by means of the nets and their names."
    wxXmlNode* busNode = getChildrenNodes( sheetChildren, "busses" );

    while( busNode )
    {
        // Get the bus name
        wxString busName = translateEagleBusName( busNode->GetAttribute( "name" ) );

        // Load segments of this bus
        loadSegments( busNode, busName, wxString() );

        // Get next bus
        busNode = busNode->GetNext();
    }

    // Loop through all nets
    // From the DTD: "Net is an electrical connection in a schematic."
    wxXmlNode* netNode = getChildrenNodes( sheetChildren, "nets" );

    while( netNode )
    {
        // Get the net name and class
        wxString netName  = netNode->GetAttribute( "name" );
        wxString netClass = netNode->GetAttribute( "class" );

        // Load segments of this net
        loadSegments( netNode, netName, netClass );

        // Get next net
        netNode = netNode->GetNext();
    }

    adjustNetLabels(); // needs to be called before addBusEntries()
    addBusEntries();

    // Loop through all instances
    wxXmlNode* instanceNode = getChildrenNodes( sheetChildren, "instances" );

    while( instanceNode )
    {
        loadInstance( instanceNode );
        instanceNode = instanceNode->GetNext();
    }

    /*  moduleinst is a design block definition and is an EagleCad 8 feature,
     *
     *  // Loop through all moduleinsts
     *  wxXmlNode* moduleinstNode = getChildrenNodes( sheetChildren, "moduleinsts" );
     *
     *  while( moduleinstNode )
     *  {
     *   loadModuleinst( moduleinstNode );
     *   moduleinstNode = moduleinstNode->GetNext();
     *  }
     */

    wxXmlNode* plainNode = getChildrenNodes( sheetChildren, "plain" );

    while( plainNode )
    {
        wxString nodeName = plainNode->GetName();

        if( nodeName == "text" )
        {
            m_currentSheet->GetScreen()->Append( loadPlainText( plainNode ) );
        }
        else if( nodeName == "wire" )
        {
            m_currentSheet->GetScreen()->Append( loadWire( plainNode ) );
        }

        plainNode = plainNode->GetNext();
    }

    // Calculate the new sheet size.
    EDA_RECT sheetBoundingBox = getSheetBbox( m_currentSheet );
    wxSize   targetSheetSize  = sheetBoundingBox.GetSize();
    targetSheetSize.IncBy( Mils2iu( 1500 ), Mils2iu( 1500 ) );

    // Get current Eeschema sheet size.
    wxSize    pageSizeIU = m_currentSheet->GetScreen()->GetPageSettings().GetSizeIU();
    PAGE_INFO pageInfo   = m_currentSheet->GetScreen()->GetPageSettings();

    // Increase if necessary
    if( pageSizeIU.x < targetSheetSize.x )
        pageInfo.SetWidthMils( Iu2Mils( targetSheetSize.x ) );

    if( pageSizeIU.y < targetSheetSize.y )
        pageInfo.SetHeightMils( Iu2Mils( targetSheetSize.y ) );

    // Set the new sheet size.
    m_currentSheet->GetScreen()->SetPageSettings( pageInfo );

    pageSizeIU = m_currentSheet->GetScreen()->GetPageSettings().GetSizeIU();
    wxPoint sheetcentre( pageSizeIU.x / 2, pageSizeIU.y / 2 );
    wxPoint itemsCentre = sheetBoundingBox.Centre();

    // round the translation to nearest 100mil to place it on the grid.
    wxPoint translation = sheetcentre - itemsCentre;
    translation.x       = translation.x - translation.x % Mils2iu( 100 );
    translation.y       = translation.y - translation.y % Mils2iu( 100 );

    // Add global net labels for the named power input pins in this sheet
    for( auto item : m_currentSheet->GetScreen()->Items().OfType( SCH_COMPONENT_T ) )
        addImplicitConnections(
                static_cast<SCH_COMPONENT*>( item ), m_currentSheet->GetScreen(), true );

    m_connPoints.clear();

    // Translate the items.
    std::vector<SCH_ITEM*> allItems;

    std::copy( m_currentSheet->GetScreen()->Items().begin(),
            m_currentSheet->GetScreen()->Items().end(), std::back_inserter( allItems ) );

    for( auto item : allItems )
    {
        item->SetPosition( item->GetPosition() + translation );
        item->ClearFlags();
        m_currentSheet->GetScreen()->Update( item );

    }
}


void SCH_EAGLE_PLUGIN::loadSegments(
        wxXmlNode* aSegmentsNode, const wxString& netName, const wxString& aNetClass )
{
    // Loop through all segments
    wxXmlNode*  currentSegment = aSegmentsNode->GetChildren();
    SCH_SCREEN* screen         = m_currentSheet->GetScreen();

    int segmentCount = countChildren( aSegmentsNode, "segment" );

    // wxCHECK( screen, [>void<] );
    while( currentSegment )
    {
        bool      labelled = false; // has a label been added to this continously connected segment
        NODE_MAP  segmentChildren = MapChildren( currentSegment );
        SCH_LINE* firstWire       = nullptr;
        m_segments.emplace_back();
        SEG_DESC& segDesc = m_segments.back();

        // Loop through all segment children
        wxXmlNode* segmentAttribute = currentSegment->GetChildren();

        while( segmentAttribute )
        {
            if( segmentAttribute->GetName() == "wire" )
            {
                SCH_LINE* wire = loadWire( segmentAttribute );

                if( !firstWire )
                    firstWire = wire;

                // Test for intersections with other wires
                SEG thisWire( wire->GetStartPoint(), wire->GetEndPoint() );

                for( auto& desc : m_segments )
                {
                    if( !desc.labels.empty() && desc.labels.front()->GetText() == netName )
                        continue; // no point in saving intersections of the same net

                    for( const auto& seg : desc.segs )
                    {
                        auto intersection = thisWire.Intersect( seg, true );

                        if( intersection )
                            m_wireIntersections.push_back( *intersection );
                    }
                }

                segDesc.segs.push_back( thisWire );
                screen->Append( wire );
            }

            segmentAttribute = segmentAttribute->GetNext();
        }

        segmentAttribute = currentSegment->GetChildren();

        while( segmentAttribute )
        {
            wxString nodeName = segmentAttribute->GetName();

            if( nodeName == "junction" )
            {
                screen->Append( loadJunction( segmentAttribute ) );
            }
            else if( nodeName == "label" )
            {
                SCH_TEXT* label = loadLabel( segmentAttribute, netName );
                screen->Append( label );
                wxASSERT( segDesc.labels.empty()
                          || segDesc.labels.front()->GetText() == label->GetText() );
                segDesc.labels.push_back( label );
                labelled = true;
            }
            else if( nodeName == "pinref" )
            {
                segmentAttribute->GetAttribute( "gate" ); // REQUIRED
                segmentAttribute->GetAttribute( "part" ); // REQUIRED
                segmentAttribute->GetAttribute( "pin" );  // REQUIRED
            }
            else if( nodeName == "wire" )
            {
                // already handled;
            }
            else // DEFAULT
            {
                // THROW_IO_ERROR( wxString::Format( _( "XML node \"%s\" unknown" ), nodeName ) );
            }

            // Get next segment attribute
            segmentAttribute = segmentAttribute->GetNext();
        }

        // Add a small label to the net segment if it hasn't been labelled already
        // this preserves the named net feature of Eagle schematics.
        if( !labelled && firstWire )
        {
            std::unique_ptr<SCH_TEXT> label;

            // Add a global label if the net appears on more than one Eagle sheet
            if( m_netCounts[netName.ToStdString()] > 1 )
                label.reset( new SCH_GLOBALLABEL );
            else if( segmentCount > 1 )
                label.reset( new SCH_LABEL );

            if( label )
            {
                label->SetPosition( firstWire->GetStartPoint() );
                label->SetText( escapeName( netName ) );
                label->SetTextSize( wxSize( Mils2iu( 10 ), Mils2iu( 10 ) ) );
                label->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
                screen->Append( label.release() );
            }
        }

        currentSegment = currentSegment->GetNext();
    }
}


SCH_LINE* SCH_EAGLE_PLUGIN::loadWire( wxXmlNode* aWireNode )
{
    std::unique_ptr<SCH_LINE> wire( new SCH_LINE );

    auto ewire = EWIRE( aWireNode );

    wire->SetLayer( kiCadLayer( ewire.layer ) );

    wxPoint begin, end;

    begin.x = ewire.x1.ToSchUnits();
    begin.y = -ewire.y1.ToSchUnits();
    end.x   = ewire.x2.ToSchUnits();
    end.y   = -ewire.y2.ToSchUnits();

    wire->SetStartPoint( begin );
    wire->SetEndPoint( end );

    m_connPoints[begin].emplace( wire.get() );
    m_connPoints[end].emplace( wire.get() );

    return wire.release();
}


SCH_JUNCTION* SCH_EAGLE_PLUGIN::loadJunction( wxXmlNode* aJunction )
{
    std::unique_ptr<SCH_JUNCTION> junction( new SCH_JUNCTION );

    auto    ejunction = EJUNCTION( aJunction );
    wxPoint pos( ejunction.x.ToSchUnits(), -ejunction.y.ToSchUnits() );

    junction->SetPosition( pos );

    return junction.release();
}


SCH_TEXT* SCH_EAGLE_PLUGIN::loadLabel( wxXmlNode* aLabelNode, const wxString& aNetName )
{
    auto    elabel = ELABEL( aLabelNode, aNetName );
    wxPoint elabelpos( elabel.x.ToSchUnits(), -elabel.y.ToSchUnits() );

    // Determine if the label is local or global depending on
    // the number of sheets the net appears in
    bool                      global = m_netCounts[aNetName] > 1;
    std::unique_ptr<SCH_TEXT> label;

    if( global )
        label.reset( new SCH_GLOBALLABEL );
    else
        label.reset( new SCH_LABEL );

    label->SetPosition( elabelpos );
    label->SetText( escapeName( elabel.netname ) );
    label->SetTextSize( wxSize( elabel.size.ToSchUnits(), elabel.size.ToSchUnits() ) );
    label->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );

    if( elabel.rot )
    {
        label->SetLabelSpinStyle( (LABEL_SPIN_STYLE::SPIN) ( KiROUND( elabel.rot->degrees / 90 ) % 4 ) );

        if( elabel.rot->mirror )
        {
            label->SetLabelSpinStyle( label->GetLabelSpinStyle().MirrorY() );
        }
    }

    return label.release();
}


std::pair<VECTOR2I, const SEG*> SCH_EAGLE_PLUGIN::findNearestLinePoint(
        const wxPoint& aPoint, const std::vector<SEG>& aLines ) const
{
    VECTOR2I   nearestPoint;
    const SEG* nearestLine = nullptr;

    float d, mindistance = std::numeric_limits<float>::max();

    // Find the nearest start, middle or end of a line from the list of lines.
    for( const SEG& line : aLines )
    {
        auto testpoint = line.A;
        d = sqrt( abs( ( ( aPoint.x - testpoint.x ) ^ 2 ) + ( ( aPoint.y - testpoint.y ) ^ 2 ) ) );

        if( d < mindistance )
        {
            mindistance  = d;
            nearestPoint = testpoint;
            nearestLine  = &line;
        }

        testpoint = line.Center();
        d = sqrt( abs( ( ( aPoint.x - testpoint.x ) ^ 2 ) + ( ( aPoint.y - testpoint.y ) ^ 2 ) ) );

        if( d < mindistance )
        {
            mindistance  = d;
            nearestPoint = testpoint;
            nearestLine  = &line;
        }

        testpoint = line.B;
        d = sqrt( abs( ( ( aPoint.x - testpoint.x ) ^ 2 ) + ( ( aPoint.y - testpoint.y ) ^ 2 ) ) );

        if( d < mindistance )
        {
            mindistance  = d;
            nearestPoint = testpoint;
            nearestLine  = &line;
        }
    }

    return std::make_pair( nearestPoint, nearestLine );
}


void SCH_EAGLE_PLUGIN::loadInstance( wxXmlNode* aInstanceNode )
{
    auto einstance = EINSTANCE( aInstanceNode );

    SCH_SCREEN* screen = m_currentSheet->GetScreen();

    // Find the part in the list for the sheet.
    // Assign the component its value from the part entry
    // Calculate the unit number from the gate entry of the instance
    // Assign the the LIB_ID from deviceset and device names

    auto part_it = m_partlist.find( einstance.part.Upper() );

    if( part_it == m_partlist.end() )
    {
        wxLogError( _( "Error parsing Eagle file.  "
                       "Could not find \"%s\" instance but it is referenced in the schematic." ),
                einstance.part );

        return;
    }

    EPART* epart = part_it->second.get();

    wxString libraryname = epart->library;
    wxString gatename    = epart->deviceset + epart->device + einstance.gate;
    wxString symbolname  = wxString( epart->deviceset + epart->device );
    symbolname.Replace( "*", "" );
    wxString kisymbolname = fixSymbolName( symbolname );

    int unit = m_eagleLibs[libraryname].GateUnit[gatename];

    wxString       package;
    EAGLE_LIBRARY* elib = &m_eagleLibs[libraryname];

    auto p = elib->package.find( kisymbolname );

    if( p != elib->package.end() )
        package = p->second;

    LIB_PART* part =
            m_pi->LoadSymbol( getLibFileName().GetFullPath(), kisymbolname, m_properties.get() );

    if( !part )
    {
        wxLogMessage( wxString::Format( _( "Could not find %s in the imported library" ),
                                        kisymbolname ) );
        return;
    }

    LIB_ID                         libId( getLibName(), kisymbolname );
    std::unique_ptr<SCH_COMPONENT> component( new SCH_COMPONENT() );
    component->SetLibId( libId );
    component->SetUnit( unit );
    component->SetPosition( wxPoint( einstance.x.ToSchUnits(), -einstance.y.ToSchUnits() ) );
    component->GetField( FOOTPRINT )->SetText( package );

    if( einstance.rot )
    {
        component->SetOrientation( kiCadComponentRotation( einstance.rot->degrees ) );

        if( einstance.rot->mirror )
            component->MirrorY( einstance.x.ToSchUnits() );
    }

    LIB_FIELDS partFields;
    part->GetFields( partFields );

    for( auto const& field : partFields )
    {
        component->GetField( field.GetId() )->ImportValues( field );
        component->GetField( field.GetId() )
                ->SetTextPos( component->GetPosition() + field.GetTextPos() );
    }

    // If there is no footprint assigned, then prepend the reference value
    // with a hash character to mute netlist updater complaints
    wxString reference = package.IsEmpty() ? '#' + einstance.part : einstance.part;

    // EAGLE allows references to be single digits.  This breaks KiCad netlisting, which requires
    // parts to have non-digit + digit annotation.  If the reference begins with a number,
    // we prepend 'UNK' (unknown) for the symbol designator
    if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
        reference.Prepend( "UNK" );

    SCH_SHEET_PATH sheetpath;
    m_rootSheet->LocatePathOfScreen( screen, &sheetpath );
    wxString current_sheetpath = sheetpath.PathAsString() + component->m_Uuid.AsString();

    component->GetField( REFERENCE )->SetText( reference );
    component->AddHierarchicalReference( current_sheetpath, reference, unit );

    if( epart->value )
        component->GetField( VALUE )->SetText( *epart->value );
    else
        component->GetField( VALUE )->SetText( kisymbolname );

    // Set the visibility of fields.
    component->GetField( REFERENCE )->SetVisible( part->GetField( REFERENCE )->IsVisible() );
    component->GetField( VALUE )->SetVisible( part->GetField( VALUE )->IsVisible() );

    for( const auto& a : epart->attribute )
    {
        auto field = component->AddField( *component->GetField( VALUE ) );
        field->SetName( a.first );
        field->SetText( a.second );
        field->SetVisible( false );
    }

    for( const auto& a : epart->variant )
    {
        auto field = component->AddField( *component->GetField( VALUE ) );
        field->SetName( "VARIANT_" + a.first );
        field->SetText( a.second );
        field->SetVisible( false );
    }

    bool valueAttributeFound = false;
    bool nameAttributeFound  = false;

    wxXmlNode* attributeNode = aInstanceNode->GetChildren();

    // Parse attributes for the instance
    while( attributeNode )
    {
        if( attributeNode->GetName() == "attribute" )
        {
            auto       attr  = EATTR( attributeNode );
            SCH_FIELD* field = NULL;

            if( attr.name.Lower() == "name" )
            {
                field              = component->GetField( REFERENCE );
                nameAttributeFound = true;
            }
            else if( attr.name.Lower() == "value" )
            {
                field               = component->GetField( VALUE );
                valueAttributeFound = true;
            }
            else
            {
                field = component->FindField( attr.name );

                if( field )
                    field->SetVisible( false );
            }

            if( field )
            {

                field->SetPosition( wxPoint( attr.x->ToSchUnits(), -attr.y->ToSchUnits() ) );
                int  align      = attr.align ? *attr.align : ETEXT::BOTTOM_LEFT;
                int  absdegrees = attr.rot ? attr.rot->degrees : 0;
                bool mirror     = attr.rot ? attr.rot->mirror : false;

                if( einstance.rot && einstance.rot->mirror )
                    mirror = !mirror;

                bool spin = attr.rot ? attr.rot->spin : false;

                if( attr.display == EATTR::Off || attr.display == EATTR::NAME )
                    field->SetVisible( false );

                int rotation   = einstance.rot ? einstance.rot->degrees : 0;
                int reldegrees = ( absdegrees - rotation + 360.0 );
                reldegrees %= 360;

                eagleToKicadAlignment(
                        (EDA_TEXT*) field, align, reldegrees, mirror, spin, absdegrees );
            }
        }
        else if( attributeNode->GetName() == "variant" )
        {
            wxString variant, value;

            if( attributeNode->GetAttribute( "name", &variant )
                    && attributeNode->GetAttribute( "value", &value ) )
            {
                auto field = component->AddField( *component->GetField( VALUE ) );
                field->SetName( "VARIANT_" + variant );
                field->SetText( value );
                field->SetVisible( false );
            }
        }

        attributeNode = attributeNode->GetNext();
    }

    if( einstance.smashed && einstance.smashed.Get() )
    {
        if( !valueAttributeFound )
            component->GetField( VALUE )->SetVisible( false );

        if( !nameAttributeFound )
            component->GetField( REFERENCE )->SetVisible( false );
    }


    // Save the pin positions
    auto& schLibTable = *m_kiway->Prj().SchSymbolLibTable();
    wxCHECK( component->Resolve( schLibTable ), /*void*/ );
    std::vector<LIB_PIN*> pins;
    component->GetPins( pins );

    for( const auto& pin : pins )
        m_connPoints[component->GetPinPhysicalPosition( pin )].emplace( pin );


    component->ClearFlags();

    screen->Append( component.release() );
}


EAGLE_LIBRARY* SCH_EAGLE_PLUGIN::loadLibrary(
        wxXmlNode* aLibraryNode, EAGLE_LIBRARY* aEagleLibrary )
{
    NODE_MAP libraryChildren = MapChildren( aLibraryNode );

    // Loop through the symbols and load each of them
    wxXmlNode* symbolNode = getChildrenNodes( libraryChildren, "symbols" );

    while( symbolNode )
    {
        wxString symbolName                    = symbolNode->GetAttribute( "name" );
        aEagleLibrary->SymbolNodes[symbolName] = symbolNode;
        symbolNode                             = symbolNode->GetNext();
    }

    // Loop through the devicesets and load each of them
    wxXmlNode* devicesetNode = getChildrenNodes( libraryChildren, "devicesets" );

    while( devicesetNode )
    {
        // Get Device set information
        EDEVICE_SET edeviceset = EDEVICE_SET( devicesetNode );

        wxString prefix = edeviceset.prefix ? edeviceset.prefix.Get() : "";

        NODE_MAP   aDeviceSetChildren = MapChildren( devicesetNode );
        wxXmlNode* deviceNode         = getChildrenNodes( aDeviceSetChildren, "devices" );

        // For each device in the device set:
        while( deviceNode )
        {
            // Get device information
            EDEVICE edevice = EDEVICE( deviceNode );

            // Create symbol name from deviceset and device names.
            wxString symbolName = edeviceset.name + edevice.name;
            symbolName.Replace( "*", "" );
            wxASSERT( !symbolName.IsEmpty() );
            symbolName = fixSymbolName( symbolName );

            if( edevice.package )
                aEagleLibrary->package[symbolName] = edevice.package.Get();

            // Create KiCad symbol.
            unique_ptr<LIB_PART> kpart( new LIB_PART( symbolName ) );

            // Process each gate in the deviceset for this device.
            wxXmlNode* gateNode    = getChildrenNodes( aDeviceSetChildren, "gates" );
            int        gates_count = countChildren( aDeviceSetChildren["gates"], "gate" );
            kpart->SetUnitCount( gates_count );
            kpart->LockUnits( true );

            LIB_FIELD* reference = kpart->GetField( REFERENCE );

            if( prefix.length() == 0 )
                reference->SetVisible( false );
            else
                // If there is no footprint assigned, then prepend the reference value
                // with a hash character to mute netlist updater complaints
                reference->SetText( edevice.package ? prefix : '#' + prefix );

            int  gateindex = 1;
            bool ispower   = false;

            while( gateNode )
            {
                EGATE egate = EGATE( gateNode );

                aEagleLibrary->GateUnit[edeviceset.name + edevice.name + egate.name] = gateindex;

                ispower = loadSymbol( aEagleLibrary->SymbolNodes[egate.symbol], kpart, &edevice,
                        gateindex, egate.name );

                gateindex++;
                gateNode = gateNode->GetNext();
            } // gateNode

            kpart->SetUnitCount( gates_count );

            if( gates_count == 1 && ispower )
                kpart->SetPower();

            wxString name = fixSymbolName( kpart->GetName() );
            kpart->SetName( name );
            m_pi->SaveSymbol( getLibFileName().GetFullPath(), new LIB_PART( *kpart.get() ),
                    m_properties.get() );
            aEagleLibrary->KiCadSymbols.insert( name, kpart.release() );

            deviceNode = deviceNode->GetNext();
        } // devicenode

        devicesetNode = devicesetNode->GetNext();
    } // devicesetNode

    return aEagleLibrary;
}


bool SCH_EAGLE_PLUGIN::loadSymbol( wxXmlNode* aSymbolNode, std::unique_ptr<LIB_PART>& aPart,
        EDEVICE* aDevice, int aGateNumber, const wxString& aGateName )
{
    wxString               symbolName = aSymbolNode->GetAttribute( "name" );
    std::vector<LIB_ITEM*> items;

    wxXmlNode* currentNode = aSymbolNode->GetChildren();

    bool foundName  = false;
    bool foundValue = false;
    bool ispower    = false;
    int  pincount   = 0;

    while( currentNode )
    {
        wxString nodeName = currentNode->GetName();

        if( nodeName == "circle" )
        {
            aPart->AddDrawItem( loadSymbolCircle( aPart, currentNode, aGateNumber ) );
        }
        else if( nodeName == "pin" )
        {
            EPIN                     ePin = EPIN( currentNode );
            std::unique_ptr<LIB_PIN> pin( loadPin( aPart, currentNode, &ePin, aGateNumber ) );
            pincount++;

            pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );

            if( ePin.direction )
            {
                for( const auto& pinDir : pinDirectionsMap )
                {
                    if( ePin.direction->Lower() == pinDir.first )
                    {
                        pin->SetType( pinDir.second );

                        if( pinDir.first == "sup" ) // power supply symbol
                            ispower = true;

                        break;
                    }
                }
            }


            if( aDevice->connects.size() != 0 )
            {
                for( const auto& connect : aDevice->connects )
                {
                    if( connect.gate == aGateName && pin->GetName() == connect.pin )
                    {
                        wxArrayString pads = wxSplit( wxString( connect.pad ), ' ' );

                        pin->SetPartNumber( aGateNumber );
                        pin->SetUnit( aGateNumber );
                        pin->SetName( escapeName( pin->GetName() ) );

                        if( pads.GetCount() > 1 )
                        {
                            pin->SetNumberTextSize( 0 );
                        }

                        // Eagle does not connect multiple NC pins together when they are stacked.
                        // KiCad will do this for pins that are coincident.  We opt here for correct
                        // schematic netlist and leave out the multiple NC pins when stacked.
                        for( unsigned i = 0; i < pads.GetCount(); i++ )
                        {
                            if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC && i > 0 )
                                break;

                            LIB_PIN* apin = new LIB_PIN( *pin );

                            wxString padname( pads[i] );
                            apin->SetNumber( padname );
                            aPart->AddDrawItem( apin );
                        }

                        break;
                    }
                }
            }
            else
            {
                pin->SetPartNumber( aGateNumber );
                pin->SetUnit( aGateNumber );
                pin->SetNumber( wxString::Format( "%i", pincount ) );
                aPart->AddDrawItem( pin.release() );
            }
        }
        else if( nodeName == "polygon" )
        {
            aPart->AddDrawItem( loadSymbolPolyLine( aPart, currentNode, aGateNumber ) );
        }
        else if( nodeName == "rectangle" )
        {
            aPart->AddDrawItem( loadSymbolRectangle( aPart, currentNode, aGateNumber ) );
        }
        else if( nodeName == "text" )
        {
            std::unique_ptr<LIB_TEXT> libtext( loadSymbolText( aPart, currentNode, aGateNumber ) );

            if( libtext->GetText().Upper() == ">NAME" )
            {
                LIB_FIELD* field = aPart->GetField( REFERENCE );
                loadFieldAttributes( field, libtext.get() );
                foundName = true;
            }
            else if( libtext->GetText().Upper() == ">VALUE" )
            {
                LIB_FIELD* field = aPart->GetField( VALUE );
                loadFieldAttributes( field, libtext.get() );
                foundValue = true;
            }
            else
            {
                aPart->AddDrawItem( libtext.release() );
            }
        }
        else if( nodeName == "wire" )
        {
            aPart->AddDrawItem( loadSymbolWire( aPart, currentNode, aGateNumber ) );
        }

        /*
         *  else if( nodeName == "description" )
         *  {
         *  }
         *  else if( nodeName == "dimension" )
         *  {
         *  }
         *  else if( nodeName == "frame" )
         *  {
         *  }
         */

        currentNode = currentNode->GetNext();
    }

    if( foundName == false )
        aPart->GetField( REFERENCE )->SetVisible( false );

    if( foundValue == false )
        aPart->GetField( VALUE )->SetVisible( false );

    return pincount == 1 ? ispower : false;
}


LIB_CIRCLE* SCH_EAGLE_PLUGIN::loadSymbolCircle(
        std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aCircleNode, int aGateNumber )
{
    // Parse the circle properties
    ECIRCLE c( aCircleNode );

    unique_ptr<LIB_CIRCLE> circle( new LIB_CIRCLE( aPart.get() ) );

    circle->SetPosition( wxPoint( c.x.ToSchUnits(), c.y.ToSchUnits() ) );
    circle->SetRadius( c.radius.ToSchUnits() );
    circle->SetWidth( c.width.ToSchUnits() );
    circle->SetUnit( aGateNumber );

    return circle.release();
}


LIB_RECTANGLE* SCH_EAGLE_PLUGIN::loadSymbolRectangle(
        std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aRectNode, int aGateNumber )
{
    ERECT rect( aRectNode );

    unique_ptr<LIB_RECTANGLE> rectangle( new LIB_RECTANGLE( aPart.get() ) );

    rectangle->SetPosition( wxPoint( rect.x1.ToSchUnits(), rect.y1.ToSchUnits() ) );
    rectangle->SetEnd( wxPoint( rect.x2.ToSchUnits(), rect.y2.ToSchUnits() ) );

    rectangle->SetUnit( aGateNumber );
    // Eagle rectangles are filled by definition.
    rectangle->SetFillMode( FILLED_SHAPE );

    return rectangle.release();
}


LIB_ITEM* SCH_EAGLE_PLUGIN::loadSymbolWire(
        std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aWireNode, int aGateNumber )
{
    auto ewire = EWIRE( aWireNode );

    wxPoint begin, end;

    begin.x = ewire.x1.ToSchUnits();
    begin.y = ewire.y1.ToSchUnits();
    end.x   = ewire.x2.ToSchUnits();
    end.y   = ewire.y2.ToSchUnits();

    if( begin == end )
        return nullptr;

    // if the wire is an arc
    if( ewire.curve )
    {
        std::unique_ptr<LIB_ARC> arc( new LIB_ARC( aPart.get() ) );
        wxPoint                  center = ConvertArcCenter( begin, end, *ewire.curve * -1 );

        double radius = sqrt( abs( ( ( center.x - begin.x ) * ( center.x - begin.x ) )
                                   + ( ( center.y - begin.y ) * ( center.y - begin.y ) ) ) )
                        * 2;

        // this emulates the filled semicircles created by a thick arc with flat ends caps.
        if( ewire.width.ToSchUnits() * 2 > radius )
        {
            wxPoint centerStartVector = begin - center;
            wxPoint centerEndVector   = end - center;

            centerStartVector.x = centerStartVector.x * ewire.width.ToSchUnits() * 2 / radius;
            centerStartVector.y = centerStartVector.y * ewire.width.ToSchUnits() * 2 / radius;

            centerEndVector.x = centerEndVector.x * ewire.width.ToSchUnits() * 2 / radius;
            centerEndVector.y = centerEndVector.y * ewire.width.ToSchUnits() * 2 / radius;

            begin = center + centerStartVector;
            end   = center + centerEndVector;

            radius = sqrt( abs( ( ( center.x - begin.x ) * ( center.x - begin.x ) )
                                + ( ( center.y - begin.y ) * ( center.y - begin.y ) ) ) )
                     * 2;

            arc->SetWidth( 1 );
            arc->SetFillMode( FILLED_SHAPE );
        }
        else
        {
            arc->SetWidth( ewire.width.ToSchUnits() );
        }

        arc->SetPosition( center );

        if( *ewire.curve > 0 )
        {
            arc->SetStart( begin );
            arc->SetEnd( end );
        }
        else
        {
            arc->SetStart( end );
            arc->SetEnd( begin );
        }

        arc->SetRadius( radius );
        arc->CalcRadiusAngles();
        arc->SetUnit( aGateNumber );

        return (LIB_ITEM*) arc.release();
    }
    else
    {
        std::unique_ptr<LIB_POLYLINE> polyLine( new LIB_POLYLINE( aPart.get() ) );

        polyLine->AddPoint( begin );
        polyLine->AddPoint( end );
        polyLine->SetUnit( aGateNumber );
        polyLine->SetWidth( ewire.width.ToSchUnits() );

        return (LIB_ITEM*) polyLine.release();
    }
}


LIB_POLYLINE* SCH_EAGLE_PLUGIN::loadSymbolPolyLine(
        std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aPolygonNode, int aGateNumber )
{
    std::unique_ptr<LIB_POLYLINE> polyLine( new LIB_POLYLINE( aPart.get() ) );

    EPOLYGON   epoly( aPolygonNode );
    wxXmlNode* vertex = aPolygonNode->GetChildren();


    wxPoint pt;

    while( vertex )
    {
        if( vertex->GetName() == "vertex" ) // skip <xmlattr> node
        {
            EVERTEX evertex( vertex );
            pt = wxPoint( evertex.x.ToSchUnits(), evertex.y.ToSchUnits() );
            polyLine->AddPoint( pt );
        }

        vertex = vertex->GetNext();
    }

    polyLine->SetFillMode( FILLED_SHAPE );
    polyLine->SetUnit( aGateNumber );

    return polyLine.release();
}


LIB_PIN* SCH_EAGLE_PLUGIN::loadPin(
        std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aPin, EPIN* aEPin, int aGateNumber )
{
    std::unique_ptr<LIB_PIN> pin( new LIB_PIN( aPart.get() ) );
    pin->SetPosition( wxPoint( aEPin->x.ToSchUnits(), aEPin->y.ToSchUnits() ) );
    pin->SetName( aEPin->name );
    pin->SetUnit( aGateNumber );

    int roti = aEPin->rot ? aEPin->rot->degrees : 0;

    switch( roti )
    {
    default:
        wxASSERT_MSG( false, wxString::Format( "Unhandled orientation (%d degrees)", roti ) );

    // fall through
    case 0:
        pin->SetOrientation( 'R' );
        break;

    case 90:
        pin->SetOrientation( 'U' );
        break;

    case 180:
        pin->SetOrientation( 'L' );
        break;

    case 270:
        pin->SetOrientation( 'D' );
        break;
    }

    if( aEPin->length )
    {
        wxString length = aEPin->length.Get();

        if( length == "short" )
        {
            pin->SetLength( Mils2iu( 100 ) );
        }
        else if( length == "middle" )
        {
            pin->SetLength( Mils2iu( 200 ) );
        }
        else if( length == "long" )
        {
            pin->SetLength( Mils2iu( 300 ) );
        }
        else if( length == "point" )
        {
            pin->SetLength( Mils2iu( 0 ) );
        }
    }

    // emulate the visibility of pin elements
    if( aEPin->visible )
    {
        wxString visible = aEPin->visible.Get();

        if( visible == "off" )
        {
            pin->SetNameTextSize( 0 );
            pin->SetNumberTextSize( 0 );
        }
        else if( visible == "pad" )
        {
            pin->SetNameTextSize( 0 );
        }
        else if( visible == "pin" )
        {
            pin->SetNumberTextSize( 0 );
        }

        /*
         *  else if( visible == "both" )
         *  {
         *  }
         */
    }

    if( aEPin->function )
    {
        wxString function = aEPin->function.Get();

        if( function == "dot" )
        {
            pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
        }
        else if( function == "clk" )
        {
            pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
        }
        else if( function == "dotclk" )
        {
            pin->SetShape( GRAPHIC_PINSHAPE::INVERTED_CLOCK );
        }
    }

    return pin.release();
}


LIB_TEXT* SCH_EAGLE_PLUGIN::loadSymbolText(
        std::unique_ptr<LIB_PART>& aPart, wxXmlNode* aLibText, int aGateNumber )
{
    std::unique_ptr<LIB_TEXT> libtext( new LIB_TEXT( aPart.get() ) );
    ETEXT                     etext( aLibText );

    libtext->SetUnit( aGateNumber );
    libtext->SetPosition( wxPoint( etext.x.ToSchUnits(), etext.y.ToSchUnits() ) );

    // Eagle supports multiple line text in library symbols.  Legacy library symbol text cannot
    // contain CRs or LFs.
    //
    // @todo Split this into multiple text objects and offset the Y position so that it looks
    //       more like the original Eagle schematic.
    wxString text = aLibText->GetNodeContent();
    std::replace( text.begin(), text.end(), '\n', '_' );
    std::replace( text.begin(), text.end(), '\r', '_' );

    libtext->SetText( text.IsEmpty() ? "~~" : text );
    loadTextAttributes( libtext.get(), etext );

    return libtext.release();
}


SCH_TEXT* SCH_EAGLE_PLUGIN::loadPlainText( wxXmlNode* aSchText )
{
    std::unique_ptr<SCH_TEXT> schtext( new SCH_TEXT() );
    ETEXT                     etext = ETEXT( aSchText );

    const wxString& thetext = aSchText->GetNodeContent();
    schtext->SetText( thetext.IsEmpty() ? "\" \"" : escapeName( thetext ) );
    schtext->SetPosition( wxPoint( etext.x.ToSchUnits(), -etext.y.ToSchUnits() ) );
    loadTextAttributes( schtext.get(), etext );
    schtext->SetItalic( false );

    return schtext.release();
}


void SCH_EAGLE_PLUGIN::loadTextAttributes( EDA_TEXT* aText, const ETEXT& aAttribs ) const
{
    aText->SetTextSize( aAttribs.ConvertSize() );

    if( aAttribs.ratio )
    {
        if( aAttribs.ratio.CGet() > 12 )
        {
            aText->SetBold( true );
            aText->SetThickness( GetPenSizeForBold( aText->GetTextWidth() ) );
        }
    }

    int  align   = aAttribs.align ? *aAttribs.align : ETEXT::BOTTOM_LEFT;
    int  degrees = aAttribs.rot ? aAttribs.rot->degrees : 0;
    bool mirror  = aAttribs.rot ? aAttribs.rot->mirror : false;
    bool spin    = aAttribs.rot ? aAttribs.rot->spin : false;

    eagleToKicadAlignment( aText, align, degrees, mirror, spin, 0 );
}


void SCH_EAGLE_PLUGIN::loadFieldAttributes( LIB_FIELD* aField, const LIB_TEXT* aText ) const
{
    aField->SetTextPos( aText->GetPosition() );
    aField->SetTextSize( aText->GetTextSize() );
    aField->SetTextAngle( aText->GetTextAngle() );
    aField->SetBold( aText->IsBold() );
    aField->SetVertJustify( aText->GetVertJustify() );
    aField->SetHorizJustify( aText->GetHorizJustify() );
    aField->SetVisible( true );
}


void SCH_EAGLE_PLUGIN::adjustNetLabels()
{
    // Eagle supports detached labels, so a label does not need to be placed on a wire
    // to be associated with it. KiCad needs to move them, so the labels actually touch the
    // corresponding wires.

    // Sort the intersection points to speed up the search process
    std::sort( m_wireIntersections.begin(), m_wireIntersections.end() );

    auto onIntersection = [&]( const VECTOR2I& aPos ) {
        return std::binary_search( m_wireIntersections.begin(), m_wireIntersections.end(), aPos );
    };

    for( auto& segDesc : m_segments )
    {
        for( SCH_TEXT* label : segDesc.labels )
        {
            VECTOR2I   labelPos( label->GetPosition() );
            const SEG* segAttached = segDesc.LabelAttached( label );

            if( segAttached && !onIntersection( labelPos ) )
                continue; // label is placed correctly


            // Move the label to the nearest wire
            if( !segAttached )
            {
                std::tie( labelPos, segAttached ) =
                        findNearestLinePoint( label->GetPosition(), segDesc.segs );

                if( !segAttached ) // we cannot do anything
                    continue;
            }


            // Create a vector pointing in the direction of the wire, 50 mils long
            VECTOR2I wireDirection( segAttached->B - segAttached->A );
            wireDirection = wireDirection.Resize( Mils2iu( 50 ) );
            const VECTOR2I origPos( labelPos );

            // Flags determining the search direction
            bool checkPositive = true, checkNegative = true, move = false;
            int  trial = 0;

            // Be sure the label is not placed on a wire intersection
            while( ( !move || onIntersection( labelPos ) ) && ( checkPositive || checkNegative ) )
            {
                move = false;

                // Move along the attached wire to find the new label position
                if( trial % 2 == 1 )
                {
                    labelPos = wxPoint( origPos + wireDirection * trial / 2 );
                    move = checkPositive = segAttached->Contains( labelPos );
                }
                else
                {
                    labelPos = wxPoint( origPos - wireDirection * trial / 2 );
                    move = checkNegative = segAttached->Contains( labelPos );
                }

                ++trial;
            }

            if( move )
                label->SetPosition( wxPoint( labelPos ) );
        }
    }

    m_segments.clear();
    m_wireIntersections.clear();
}


bool SCH_EAGLE_PLUGIN::CheckHeader( const wxString& aFileName )
{
    // Open file and check first line
    wxTextFile tempFile;

    tempFile.Open( aFileName );
    wxString firstline;
    // read the first line
    firstline           = tempFile.GetFirstLine();
    wxString secondline = tempFile.GetNextLine();
    wxString thirdline  = tempFile.GetNextLine();
    tempFile.Close();

    return firstline.StartsWith( "<?xml" ) && secondline.StartsWith( "<!DOCTYPE eagle SYSTEM" )
           && thirdline.StartsWith( "<eagle version" );
}


void SCH_EAGLE_PLUGIN::moveLabels( SCH_ITEM* aWire, const wxPoint& aNewEndPoint )
{
    for( auto item : m_currentSheet->GetScreen()->Items().Overlapping( aWire->GetBoundingBox() ) )
    {
        if( item->Type() == SCH_LABEL_T || item->Type() == SCH_GLOBAL_LABEL_T )
        {
            if( TestSegmentHit( item->GetPosition(), ( (SCH_LINE*) aWire )->GetStartPoint(),
                        ( (SCH_LINE*) aWire )->GetEndPoint(), 0 ) )
            {
                item->SetPosition( aNewEndPoint );
            }
        }
    }
}


void SCH_EAGLE_PLUGIN::addBusEntries()
{
    // Add bus entry symbols
    // TODO: Cleanup this function and break into pieces

    // for each wire segment, compare each end with all busess.
    // If the wire end is found to end on a bus segment, place a bus entry symbol.

    for( auto it1 = m_currentSheet->GetScreen()->Items().OfType( SCH_LINE_T ).begin();
            it1 != m_currentSheet->GetScreen()->Items().end(); ++it1 )
    {
        SCH_LINE* bus = static_cast<SCH_LINE*>( *it1 );

        // Check line type for wire
        if( bus->GetLayer() != LAYER_BUS )
            continue;


        wxPoint busstart = bus->GetStartPoint();
        wxPoint busend   = bus->GetEndPoint();

        auto it2 = it1;
        ++it2;
        for( ; it2 != m_currentSheet->GetScreen()->Items().end(); ++it2 )
        {
            SCH_LINE* line = static_cast<SCH_LINE*>( *it2 );

            // Check line type for bus
            if( ( (SCH_LINE*) *it2 )->GetLayer() == LAYER_WIRE )
            {
                // Get points of both segments.
                wxPoint linestart = line->GetStartPoint();
                wxPoint lineend   = line->GetEndPoint();

                // Test for horizontal wire and         vertical bus
                if( linestart.y == lineend.y && busstart.x == busend.x )
                {
                    if( TestSegmentHit( linestart, busstart, busend, 0 ) )
                    {
                        // Wire start is on a bus.
                        // Wire start is on the vertical bus

                        // if the end of the wire is to the left of the bus
                        if( lineend.x < busstart.x )
                        {
                            // |
                            // ---|
                            // |
                            if( TestSegmentHit(
                                        linestart + wxPoint( 0, -100 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( -100, 0 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( -100, 0 ) );
                                line->SetStartPoint( linestart + wxPoint( -100, 0 ) );
                            }
                            else if( TestSegmentHit(
                                             linestart + wxPoint( 0, 100 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( -100, 0 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( -100, 0 ) );
                                line->SetStartPoint( linestart + wxPoint( -100, 0 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, linestart, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                        // else the wire end is to the right of the bus
                        // Wire is to the right of the bus
                        // |
                        // |----
                        // |
                        else
                        {
                            // test is bus exists above the wire
                            if( TestSegmentHit(
                                        linestart + wxPoint( 0, -100 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( 0, -100 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( 100, 0 ) );
                                line->SetStartPoint( linestart + wxPoint( 100, 0 ) );
                            }
                            // test is bus exists below the wire
                            else if( TestSegmentHit(
                                             linestart + wxPoint( 0, 100 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( 0, 100 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( 100, 0 ) );
                                line->SetStartPoint( linestart + wxPoint( 100, 0 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, linestart, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                    }

                    // Same thing but test end of the wire instead.
                    if( TestSegmentHit( lineend, busstart, busend, 0 ) )
                    {
                        // Wire end is on the vertical bus

                        // if the start of the wire is to the left of the bus
                        if( linestart.x < busstart.x )
                        {
                            // Test if bus exists above the wire
                            if( TestSegmentHit( lineend + wxPoint( 0, 100 ), busstart, busend, 0 ) )
                            {
                                // |
                                // ___/|
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        lineend + wxPoint( -100, 0 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( -100, 0 ) );
                                line->SetEndPoint( lineend + wxPoint( -100, 0 ) );
                            }
                            // Test if bus exists below the wire
                            else if( TestSegmentHit(
                                             lineend + wxPoint( 0, -100 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry =
                                        new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( -100, 0 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( -100, 0 ) );
                                line->SetEndPoint( lineend + wxPoint( -100, 0 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, lineend, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                        // else the start of the wire is to the right of the bus
                        // |
                        // |----
                        // |
                        else
                        {
                            // test if bus existed above the wire
                            if( TestSegmentHit(
                                        lineend + wxPoint( 0, -100 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        lineend + wxPoint( 0, -100 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( 100, 0 ) );
                                line->SetEndPoint( lineend + wxPoint( 100, 0 ) );
                            }
                            // test if bus existed below the wire
                            else if( TestSegmentHit(
                                             lineend + wxPoint( 0, 100 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry =
                                        new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( 0, 100 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( 100, 0 ) );
                                line->SetEndPoint( lineend + wxPoint( 100, 0 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, lineend, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                    }
                } // if( linestart.y == lineend.y && busstart.x == busend.x)

                // Test for horizontal wire and vertical bus
                if( linestart.x == lineend.x && busstart.y == busend.y )
                {
                    if( TestSegmentHit( linestart, busstart, busend, 0 ) )
                    {
                        // Wire start is on the bus
                        // If wire end is above the bus,
                        if( lineend.y < busstart.y )
                        {
                            // Test for bus existance to the left of the wire
                            if( TestSegmentHit(
                                        linestart + wxPoint( -100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( -100, 0 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( 0, -100 ) );
                                line->SetStartPoint( linestart + wxPoint( 0, -100 ) );
                            }
                            else if( TestSegmentHit(
                                             linestart + wxPoint( 100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( 0, 100 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( 0, -100 ) );
                                line->SetStartPoint( linestart + wxPoint( 0, -100 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, linestart, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                        else // wire end is below the bus.
                        {
                            // Test for bus existance to the left of the wire
                            if( TestSegmentHit(
                                        linestart + wxPoint( -100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( -100, 0 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( 0, 100 ) );
                                line->SetStartPoint( linestart + wxPoint( 0, 100 ) );
                            }
                            else if( TestSegmentHit(
                                             linestart + wxPoint( 100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        linestart + wxPoint( 100, 0 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, linestart + wxPoint( 0, 100 ) );
                                line->SetStartPoint( linestart + wxPoint( 0, 100 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, linestart, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                    }

                    if( TestSegmentHit( lineend, busstart, busend, 0 ) )
                    {
                        // Wire end is on the bus
                        // If wire start is above the bus,

                        if( linestart.y < busstart.y )
                        {
                            // Test for bus existance to the left of the wire
                            if( TestSegmentHit(
                                        lineend + wxPoint( -100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry =
                                        new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( -100, 0 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( 0, -100 ) );
                                line->SetEndPoint( lineend + wxPoint( 0, -100 ) );
                            }
                            else if( TestSegmentHit(
                                             lineend + wxPoint( 100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        lineend + wxPoint( 0, -100 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( 0, -100 ) );
                                line->SetEndPoint( lineend + wxPoint( 0, -100 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, lineend, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                        else // wire end is below the bus.
                        {
                            // Test for bus existance to the left of the wire
                            if( TestSegmentHit(
                                        lineend + wxPoint( -100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY(
                                        lineend + wxPoint( -100, 0 ), '\\' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( 0, 100 ) );
                                line->SetEndPoint( lineend + wxPoint( 0, 100 ) );
                            }
                            else if( TestSegmentHit(
                                             lineend + wxPoint( 100, 0 ), busstart, busend, 0 ) )
                            {
                                SCH_BUS_WIRE_ENTRY* busEntry =
                                        new SCH_BUS_WIRE_ENTRY( lineend + wxPoint( 0, 100 ), '/' );
                                busEntry->SetFlags( IS_NEW );
                                m_currentSheet->GetScreen()->Append( busEntry );
                                moveLabels( line, lineend + wxPoint( 0, 100 ) );
                                line->SetEndPoint( lineend + wxPoint( 0, 100 ) );
                            }
                            else
                            {
                                SCH_MARKER* marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
                                marker->SetData( 0, lineend, "Bus Entry needed" );
                                m_currentSheet->GetScreen()->Append( marker );
                            }
                        }
                    }
                }

                linestart = line->GetStartPoint();
                lineend   = line->GetEndPoint();
                busstart  = bus->GetStartPoint();
                busend    = bus->GetEndPoint();

                // bus entry wire isn't horizontal or vertical
                if( TestSegmentHit( linestart, busstart, busend, 0 ) )
                {
                    wxPoint wirevector = linestart - lineend;

                    if( wirevector.x > 0 )
                    {
                        if( wirevector.y > 0 )
                        {
                            wxPoint             p        = linestart + wxPoint( -100, -100 );
                            SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, '\\' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );
                            moveLabels( line, p );

                            if( p == lineend ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                                line = nullptr;
                            }
                            else
                            {
                                line->SetStartPoint( p );
                            }
                        }
                        else
                        {
                            wxPoint             p        = linestart + wxPoint( -100, 100 );
                            SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, '/' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );

                            moveLabels( line, p );

                            if( p == lineend ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                                line = nullptr;
                            }
                            else
                            {
                                line->SetStartPoint( p );
                            }
                        }
                    }
                    else
                    {
                        if( wirevector.y > 0 )
                        {
                            SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( linestart, '/' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );

                            moveLabels( line, linestart + wxPoint( 100, -100 ) );

                            if( linestart + wxPoint( 100, -100 )
                                    == lineend ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                                line = nullptr;
                            }
                            else
                            {
                                line->SetStartPoint( linestart + wxPoint( 100, -100 ) );
                            }
                        }
                        else
                        {
                            SCH_BUS_WIRE_ENTRY* busEntry =
                                    new SCH_BUS_WIRE_ENTRY( linestart, '\\' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );
                            moveLabels( line, linestart + wxPoint( 100, 100 ) );

                            if( linestart + wxPoint( 100, 100 )
                                    == lineend ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                                line = nullptr;
                            }
                            else
                            {
                                line->SetStartPoint( linestart + wxPoint( 100, 100 ) );
                            }
                        }
                    }
                }

                if( line && TestSegmentHit( lineend, busstart, busend, 0 ) )
                {
                    wxPoint wirevector = linestart - lineend;

                    if( wirevector.x > 0 )
                    {
                        if( wirevector.y > 0 )
                        {
                            wxPoint             p        = lineend + wxPoint( 100, 100 );
                            SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( lineend, '\\' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );

                            moveLabels( line, p );

                            if( p == linestart ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                            }
                            else
                            {
                                line->SetEndPoint( p );
                            }
                        }
                        else
                        {
                            wxPoint             p        = lineend + wxPoint( 100, -100 );
                            SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( lineend, '/' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );

                            moveLabels( line, p );

                            if( p == linestart ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                            }
                            else
                            {
                                line->SetEndPoint( p );
                            }
                        }
                    }
                    else
                    {
                        if( wirevector.y > 0 )
                        {
                            wxPoint             p        = lineend + wxPoint( -100, 100 );
                            SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, '/' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );
                            moveLabels( line, p );

                            if( p == linestart ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                            }
                            else
                            {
                                line->SetEndPoint( p );
                            }
                        }
                        else
                        {
                            wxPoint             p        = lineend + wxPoint( -100, -100 );
                            SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, '\\' );
                            busEntry->SetFlags( IS_NEW );
                            m_currentSheet->GetScreen()->Append( busEntry );
                            moveLabels( line, p );

                            if( p == linestart ) // wire is overlapped by bus entry symbol
                            {
                                m_currentSheet->GetScreen()->DeleteItem( line );
                            }
                            else
                            {
                                line->SetEndPoint( p );
                            }
                        }
                    }
                }
            }
        }
    } // for ( bus ..
}


const SEG* SCH_EAGLE_PLUGIN::SEG_DESC::LabelAttached( const SCH_TEXT* aLabel ) const
{
    VECTOR2I labelPos( aLabel->GetPosition() );

    for( const auto& seg : segs )
    {
        if( seg.Contains( labelPos ) )
            return &seg;
    }

    return nullptr;
}


// TODO could be used to place junctions, instead of IsJunctionNeeded() (see SCH_EDIT_FRAME::importFile())
bool SCH_EAGLE_PLUGIN::checkConnections(
        const SCH_COMPONENT* aComponent, const LIB_PIN* aPin ) const
{
    wxPoint pinPosition = aComponent->GetPinPhysicalPosition( aPin );
    auto    pointIt     = m_connPoints.find( pinPosition );

    if( pointIt == m_connPoints.end() )
        return false;

    const auto& items = pointIt->second;
    wxASSERT( items.find( aPin ) != items.end() );
    return items.size() > 1;
}


void SCH_EAGLE_PLUGIN::addImplicitConnections(
        SCH_COMPONENT* aComponent, SCH_SCREEN* aScreen, bool aUpdateSet )
{
    wxCHECK( aComponent->GetPartRef(), /*void*/ );

    // Normally power parts also have power input pins,
    // but they already force net names on the attached wires
    if( aComponent->GetPartRef()->IsPower() )
        return;

    int                   unit      = aComponent->GetUnit();
    const wxString        reference = aComponent->GetField( REFERENCE )->GetText();
    std::vector<LIB_PIN*> pins;
    aComponent->GetPartRef()->GetPins( pins );
    std::set<int> missingUnits;

    // Search all units for pins creating implicit connections
    for( const auto& pin : pins )
    {
        if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN )
        {
            bool pinInUnit = !unit || pin->GetUnit() == unit; // pin belongs to the tested unit

            // Create a global net label only if there are no other wires/pins attached
            if( pinInUnit && !checkConnections( aComponent, pin ) )
            {
                // Create a net label to force the net name on the pin
                SCH_GLOBALLABEL* netLabel = new SCH_GLOBALLABEL;
                netLabel->SetPosition( aComponent->GetPinPhysicalPosition( pin ) );
                netLabel->SetText( extractNetName( pin->GetName() ) );
                netLabel->SetTextSize( wxSize( Mils2iu( 10 ), Mils2iu( 10 ) ) );
                netLabel->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
                aScreen->Append( netLabel );
            }

            else if( !pinInUnit && aUpdateSet )
            {
                // Found a pin creating implicit connection information in another unit.
                // Such units will be instantiated if they do not appear in another sheet and
                // processed later.
                wxASSERT( pin->GetUnit() );
                missingUnits.insert( pin->GetUnit() );
            }
        }
    }

    if( aUpdateSet )
    {
        auto cmpIt = m_missingCmps.find( reference );

        // Set the flag indicating this unit has been processed
        if( cmpIt != m_missingCmps.end() )
            cmpIt->second.units[unit] = false;

        // Save the units that need later processing
        else if( !missingUnits.empty() )
        {
            EAGLE_MISSING_CMP& entry = m_missingCmps[reference];
            entry.cmp                = aComponent;

            for( int i : missingUnits )
                entry.units.emplace( i, true );
        }
    }
}


wxString SCH_EAGLE_PLUGIN::fixSymbolName( const wxString& aName )
{
    wxString ret = LIB_ID::FixIllegalChars( aName, LIB_ID::ID_SCH );

    return ret;
}


wxString SCH_EAGLE_PLUGIN::translateEagleBusName( const wxString& aEagleName ) const
{
    if( SCH_CONNECTION::IsBusVectorLabel( aEagleName ) )
        return aEagleName;

    wxString ret = "{";

    wxStringTokenizer tokenizer( aEagleName, "," );

    while( tokenizer.HasMoreTokens() )
    {
        wxString member = tokenizer.GetNextToken();

        // In Eagle, overbar text is automatically stopped at the end of the net name, even when
        // that net name is part of a bus definition.  In KiCad, we don't (currently) do that, so
        // if there is an odd number of overbar markers in this net name, we need to append one
        // to close it out before appending the space.

        if( member.Freq( '!' ) % 2 > 0 )
            member << "!";

        ret << member << " ";
    }

    ret.Trim( true );
    ret << "}";

    return ret;
}
