/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2020-2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
 * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 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/>.
 */

/**
 * @file cadstar_pcb_archive_parser.cpp
 * @brief Reads in a CADSTAR Schematic Archive (*.csa) file
 */

#ifndef CADSTAR_SCH_ARCHIVE_PARSER_H_
#define CADSTAR_SCH_ARCHIVE_PARSER_H_

#include <plugins/cadstar/cadstar_archive_parser.h>


/**
 * @brief Represents a CADSTAR Schematic Archive (*.csa) file
 */
class CADSTAR_SCH_ARCHIVE_PARSER : public CADSTAR_ARCHIVE_PARSER
{
public:
    explicit CADSTAR_SCH_ARCHIVE_PARSER( wxString aFilename ) :
            CADSTAR_ARCHIVE_PARSER(), Filename( aFilename ), Header(), Assignments(),
            KiCadUnitDivider( 10 ), m_rootNode( nullptr )
    {
    }

    virtual ~CADSTAR_SCH_ARCHIVE_PARSER()
    {
        if( m_rootNode )
            delete m_rootNode;
    }

    /**
     * @brief Parses the file
     * @throw IO_ERROR if file could not be opened or there was
     * an error while parsing
     */
    void Parse();


    typedef wxString TERMINALCODE_ID;
    typedef wxString SYMBOL_ID;
    typedef wxString BUS_ID;
    typedef wxString BLOCK_ID;
    typedef wxString SHEET_NAME;


    enum class TERMINAL_SHAPE_TYPE
    {
        ANNULUS,
        BOX,
        BULLET,
        CIRCLE, ///< Keyword "ROUND"
        CROSS,
        DIAMOND,
        FINGER,
        OCTAGON,
        PLUS,
        POINTER,
        RECTANGLE,
        ROUNDED_RECT, ///< Keyword "ROUNDED"
        SQUARE,
        STAR,
        TRIANGLE,
        UNDEFINED ///< Only used for error checking (not a real shape)
    };


    static TERMINAL_SHAPE_TYPE ParseTermShapeType( const wxString& aShapeStr );


    struct TERMINAL_SHAPE : PARSER
    {
        TERMINAL_SHAPE_TYPE ShapeType;
        long                Size            = UNDEFINED_VALUE;
        // Note in the CADSTAR GUI, it only talks about "length", but the file seems to
        // split it in "left length" and "right length" (similar to PADCODE in the PCB)
        // for some terminal shapes such as RECTANGLE but not for others, such as TRIANGLE
        long                LeftLength      = UNDEFINED_VALUE; ///< Might also be total length
        long                RightLength     = UNDEFINED_VALUE; ///< Could be blank
        long                InternalFeature = UNDEFINED_VALUE;
        long                OrientAngle     = 0; ///< 1/1000 of a Degree

