/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors.
 * Copyright (C) 2017 CERN.
 * @author Alejandro GarcĂ­a Montoro <alejandro.garciamontoro@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 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */


#ifndef _EAGLE_PARSER_H_
#define _EAGLE_PARSER_H_

#include <errno.h>
#include <unordered_map>

#include <wx/xml/xml.h>
#include <wx/string.h>
#include <wx/filename.h>

#include <layers_id_colors_and_visibility.h>
#include <convert_to_biu.h>
#include <macros.h>
#include <trigo.h>
#include <kicad_string.h>

using std::string;

class MODULE;

typedef std::unordered_map< string,  wxXmlNode* > NODE_MAP;
typedef std::map< string, MODULE* > MODULE_MAP;


/**
 * Class XML_PARSER_ERROR
 * implements a simple wrapper around runtime_error to isolate the errors thrown by the
 * Eagle XML parser.
 */
struct XML_PARSER_ERROR : std::runtime_error
{
    /**
     * Constructor XML_PARSER_ERROR
     * build an XML error by just calling its parent class constructor, std::runtime_error, with
     * the passed message.
     * @param aMessage is an explanatory error message.
     */
    XML_PARSER_ERROR( const string& aMessage ) noexcept :
        std::runtime_error( "XML parser failed - " + aMessage )
    {}
};


/// segment (element) of our XPATH into the Eagle XML document tree in PTREE form.
struct TRIPLET
{
    const char* element;
    const char* attribute;
    const char* value;

    TRIPLET( const char* aElement, const char* aAttribute = "", const char* aValue = "" ) :
        element( aElement ),
        attribute( aAttribute ),
        value( aValue )
    {}
};


/**
 * Class XPATH
 * keeps track of what we are working on within a PTREE.
 * Then if an exception is thrown, the place within the tree that gave us
 * grief can be reported almost accurately.  To minimally impact
 * speed, merely assign const char* pointers during the tree walking
 * expedition.  The const char* pointers must be to C strings residing either in
 * the data or code segment (i.e. "compiled in") or within the XML document, but
 * not on the stack, since the stack is unwound during the throwing of the
 * exception.  The XML document will not immediately vanish since we capture
 * the xpath (using function Contents()) before the XML document tree (PTREE)
 * is destroyed.
 */
class XPATH
{
    std::vector<TRIPLET>    p;

public:
    void push( const char* aPathSegment, const char* aAttribute="" )
    {
        p.push_back( TRIPLET( aPathSegment, aAttribute ) );
    }

    void clear()    { p.clear(); }

    void pop()      { p.pop_back(); }

    /// modify the last path node's value
    void Value( const char* aValue )
    {
        p.back().value = aValue;
    }

    /// modify the last path node's attribute
    void Attribute( const char* aAttribute )
    {
        p.back().attribute = aAttribute;
    }

    /// return the contents of the XPATH as a single string
    string Contents()
    {
        typedef std::vector<TRIPLET>::const_iterator CITER_TRIPLET;

        string ret;

        for( CITER_TRIPLET it = p.begin();  it != p.end();  ++it )
        {
            if( it != p.begin() )
                ret += '.';

            ret += it->element;

            if( it->attribute[0] && it->value[0] )
            {
                ret += '[';
                ret += it->attribute;
                ret += '=';
                ret += it->value;
                ret += ']';
            }
        }

        return ret;
    }
};


/**
 * Function Convert
 * converts a wxString to a generic type T.
 * @param  aValue is a wxString containing the value that will be converted to type T.
 * @throw XML_PARSER_ERROR - an exception is thrown if the parsing fails or if the conversion to
 *        type T is unknown.
 */
template<typename T>
T Convert( const wxString& aValue )
{
    throw XML_PARSER_ERROR( "Conversion failed. Unknown type." );
}


/**
 * Class OPTIONAL_XML_ATTRIBUTE
 * models an optional XML attribute.
 * This was implemented as an alternative to boost::optional. This class should be replaced with a
 * simple typedef per type using std::optional when C++17 is published.
 */
template <class T>
class OPTIONAL_XML_ATTRIBUTE
{
private:
    /// A boolean indicating if the data is present or not.
    bool m_isAvailable;

