/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2023 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
 * Copyright The 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/>.
 */

#include <iostream>

#include "cadstar_parts_lib_parser.h"
#include "cadstar_parts_lib_grammar.h"

#include <fmt.h>
#include <set>
#include <string>

using namespace CADSTAR_PARTS_LIB;


/**
 * Struture that will be populated by the PEGTL parser
 */
struct CADSTAR_LIB_PARSER_STATE
{
    std::string                   m_CurrentString;
    std::string                   m_CurrentAttrName;
    std::string                   m_CurrentSignalName;
    long                          m_CurrentLong = 0;
    CADSTAR_PART_NODE             m_CurrentNode;
    long                          m_CurrentNodeIdx = 0;
    std::vector<long>             m_CurrentPinEquivalenceGroup;
    std::set<std::string>         m_CurrentElementsParsed;
    bool                          m_ReadOnly = false;
    CADSTAR_SWAP_GROUP            m_CurrentSwapGroup;
    CADSTAR_PART_PIN              m_CurrentPin;
    std::vector<CADSTAR_PART_PIN> m_CurrentPinList;
    CADSTAR_PART_SYMBOL_ENTRY     m_CurrentSymbol;
    CADSTAR_PART_ENTRY            m_CurrentPart;

    CADSTAR_PARTS_LIB_MODEL m_ParsedModel;
};


// Default action: Do nothing
template <typename Rule>
struct CADSTAR_LIB_PARSER_ACTION : tao::pegtl::nothing<Rule>
{
};


long helperStringToLong( std::string aString )
{
    std::stringstream ss( aString );
    long              number;
    ss >> number;
    return number;
};


//
// CONTENT TO NUMBER ACTIONS:
// Take the current content string, convert it to a long and store it in StateVariable
//
#define DEFINE_CONTENT_TO_NUMBER_ACTION( Rule, StateVariable )                                   \
template <>                                                                                      \
struct CADSTAR_LIB_PARSER_ACTION<Rule>                                                           \
{                                                                                                \
    template <typename ActionInput>                                                              \
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )                      \
    {                                                                                            \
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );                          \
        s.StateVariable = helperStringToLong( in.string() );                                     \
    }                                                                                            \
}

DEFINE_CONTENT_TO_NUMBER_ACTION( CURRENT_FORMAT_NUMBER, m_ParsedModel.m_FormatNumber );
DEFINE_CONTENT_TO_NUMBER_ACTION( PINNUM,                m_CurrentLong );
DEFINE_CONTENT_TO_NUMBER_ACTION( MAX_PIN_COUNT,         m_CurrentPart.m_MaxPinCount );
DEFINE_CONTENT_TO_NUMBER_ACTION( PIN_IDENTIFIER,        m_CurrentPin.m_Identifier );
DEFINE_CONTENT_TO_NUMBER_ACTION( PIN_LOADING,           m_CurrentPin.m_Loading );
DEFINE_CONTENT_TO_NUMBER_ACTION( HIERARCHY_NODE_INDEX,  m_CurrentLong );


// unfortunately the one below needs to be defined separately
template <>
struct CADSTAR_LIB_PARSER_ACTION<EQUIVALENT_PIN>
{
    template <typename ActionInput>
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
        s.m_CurrentPinEquivalenceGroup.push_back( helperStringToLong( in.string() ) );
    }
};


//
// CONTENT TO CURRENT STRING ACTIONS:
// Take the current content string, store it in the state current string
//
#define DEFINE_CONTENT_TO_STRING_ACTION( Rule )                                                  \
template <>                                                                                      \
struct CADSTAR_LIB_PARSER_ACTION<Rule>                                                           \
{                                                                                                \
    template <typename ActionInput>                                                              \
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )                      \
    {                                                                                            \
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );                          \
        s.m_CurrentString = in.string();                                                         \
    }                                                                                            \
}

DEFINE_CONTENT_TO_STRING_ACTION( PINNAME );