        static bool IsTermShape( XNODE* aNode );
        void        Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct TERMINALCODE : PARSER
    {
        TERMINALCODE_ID ID;
        wxString        Name;
        TERMINAL_SHAPE  Shape;
        bool            Filled = false;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct CODEDEFS_SCM : CADSTAR_ARCHIVE_PARSER::CODEDEFS
    {
        std::map<TERMINALCODE_ID, TERMINALCODE> TerminalCodes;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct ASSIGNMENTS_SCM : PARSER
    {
        CODEDEFS_SCM Codedefs;
        GRIDS        Grids;
        SETTINGS     Settings;
        bool         NetclassEditAttributeSettings     = false; //< Unclear what this does
        bool         SpacingclassEditAttributeSettings = false; //< Unclear what this does

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct TERMINAL : PARSER
    {
        TERMINAL_ID     ID;
        TERMINALCODE_ID TerminalCodeID;
        POINT           Position; ///< Pad position within the component's coordinate frame.
        long            OrientAngle = 0;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct PIN_NUM_LABEL_LOC : CADSTAR_ARCHIVE_PARSER::ATTRIBUTE_LOCATION
    {
        TERMINAL_ID TerminalID;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct SYMDEF_SCM : CADSTAR_ARCHIVE_PARSER::SYMDEF
    {
        std::map<TERMINAL_ID, TERMINAL>          Terminals;
        std::map<TERMINAL_ID, PIN_NUM_LABEL_LOC> PinLabelLocations;
        std::map<TERMINAL_ID, PIN_NUM_LABEL_LOC> PinNumberLocations;


        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct LIBRARY_SCM : PARSER
    {
        std::map<SYMDEF_ID, SYMDEF_SCM> SymbolDefinitions;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct SHEETS : PARSER
    {
        std::map<LAYER_ID, SHEET_NAME> SheetNames;
        std::vector<LAYER_ID>          SheetOrder; ///< A vector to also store the order in which
                                                   ///< sheets are to be displayed

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct COMP : PARSER
    {
        wxString           Designator  = wxEmptyString;
        bool               ReadOnly    = false;
        bool               HasLocation = false;
        ATTRIBUTE_LOCATION AttrLoc;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct PARTREF : PARSER
    {
        PART_ID            RefID       = wxEmptyString;
        bool               ReadOnly    = false;
        bool               HasLocation = false;
        ATTRIBUTE_LOCATION AttrLoc;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct TERMATTR : PARSER
    {
        TERMINAL_ID                  TerminalID;
        std::vector<ATTRIBUTE_VALUE> Attributes;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct SYMPINNAME_LABEL : PARSER
    {
        TERMINAL_ID        TerminalID;
        wxString           NameOrLabel;
        bool               HasLocation = false;
        ATTRIBUTE_LOCATION AttrLoc;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct SYMBOLVARIANT : PARSER
    {
        enum class TYPE
        {
            GLOBALSIGNAL,
            SIGNALREF,
            TESTPOINT
            //TODO: there might be others
        };

        TYPE     Type;
        wxString Reference = wxEmptyString;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct SIGNALREFERENCELINK : CADSTAR_ARCHIVE_PARSER::ATTRIBUTE_LOCATION
    {
        wxString Text; ///< This contains the numbers of the other sheets where the
                       ///< signal reference is present separated by commas

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct SYMBOL : PARSER
    {
        struct PIN_NUM : PARSER
        {
            TERMINAL_ID        TerminalID;
            long               PinNum;
            bool               HasLocation = false;
            ATTRIBUTE_LOCATION AttrLoc;

            void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
        };

        SYMBOL_ID     ID;
        SYMDEF_ID     SymdefID;
        LAYER_ID      LayerID; ///< Sheet on which symbol is located
        POINT         Origin;
        GROUP_ID      GroupID = wxEmptyString; ///< If not empty, this symbol is part of a group
        REUSEBLOCKREF ReuseBlockRef;
        long          OrientAngle = 0;
        bool          Mirror      = false;
        bool          Fixed       = false;
        long          ScaleRatioNumerator = 1; ///< Symbols can be arbitrarily scaled in CADSTAR
        long          ScaleRatioDenominator = 1;
        READABILITY   Readability = READABILITY::BOTTOM_TO_TOP;

        bool    IsComponent = false;
        COMP    ComponentRef;
        bool    HasPartRef = false;
        PARTREF PartRef;
        bool    PartNameVisible = true;
        GATE_ID GateID; ///< The gate this symbol represents within the associated Part

        bool                IsSymbolVariant = false;
        SYMBOLVARIANT       SymbolVariant;
        SIGNALREFERENCELINK SigRefLink; ///< Signal References (a special form of global signal)
                                        ///< have annotations showing the location of all the
                                        ///< other sheets where the signal is present

        SYMBOL_ID  VariantParentSymbolID = wxEmptyString;
        VARIANT_ID VariantID             = wxEmptyString;

        std::map<TERMINAL_ID, TERMATTR>         TerminalAttributes;
        std::map<TERMINAL_ID, SYMPINNAME_LABEL> PinLabels;  ///< Equivalent to KiCad's Pin Name
        std::map<TERMINAL_ID, SYMPINNAME_LABEL> PinNames;   ///< Identifier of the pin in the PCB
                                                            ///< Equivalent to KiCad's Pin Number
        std::map<TERMINAL_ID, PIN_NUM>          PinNumbers; ///< This seems to only appear in older
                                                            ///< designs and is similar to PinNames
                                                            ///< but only allowing numerical values
        std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE> AttributeValues;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    /**
     * @brief Net name or bus name label
     */
    struct SIGLOC : CADSTAR_ARCHIVE_PARSER::ATTRIBUTE_LOCATION
    {
        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct BUS : PARSER
    {
        BUS_ID      ID;
        LINECODE_ID LineCodeID;
        LAYER_ID    LayerID; ///< Sheet on which bus is located
        SHAPE       Shape;
        wxString    Name        = wxEmptyString;
        bool        HasBusLabel = false;
        SIGLOC      BusLabel;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct BLOCK : PARSER
    {
        enum class TYPE
        {
            CLONE, ///< the block is referring to the sheet it is on.
            PARENT,
            CHILD
        };

        BLOCK_ID ID;
        TYPE     Type; ///< Determines what the associated layer is, whether parent, child or clone
        LAYER_ID LayerID = wxEmptyString; ///< The sheet block is on (TODO: verify this is true)
        LAYER_ID AssocLayerID  = wxEmptyString; ///< Parent or Child linked sheet
        wxString Name          = wxEmptyString;
        bool     HasBlockLabel = false;
        ATTRIBUTE_LOCATION BlockLabel;

        std::map<TERMINAL_ID, TERMINAL> Terminals;
        std::map<FIGURE_ID, FIGURE>     Figures;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct NET_SCH : CADSTAR_ARCHIVE_PARSER::NET
    {
        struct JUNCTION_SCH : CADSTAR_ARCHIVE_PARSER::NET::JUNCTION ///< "JPT" nodename.
        {
            TERMINALCODE_ID TerminalCodeID; ///< Usually a circle, but size can be varied
            bool            HasNetLabel = false;
            SIGLOC          NetLabel;

            void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
        };

        struct SYM_TERM : PARSER ///< "TERM" nodename (represents a pin in a SCH symbol)
        {
            NETELEMENT_ID ID; ///< First character is "P"
            SYMBOL_ID     SymbolID;
            TERMINAL_ID   TerminalID;
            bool          HasNetLabel = false;
            SIGLOC        NetLabel;

            void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
        };

        struct BUS_TERM : PARSER ///< "BUSTERM" nodename (represents a connection to a bus)
        {
            NETELEMENT_ID ID; ///< First two characters "BT"
            BUS_ID        BusID;
            POINT         FirstPoint;  ///< Point on the bus itself
            POINT         SecondPoint; ///< Start point for any wires
            bool          HasNetLabel = false;
            SIGLOC        NetLabel;

            void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
        };

        struct BLOCK_TERM : PARSER ///< "BLOCKTERM" nodename (represents a connection to a block)
        {
            NETELEMENT_ID ID; ///< First four characters "BLKT"
            BLOCK_ID      BlockID;
            TERMINAL_ID   TerminalID;
            bool          HasNetLabel = false;
            SIGLOC        NetLabel;

            void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
        };


        struct DANGLER : PARSER ///< "DANGLER" nodename (represents a dangling wire)
        {
            NETELEMENT_ID   ID; ///< First character "D"
            TERMINALCODE_ID TerminalCodeID;
            LAYER_ID        LayerID;
            POINT           Position;
            bool            HasNetLabel = false;
            SIGLOC          NetLabel;

            void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
        };

        struct CONNECTION_SCH : CADSTAR_ARCHIVE_PARSER::NET::CONNECTION ///< "CONN" nodename
        {
            LAYER_ID           LayerID; ///< Sheet on which the connection is drawn
            std::vector<POINT> Path;
            GROUP_ID           GroupID = wxEmptyString;
            REUSEBLOCKREF      ReuseBlockRef;
            LINECODE_ID        ConnectionLineCode;

            void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
        };

        std::map<NETELEMENT_ID, JUNCTION_SCH> Junctions;
        std::map<NETELEMENT_ID, SYM_TERM>     Terminals;
        std::map<NETELEMENT_ID, BUS_TERM>     BusTerminals;
        std::map<NETELEMENT_ID, BLOCK_TERM>   BlockTerminals;
        std::map<NETELEMENT_ID, DANGLER>      Danglers;
        std::vector<CONNECTION_SCH>           Connections;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    struct CADSTAR_SCHEMATIC : PARSER
    {
        std::map<GROUP_ID, GROUP>                               Groups;
        std::map<REUSEBLOCK_ID, REUSEBLOCK>                     ReuseBlocks;
        std::map<FIGURE_ID, FIGURE>                             Figures;
        std::map<SYMBOL_ID, SYMBOL>                             Symbols;
        std::map<BUS_ID, BUS>                                   Buses;
        std::map<BLOCK_ID, BLOCK>                               Blocks;
        std::map<NET_ID, NET_SCH>                               Nets;
        std::map<TEXT_ID, TEXT>                                 Texts;
        std::map<DOCUMENTATION_SYMBOL_ID, DOCUMENTATION_SYMBOL> DocumentationSymbols;
        VARIANT_HIERARCHY                                       VariantHierarchy;
        std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE>                 AttributeValues;

        void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
    };


    wxString          Filename;
    HEADER            Header;
    ASSIGNMENTS_SCM   Assignments;
    LIBRARY_SCM       Library;
    PARTS             Parts;
    SHEETS            Sheets;
    CADSTAR_SCHEMATIC Schematic;
    ATTRCOLORS        AttrColors;
    PARTNAMECOL       SymbolPartNameColor;

    int KiCadUnitDivider; ///<Use this value to convert units in this CSA file to KiCad units

private:
    XNODE* m_rootNode; // Currently parsed root node

}; //CADSTAR_SCH_ARCHIVE_PARSER

#endif // CADSTAR_SCH_ARCHIVE_PARSER_H_