    /// The actual data if m_isAvailable is true; otherwise, garbage.
    T m_data;

public:
    /**
     * Constructor OPTIONAL_XML_ATTRIBUTE
     * construct a default OPTIONAL_XML_ATTRIBUTE, whose data is not available.
     */
    OPTIONAL_XML_ATTRIBUTE() :
	   m_isAvailable( false )
    {}

    /**
     * Constructor OPTIONAL_XML_ATTRIBUTE
     * @param  aData is a wxString containing the value that should be converted to type T. If
     *               aData is empty, the attribute is understood as unavailable; otherwise, the
     *               conversion to T is tried.
     */
    OPTIONAL_XML_ATTRIBUTE( const wxString& aData )
    {
        m_isAvailable = !aData.IsEmpty();

        if( m_isAvailable )
            Set( aData );
    }

    /**
     * Constructor OPTIONAL_XML_ATTRIBUTE
     * @param  aData is the value of the XML attribute. If this constructor is called, the
     *               attribute is available.
     */
    OPTIONAL_XML_ATTRIBUTE( T aData ) :
        m_isAvailable( true ),
        m_data( aData )
    {}

    /**
     * Operator bool
     * @return bool - the availability of the attribute.
     */
    operator bool() const
    {
        return m_isAvailable;
    }

    /**
     * Assignment operator
     * to a string (optionally) containing the data.
     * @param aData is a wxString that should be converted to T. If the string is empty, the
     *              attribute is set to unavailable.
     */
    OPTIONAL_XML_ATTRIBUTE<T>& operator =( const wxString& aData )
    {
        m_isAvailable = !aData.IsEmpty();

        if( m_isAvailable )
            Set( aData );

        return *this;
    }

    /**
     * Assignment operator
     * to an object of the base type containing the data.
     * @param aData is the actual value of the attribute. Calling this assignment, the attribute
     *              is automatically made available.
     */
    OPTIONAL_XML_ATTRIBUTE<T>& operator =( T aData )
    {
        m_data = aData;
        m_isAvailable = true;

        return *this;
    }

    /**
     * Equal operator
     * to an object of the base type.
     * @param aOther is the object of the base type that should be compared with this one.
     */
    bool operator ==( const T& aOther ) const
    {
        return m_isAvailable && ( aOther == m_data );
    }

    /**
     * Function Set
     * tries to convert a string to the base type.
     * @param aString is the string that will be converted to the base type.
     */
    void Set( const wxString& aString )
    {
        m_data = Convert<T>( aString );
    }

    /**
     * Function Get
     * returns a reference to the value of the attribute assuming it is available.
     * @return T& - the value of the attribute.
     */
    T& Get()
    {
        assert( m_isAvailable );
        return m_data;
    }

    /**
     * Function CGet
     * returns a constant reference to the value of the attribute assuming it is available.
     * @return const T& - the value of the attribute.
     */
    const T& CGet() const
    {
        assert( m_isAvailable );
        return m_data;
    }

    /**
     * Operator *
     * returns a reference to the value of the attribute assuming it is available.
     * @return T& - the value of the attribute.
     */
    T& operator*()
    {
        return Get();
    }

    /**
     * Operator *
     * returns a constant reference to the value of the attribute assuming it is available.
     * @return const T& - the value of the attribute.
     */
    const T& operator*() const
    {
        return CGet();
    }

    /**
     * Operator ->
     * returns a pointer to the value of the attribute assuming it is available.
     * @return T* - the value of the attribute.
     */
    T* operator->()
    {
        return &Get();
    }

    /**
     * Operator ->
     * returns a constant pointer to the value of the attribute assuming it is available.
     * @return const T* - the value of the attribute.
     */
    const T* operator->() const
    {
        return &CGet();
    }
};

// Pre-declare for typedefs
struct EROT;

typedef OPTIONAL_XML_ATTRIBUTE<string>  opt_string;
typedef OPTIONAL_XML_ATTRIBUTE<int>     opt_int;
typedef OPTIONAL_XML_ATTRIBUTE<double>  opt_double;
typedef OPTIONAL_XML_ATTRIBUTE<bool>    opt_bool;
typedef OPTIONAL_XML_ATTRIBUTE<EROT>    opt_erot;


// All of the 'E'STRUCTS below merely hold Eagle XML information verbatim, in binary.
// For maintenance and troubleshooting purposes, it was thought that we'd need to
// separate the conversion process into distinct steps. There is no intent to have KiCad
// forms of information in these 'E'STRUCTS.  They are only binary forms
// of the Eagle information in the corresponding Eagle XML nodes.