//
// STRING REPLACEMENT ACTIONS:
// Take the current string in the parser state and store it in StateVariable
//
#define DEFINE_STRING_ACTION( Rule, StateVariable )                                              \
template <>                                                                                      \
struct CADSTAR_LIB_PARSER_ACTION<Rule>                                                           \
{                                                                                                \
    /* @todo : convert to use apply0 to improve performance( once fully tested ) */              \
    template <typename ActionInput>                                                              \
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )                      \
    {                                                                                            \
        assert( in.string().size() >= s.m_CurrentString.size() );                                \
        s.StateVariable = s.m_CurrentString;                                                     \
        s.m_CurrentString = "";                                                                  \
    }                                                                                            \
}                                                                                                \

DEFINE_STRING_ACTION( PART_NAME,                m_CurrentPart.m_Name );
DEFINE_STRING_ACTION( PART_VERSION,             m_CurrentPart.m_Version );
DEFINE_STRING_ACTION( PART_NUMBER,              m_CurrentPart.m_Number );
DEFINE_STRING_ACTION( PART_DESCRIPTION,         m_CurrentPart.m_Description );
DEFINE_STRING_ACTION( PCB_COMPONENT,            m_CurrentPart.m_Pcb_component );
DEFINE_STRING_ACTION( PCB_ALTERNATE,            m_CurrentPart.m_Pcb_alternate );
DEFINE_STRING_ACTION( VALUE,                    m_CurrentPart.m_Value );
DEFINE_STRING_ACTION( DEFINITION_NAME,          m_CurrentPart.m_PartDefinitionName );
DEFINE_STRING_ACTION( STEM,                     m_CurrentPart.m_ComponentStem );
DEFINE_STRING_ACTION( SYM_ELEMENT_NAME,         m_CurrentSwapGroup.m_Name );
DEFINE_STRING_ACTION( USER_PART_ATTRIBUTE_NAME, m_CurrentAttrName );
DEFINE_STRING_ACTION( ATTRIBUTE_NAME,           m_CurrentAttrName );
DEFINE_STRING_ACTION( PIN_SIGNAL_NAME,          m_CurrentSignalName );
DEFINE_STRING_ACTION( ACCEPTANCE_PART_NAME,     m_CurrentPart.m_AcceptancePartName );
DEFINE_STRING_ACTION( ACCEPTANCE_TEXT,          m_CurrentPart.m_AcceptanceText );
DEFINE_STRING_ACTION( SPICE_PART_NAME,          m_CurrentPart.m_SpicePartName );
DEFINE_STRING_ACTION( SPICE_MODEL,              m_CurrentPart.m_SpiceModel );
DEFINE_STRING_ACTION( SCH_NAME,                 m_CurrentSymbol.m_SymbolName );
DEFINE_STRING_ACTION( SCH_ALTERNATE,            m_CurrentSymbol.m_SymbolAlternateName );


// STRING SEGMENT action
// Any strings we match, append to the current state string (the state string gets
// reset after we extract the string to store somewhere else).
// The reason we append is because in the fileformat, there can be line continuations,
// which we don't want to have in the final string - saves post-processing.
template <typename... EXCLUSION_RULES>
struct CADSTAR_LIB_PARSER_ACTION<STR_SEGMENT_EXCLUDING<EXCLUSION_RULES...>>
{
    template <typename ActionInput>
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
    {
        s.m_CurrentString += in.string();
    }
};


//
// HIERARCHY actions
//
template <>
struct CADSTAR_LIB_PARSER_ACTION<HIERARCHY_NODE_ENTRY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
        s.m_ParsedModel.m_HierarchyNodes.insert(
                { s.m_CurrentNodeIdx, std::move( s.m_CurrentNode ) } );
        s.m_CurrentNode = CADSTAR_PART_NODE();
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<HIERARCHY_CURRENT_NODE>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
        s.m_CurrentNodeIdx = s.m_CurrentLong;
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<HIERARCHY_PARENT_NODE>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
        s.m_CurrentNode.m_ParentNodeIdx = s.m_CurrentLong;
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<HIERARCHY_NODE_NAME>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentAttrName == "" );
        s.m_CurrentNode.m_Name = std::move( s.m_CurrentString );
        s.m_CurrentString = "";
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<HIERARCHY_PART_NAME>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentAttrName == "" );
        s.m_CurrentNode.m_PartNames.push_back( std::move( s.m_CurrentString ) );
        s.m_CurrentString = "";
    }
};


