/*========================== begin_copyright_notice ============================
Copyright (C) 2021-2022 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/

/*!
 * @file Implementation of the IGtGenProcedure interface
 */

#ifndef GT_GEN_PROCEDURE_H_
#define GT_GEN_PROCEDURE_H_

#include <list>

#include "gt_basic_defs.h"
#include "igt_gen_procedure.h"
#include "igt_ins.h"

namespace gtpin
{
#pragma pack(push, 8)

/// @ingroup GTPIN_INSTRUMENT_PROC
using IGtInsList = std::list<IGtIns*>; ///< List of IGtIns instruction pointers

/* ===================================================================================================== */
// Class GtGenProcedure
/* ===================================================================================================== */
/*! @ingroup GTPIN_INSTRUMENT_PROC
 * @brief Class that implements the IGtGenProcedure interface on top of IGtInsList
 */
class GtGenProcedure final : public IGtGenProcedure, public IGtInsList
{
public:
    //----------------------------------------------------------------------------------------------------
    // IGtGenProcedure interface Implementation
    //----------------------------------------------------------------------------------------------------
    void        Append(IGtIns& ins)                         { IGtInsList::push_back(&ins); }
    void        AppendTo(IGtGenProcedure& otherProc) const  { for (auto insPtr : *this) { otherProc.Append(*insPtr); } }
    void        Clear()                                     { IGtInsList::clear(); }
    uint32_t    Size()    const                             { return uint32_t(IGtInsList::size()); }
    bool        IsEmpty() const                             { return IGtInsList::empty(); }

    void IterateInstructions(IGtInsHandler& handler) const
    {
        for (auto insPtr : *this) { handler.Handle((const IGtIns&)(*insPtr)); }
    }
    void IterateInstructions(IGtInsHandler& handler)
    {
        for (auto insPtr : *this) { handler.Handle(*insPtr); }
    }
    //----------------------------------------------------------------------------------------------------
    // End of the IGtGenProcedure interface implementation
    //----------------------------------------------------------------------------------------------------

public:
    /// Insert 'otherProc' instructions at the end of this procedure
    void Append(const GtGenProcedure& otherProc)  { IGtInsList::insert(end(), otherProc.begin(), otherProc.end()); }

    /// Insert 'otherProc' instructions at the beginning of this procedure
    void Prepend(const GtGenProcedure& otherProc) { IGtInsList::insert(begin(), otherProc.begin(), otherProc.end()); }

#if (_GTPIN_CPP_STD >= 11)
    /// Move 'otherProc' instructions to the end of this procedure. 'otherProc' becomes empty after the operation 
    void MoveAfter(GtGenProcedure&& otherProc)  { IGtInsList::splice(end(), otherProc); }

    /// Move 'otherProc' instructions to the beginning of this procedure. 'otherProc' becomes empty after the operation 
    void MoveBefore(GtGenProcedure&& otherProc) { IGtInsList::splice(begin(), otherProc); }
#endif

public:
    GtGenProcedure() = default;                                                 ///< Construct empty procedure
    GtGenProcedure(const IGtInsList& igtInsList) : IGtInsList(igtInsList) {}    ///< Construct procedure from IGtInsList
    GtGenProcedure(const IGtGenProcedure& otherProc) { IGtGenProcedure::Append(otherProc); }    ///< Copy constructor

    /// Destructor
    ~GtGenProcedure() = default;
};

/*! @addtogroup GTPIN_INSTRUMENT_PROC 
    @{
 */
/// Append instruction 'ins' at the end of procedure 'proc'
inline GtGenProcedure& operator +=(GtGenProcedure& proc, IGtIns& ins) { proc.Append(ins); return proc;}

/// Append 'rProc' at the end of 'lProc'
inline GtGenProcedure& operator +=(GtGenProcedure& lProc, const GtGenProcedure& rProc) { lProc.Append(rProc); return lProc;}

#if (_GTPIN_CPP_STD >= 11)
/// Move 'rProc' to the end of 'lProc'. 'rProc' becomes empty after the operation 
inline GtGenProcedure& operator +=(GtGenProcedure& lProc, GtGenProcedure&& rProc) { lProc.MoveAfter(ToMove(rProc)); return lProc; }
#endif
/*! @} */

#pragma pack(pop)
}
#endif