/// Eagle net
struct ENET
{
    int         netcode;
    std::string netname;

    ENET( int aNetCode, const std::string& aNetName ) :
        netcode( aNetCode ),
        netname( aNetName )
    {}

    ENET() :
        netcode( 0 )
    {}
};


/// Eagle rotation
struct EROT
{
    bool    mirror;
    bool    spin;
    double  degrees;

    EROT() :
        mirror( false ),
        spin( false ),
        degrees( 0 )
    {}

    EROT( double aDegrees ) :
        mirror( false ),
        spin( false ),
        degrees( aDegrees )
    {}
};


/// Eagle wire
struct EWIRE
{
    double     x1;
    double     y1;
    double     x2;
    double     y2;
    double     width;
    LAYER_NUM  layer;

    // for style: (continuous | longdash | shortdash | dashdot)
    enum {
        CONTINUOUS,
        LONGDASH,
        SHORTDASH,
        DASHDOT,
    };
    opt_int     style;
    opt_double  curve;      ///< range is -359.9..359.9

    // for cap: (flat | round)
    enum {
        FLAT,
        ROUND,
    };
    opt_int     cap;

    EWIRE( wxXmlNode* aWire );
};


/// Eagle via
struct EVIA
{
    double     x;
    double     y;
    int        layer_front_most;   /// < extent
    int        layer_back_most;    /// < inclusive
    double     drill;
    opt_double diam;
    opt_string shape;

    EVIA( wxXmlNode* aVia );
};


/// Eagle circle
struct ECIRCLE
{
    double    x;
    double    y;
    double    radius;
    double    width;
    LAYER_NUM layer;

    ECIRCLE( wxXmlNode* aCircle );
};


/// Eagle XML rectangle in binary
struct ERECT
{
    double   x1;
    double   y1;
    double   x2;
    double   y2;
    int      layer;
    opt_erot rot;

    ERECT( wxXmlNode* aRect );
};


/**
 * Class EATTR
 * parses an Eagle "attribute" XML element.  Note that an attribute element
 * is different than an XML element attribute.  The attribute element is a
 * full XML node in and of itself, and has attributes of its own.  Blame Eagle.
 */
struct EATTR
{
    string     name;
    opt_string value;
    opt_double x;
    opt_double y;
    opt_double size;
    opt_int    layer;
    opt_double ratio;
    opt_erot   rot;

    enum {  // for 'display'
        Off,
        VALUE,
        NAME,
        BOTH,
    };
    opt_int     display;

    EATTR( wxXmlNode* aTree );
    EATTR() {}
};


/// Eagle dimension element
struct EDIMENSION
{
    double x1;
    double y1;
    double x2;
    double y2;
    double x3;
    double y3;
    int    layer;

    opt_string dimensionType;

    EDIMENSION( wxXmlNode* aDimension );
};


/// Eagle text element
struct ETEXT
{
    string     text;
    double     x;
    double     y;
    double     size;
    int        layer;
    opt_string font;
    opt_double ratio;
    opt_erot   rot;

    enum {          // for align
        CENTER,
        CENTER_LEFT,
        TOP_CENTER,
        TOP_LEFT,
        TOP_RIGHT,

        // opposites are -1 x above, used by code tricks in here
        CENTER_RIGHT  = -CENTER_LEFT,
        BOTTOM_CENTER = -TOP_CENTER,
        BOTTOM_LEFT   = -TOP_RIGHT,
        BOTTOM_RIGHT  = -TOP_LEFT,
    };

    opt_int     align;

    ETEXT( wxXmlNode* aText );
};


/// Eagle thru hol pad
struct EPAD
{
    string     name;
    double     x;
    double     y;
    double     drill;
    opt_double diameter;

    // for shape: (square | round | octagon | long | offset)
    enum {
        SQUARE,
        ROUND,
        OCTAGON,
        LONG,
        OFFSET,
    };
    opt_int  shape;
    opt_erot rot;
    opt_bool stop;
    opt_bool thermals;
    opt_bool first;

    EPAD( wxXmlNode* aPad );
};


/// Eagle SMD pad
struct ESMD
{
    string   name;
    double   x;
    double   y;
    double   dx;
    double   dy;
    int      layer;
    opt_int  roundness;
    opt_erot rot;
    opt_bool stop;
    opt_bool thermals;
    opt_bool cream;