// PART_ENTRY action
// We just push the part to the vector of parts in our state
template <>
struct CADSTAR_LIB_PARSER_ACTION<PART_ENTRY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
        //Finish the entry
        s.m_ParsedModel.m_PartEntries.push_back( s.m_CurrentPart );
        s.m_CurrentPart = CADSTAR_PART_ENTRY();
        s.m_CurrentElementsParsed.clear();
        // Todo-we could add progress reporting here?
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<READONLY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
        s.m_ReadOnly = true;
    }
};


//
// SINGLE RULE ACTIONS:
// Make sure that this rule is only matched once per part and throw a parse error
// when this is not the case.
//
#define DECLARE_SINGLE_MATCH_RULE( Rule, ExtraCode )                                             \
template <>                                                                                      \
struct CADSTAR_LIB_PARSER_ACTION<Rule>                                                           \
{                                                                                                \
    template <typename ActionInput>                                                              \
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )                      \
    {                                                                                            \
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );                          \
                                                                                                 \
        if( s.m_CurrentElementsParsed.count( #Rule ) )                                           \
        {                                                                                        \
            throw parse_error( #Rule                                                             \
                               " was already defined for this part!",                            \
                    in );                                                                        \
        }                                                                                        \
                                                                                                 \
        s.m_CurrentElementsParsed.insert( #Rule );                                               \
        ExtraCode;                                                                               \
    }                                                                                            \
}                                                                                                \

DECLARE_SINGLE_MATCH_RULE( PART_VALUE, );
DECLARE_SINGLE_MATCH_RULE( DFN_LINE, );
DECLARE_SINGLE_MATCH_RULE( NGS_LINE, s.m_CurrentPart.m_GateSwappingAllowed = false );
DECLARE_SINGLE_MATCH_RULE( NPV_LINE, s.m_CurrentPart.m_PinsVisible = false );
DECLARE_SINGLE_MATCH_RULE( STM_LINE, );
DECLARE_SINGLE_MATCH_RULE( MXP_LINE, );
DECLARE_SINGLE_MATCH_RULE( SPI_LINE, );
DECLARE_SINGLE_MATCH_RULE( PAC_LINE, );


template <>
struct CADSTAR_LIB_PARSER_ACTION<PINNAME_ENTRY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentAttrName == "" );
        // m_CurrentLong should have been parsed as part of the PINNUM action
        // m_CurrentString should have been parsed as part of the PINNAME action
        s.m_CurrentPart.m_PinNamesMap.insert( { s.m_CurrentLong, s.m_CurrentString } );
        s.m_CurrentString = "";
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<PINLABEL_ENTRY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        assert( s.m_CurrentAttrName == "" );
        // m_CurrentLong should have been parsed as part of the PINNUM action
        // m_CurrentString should have been parsed as part of the PINLABEL action
        s.m_CurrentPart.m_PinLabelsMap.insert( { s.m_CurrentLong, s.m_CurrentString } );
        s.m_CurrentString = "";
    }
};

//
// PIN EQUIVALENCE GROUP ACTIONS:
// Take the current m_CurrentPinEquivalenceGroup in the parser state and store it in StateVariable
// then clear m_CurrentPinEquivalenceGroup.
// Note that m_CurrentPinEquivalenceGroup should have been parsed as part of EQUIVALENT_PIN action
//
#define DEFINE_PIN_GROUP_ACTION( Rule, StateVariable )                                           \
template <>                                                                                      \
struct CADSTAR_LIB_PARSER_ACTION<Rule>                                                           \
{                                                                                                \
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )                                            \
    {                                                                                            \
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );                          \
        s.StateVariable.push_back( s.m_CurrentPinEquivalenceGroup );                             \
        s.m_CurrentPinEquivalenceGroup.clear();                                                  \
    }                                                                                            \
}                                                                                                \

DEFINE_PIN_GROUP_ACTION( EQUIVALENT_PINS_GROUP, m_CurrentPart.m_PinEquivalences );
DEFINE_PIN_GROUP_ACTION( INTERNAL_SWAP_GATE,    m_CurrentSwapGroup.m_Gates );
DEFINE_PIN_GROUP_ACTION( EXTERNAL_SWAP_GATE,    m_CurrentSwapGroup.m_Gates );