    ESMD( wxXmlNode* aSMD );
};


/// Eagle vertex
struct EVERTEX
{
    double      x;
    double      y;

    EVERTEX( wxXmlNode* aVertex );
};


/// Eagle polygon, without vertices which are parsed as needed
struct EPOLYGON
{
    double     width;
    int        layer;
    opt_double spacing;

    // KiCad priority is opposite of Eagle rank, that is:
    //  - Eagle Low rank drawn first
    //  - KiCad high priority drawn first
    // So since Eagle has an upper limit we define this, used for the cases
    // where no rank is specified.
    static const int    max_priority = 6;

    enum {      // for pour
        SOLID,
        HATCH,
        CUTOUT,
    };
    int        pour;
    opt_double isolate;
    opt_bool   orphans;
    opt_bool   thermals;
    opt_int    rank;

    EPOLYGON( wxXmlNode* aPolygon );
};


/// Eagle hole element
struct EHOLE
{
    double x;
    double y;
    double drill;

    EHOLE( wxXmlNode* aHole );
};


/// Eagle element element
struct EELEMENT
{
    string   name;
    string   library;
    string   package;
    string   value;
    double   x;
    double   y;
    opt_bool locked;
    opt_bool smashed;
    opt_erot rot;

    EELEMENT( wxXmlNode* aElement );
};


struct ELAYER
{
    int      number;
    string   name;
    int      color;
    int      fill;
    opt_bool visible;
    opt_bool active;

    ELAYER( wxXmlNode* aLayer );
};


struct EAGLE_LAYER
{
    enum
    {
        TOP         = 1,
        ROUTE2      = 2,
        ROUTE3      = 3,
        ROUTE4      = 4,
        ROUTE5      = 5,
        ROUTE6      = 6,
        ROUTE7      = 7,
        ROUTE8      = 8,
        ROUTE9      = 9,
        ROUTE10     = 10,
        ROUTE11     = 11,
        ROUTE12     = 12,
        ROUTE13     = 13,
        ROUTE14     = 14,
        ROUTE15     = 15,
        BOTTOM      = 16,
        PADS        = 17,
        VIAS        = 18,
        UNROUTED    = 19,
        DIMENSION   = 20,
        TPLACE      = 21,
        BPLACE      = 22,
        TORIGINS    = 23,
        BORIGINS    = 24,
        TNAMES      = 25,
        BNAMES      = 26,
        TVALUES     = 27,
        BVALUES     = 28,
        TSTOP       = 29,
        BSTOP       = 30,
        TCREAM      = 31,
        BCREAM      = 32,
        TFINISH     = 33,
        BFINISH     = 34,
        TGLUE       = 35,
        BGLUE       = 36,
        TTEST       = 37,
        BTEST       = 38,
        TKEEPOUT    = 39,
        BKEEPOUT    = 40,
        TRESTRICT   = 41,
        BRESTRICT   = 42,
        VRESTRICT   = 43,
        DRILLS      = 44,
        HOLES       = 45,
        MILLING     = 46,
        MEASURES    = 47,
        DOCUMENT    = 48,
        REFERENCELC = 49,
        REFERENCELS = 50,
        TDOCU       = 51,
        BDOCU       = 52,
        NETS        = 91,
        BUSSES      = 92,
        PINS        = 93,
        SYMBOLS     = 94,
        NAMES       = 95,
        VALUES      = 96,
        INFO        = 97,
        GUIDE       = 98,
        USERLAYER1  = 160,
        USERLAYER2  = 161
    };
};

/**
 * Function MapChildren
 * provides an easy access to the children of an XML node via their names.
 * @param  currentNode is a pointer to a wxXmlNode, whose children will be mapped.
 * @return NODE_MAP - a map linking the name of each children to the children itself (via a
 *                  wxXmlNode*)
 */
NODE_MAP MapChildren( wxXmlNode* currentNode );

/// Assemble a two part key as a simple concatenation of aFirst and aSecond parts,
/// using a separator.
string makeKey( const string& aFirst, const string& aSecond );

/// Make a unique time stamp
unsigned long timeStamp( wxXmlNode* aTree );

/// Convert an Eagle curve end to a KiCad center for S_ARC
wxPoint kicad_arc_center( const wxPoint& aStart, const wxPoint& aEnd, double aAngle );

#endif // _EAGLE_PARSER_H_