//
// SWAP GROUP ACTIONS:
// Take the current m_CurrentSwapGroup in the parser state and store it in StateVariable
// then reset m_CurrentSwapGroup.
//
#define DEFINE_SWAP_GROUP_ACTION( Rule, StateVariable )                                          \
template <>                                                                                      \
struct CADSTAR_LIB_PARSER_ACTION<Rule>                                                           \
{                                                                                                \
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )                                            \
    {                                                                                            \
        assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );                          \
        s.StateVariable.push_back( s.m_CurrentSwapGroup );                                       \
        s.m_CurrentSwapGroup = CADSTAR_SWAP_GROUP();                                             \
    }                                                                                            \
}                                                                                                \

DEFINE_SWAP_GROUP_ACTION( INTERNAL_SWAP_GROUP, m_CurrentPart.m_InternalSwapGroup );
DEFINE_SWAP_GROUP_ACTION( EXTERNAL_SWAP_GROUP, m_CurrentPart.m_ExternalSwapGroup );


template <>
struct CADSTAR_LIB_PARSER_ACTION<USER_PART_ATTRIBUTE>
{
    template <typename ActionInput>
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
    {
        // The format allows user defined "part" attrbutes, but the ones listed here are in-built
        // with special meaning
        static const std::set<std::string> reservedWordsStarLines = { "VALUE", "PNM", "PLB", "EQU",
                                                                      "SYM",   "INT", "EXT", "DFN",
                                                                      "NGS",   "NPV", "STM", "MXP",
                                                                      "SPI",   "PAC" };

        if( s.m_CurrentPart.m_UserAttributes.count( s.m_CurrentAttrName ) )
        {
            throw parse_error( fmt::format( "Duplicate attribute name '{}'", s.m_CurrentAttrName ),
                               in );
        }

        if( reservedWordsStarLines.count( s.m_CurrentAttrName ) )
        {
            throw parse_error(
                    fmt::format(
                            "Invalid use of in-built attribute name '{}'. Either the attribute "
                            "was already defined for this part or it has an unexpected syntax.",
                            s.m_CurrentAttrName ),
                    in );
        }

        s.m_CurrentPart.m_UserAttributes.insert( { s.m_CurrentAttrName, s.m_CurrentString } );
        s.m_CurrentAttrName = "";
        s.m_CurrentString = "";
    }
};

#define DEFINE_ATTRIBUTE_ACTION( Rule, StateVariable )                                           \
template <>                                                                                      \
struct CADSTAR_LIB_PARSER_ACTION<Rule>                                                           \
{                                                                                                \
    template <typename ActionInput>                                                              \
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )                      \
    {                                                                                            \
        if( s.StateVariable.count( s.m_CurrentAttrName ) )                                       \
        {                                                                                        \
            throw parse_error(                                                                   \
                    fmt::format( "Duplicate attribute name '{}'", s.m_CurrentAttrName ),         \
                in );                                                                            \
        }                                                                                        \
                                                                                                 \
        CADSTAR_ATTRIBUTE_VALUE val;                                                             \
        val.m_ReadOnly = s.m_ReadOnly;                                                           \
        val.m_Value = s.m_CurrentString;                                                         \
                                                                                                 \
        s.StateVariable.insert( { s.m_CurrentAttrName, val } );                                  \
        s.m_CurrentAttrName = "";                                                                \
        s.m_CurrentString = "";                                                                  \
        s.m_ReadOnly = false;                                                                    \
    }                                                                                            \
}                                                                                                \

DEFINE_ATTRIBUTE_ACTION( SCM_ATTRIBUTE, m_CurrentPart.m_SchAttributes );
DEFINE_ATTRIBUTE_ACTION( PCB_ATTRIBUTE, m_CurrentPart.m_PcbAttributes );
DEFINE_ATTRIBUTE_ACTION( PART_ATTRIBUTE, m_CurrentPart.m_PartAttributes );
DEFINE_ATTRIBUTE_ACTION( SCH_PCB_ATTRIBUTE, m_CurrentPart.m_SchAndPcbAttributes );


template <>
struct CADSTAR_LIB_PARSER_ACTION<SYMBOL_ENTRY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        s.m_CurrentSymbol.m_Pins.swap( s.m_CurrentPinList );
        s.m_CurrentPart.m_Symbols.push_back( std::move( s.m_CurrentSymbol ) );
        s.m_CurrentSymbol = CADSTAR_PART_SYMBOL_ENTRY();
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<PIN_ENTRY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        s.m_CurrentPinList.push_back( std::move( s.m_CurrentPin ) );
        s.m_CurrentPin = CADSTAR_PART_PIN();
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<HIDDEN_PIN_ENTRY>
{
    static void apply0( CADSTAR_LIB_PARSER_STATE& s )
    {
        s.m_CurrentPart.m_HiddenPins.insert(
                { std::move( s.m_CurrentSignalName ), std::move( s.m_CurrentPinList ) } );
        s.m_CurrentPinList.clear();
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<PIN_POSITION>
{
    template <typename ActionInput>
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
    {
        s.m_CurrentPin.m_Position = CADSTAR_PIN_POSITION( helperStringToLong( in.string() ) );
    }
};


template <>
struct CADSTAR_LIB_PARSER_ACTION<PIN_TYPE>
{
    template <typename ActionInput>
    static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
    {
        // The format allows user defined "part" attrbutes, but the ones listed here are in-built
        // with special meaning
        static const std::map<std::string, CADSTAR_PIN_TYPE> tokenToPinType = {
            { "U", CADSTAR_PIN_TYPE::UNCOMMITTED },
            { "I", CADSTAR_PIN_TYPE::INPUT },
            { "N", CADSTAR_PIN_TYPE::OUTPUT_NOT_OR },
            { "Y", CADSTAR_PIN_TYPE::OUTPUT_OR },
            { "Q", CADSTAR_PIN_TYPE::OUTPUT_NOT_NORM_OR },
            { "P", CADSTAR_PIN_TYPE::POWER },
            { "G", CADSTAR_PIN_TYPE::GROUND },
            { "T", CADSTAR_PIN_TYPE::TRISTATE_BIDIR },
            { "TI", CADSTAR_PIN_TYPE::TRISTATE_INPUT },
            { "TD", CADSTAR_PIN_TYPE::TRISTATE_DRIVER }
        };

        if( !tokenToPinType.count( in.string() ) )
            throw parse_error( fmt::format( "Unexpected pin type '{}'", in.string() ), in );

        s.m_CurrentPin.m_Type = tokenToPinType.at( in.string() );
    }
};


template <typename INPUT_TYPE>
bool checkHeaderHelper( INPUT_TYPE& aInput )
{
    try
    {
        if( !parse<VALID_HEADER>( aInput ) )
            return false;
    }
    catch( const parse_error& )
    {
        return false;
    }

    return true;
}


bool CADSTAR_PARTS_LIB_PARSER::CheckContentHeader( const std::string& aSource ) const
{
    string_input in( aSource, "from_content" );
    return checkHeaderHelper( in );
}


bool CADSTAR_PARTS_LIB_PARSER::CheckFileHeader( const std::filesystem::path& aPath ) const
{
    file_input in( aPath );
    return checkHeaderHelper( in );
}


template<typename INPUT_TYPE>
CADSTAR_PARTS_LIB_MODEL readCadstarHelper( INPUT_TYPE& aInput )
{
    CADSTAR_LIB_PARSER_STATE s;

    try
    {
        // Todo: We could reserve space for the partEntries vector
        // to improve performance? E.g.:
        // s.m_ParsedModel.m_PartEntries.reserve( expectedNumParts );

        if( !parse<GRAMMAR, CADSTAR_LIB_PARSER_ACTION>( aInput, s ) )
            printf( "Some error occurred!\n" );
    }
    catch( const parse_error& e )
    {
        const auto& p = e.positions().front();
        std::cerr << "Error at line " << p.line << ", column " << p.column << std::endl
                  << aInput.line_at( p ) << std::endl
                  << std::setw( p.column ) << '^' << std::endl
                  << e.message() << std::endl;
    }

    return s.m_ParsedModel;
}


CADSTAR_PARTS_LIB_MODEL CADSTAR_PARTS_LIB_PARSER::ReadContent( const std::string& aSource ) const
{
    string_input in( aSource, "from_content" );
    return readCadstarHelper( in );
}


CADSTAR_PARTS_LIB_MODEL
CADSTAR_PARTS_LIB_PARSER::ReadFile( const std::filesystem::path& aPath ) const
{
    file_input in( aPath );
    return readCadstarHelper( in );
}
