/**
 * @file JAConditionalSignalUpdateGAM.h
 * @brief Header file for class JAConditionalSignalUpdateGAM
 * @date Jan, 2019
 * @author rhari
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This header file contains the declaration of the class JAConditionalSignalUpdateGAM
 * with all of its public, protected and private members. It may also include
 * definitions for inline methods which need to be visible to the compiler.
 */

#ifndef GAMS_JACONDITIONALSIGNALUPDATEGAM_H_
#define GAMS_JACONDITIONALSIGNALUPDATEGAM_H_

/*---------------------------------------------------------------------------*/
/*                        Standard header includes                           */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                        Project header includes                            */
/*---------------------------------------------------------------------------*/
#include "GAM.h"

/*---------------------------------------------------------------------------*/
/*                           Class declaration                               */
/*---------------------------------------------------------------------------*/

/**
 * @brief GAM that writes predefined values to output signals when a condition is met.
 * If there are no conditional signals provided, the condition is presumed to be met.
 *
 *      +ASYNCShotlengthControlGAM = {
 *          Class = JAConditionalSignalUpdateGAM
 *          Operation = OR // Logical operation performed between conditional signals
 *                         // Supported values: AND, OR, XOR, NOR
 *                         // Default: AND
 *          ExpectedValues = {1 1} // Values to which conditional signals will be compared.
 *          Comparators = {EQUALS EQUALS} // Operator between conditional signal an expected value
 *                                        // Supported values: EQUALS, NOT, GREATER, EQUALS_OR_GREATER, LESS, EQUALS_OR_LESS
 *                                        // Default: EQUALS
 *          Values = {0 3} // Values that will be written to output signals when condition is met.
 *          InputSignals = {
 *              // Conditional Signals
 *              SHOTLEN_FLAG = {
 *                  DataSource = DDB1
 *                  Type = uint32
 *              }
 *              MODE_SHOTLEN_FLAG = {
 *                  DataSource = DDB1
 *                  Type = uint32
 *              }
 *              // Default values (set to output signals before the condition is met)
 *              APS_SWON = { // APS_SWON will keep the value from previous state.
 *                  DataSource = DDB1
 *                  Type = uint32
 *              }
 *              BPS_SWON_DEFAULT = { // BPS_SWON will be set to 7 before condition is met.
 *                  DataSource = DDB1
 *                  Type = uint32
 *                  Default = 7
 *              }
 *          }
 *          OutputSignals = {
 *              APS_SWON = {
 *                  DataSource = DDB1
 *                  Type = uint32
 *              }
 *              BPS_SWON = {
 *                  DataSource = DDB1
 *                  Type = uint32
 *              }
 *          }
 *      }
 */
class JAConditionalSignalUpdateGAM : public MARTe::GAM, public MARTe::StatefulI  {
public:
    CLASS_REGISTER_DECLARATION()

    JAConditionalSignalUpdateGAM();

    virtual ~JAConditionalSignalUpdateGAM();

    virtual bool Initialise(MARTe::StructuredDataI & data);

    virtual bool Setup();

    virtual bool Execute();

    virtual bool PrepareNextState(const MARTe::char8 * const currentStateName,
                                  const MARTe::char8 * const nextStateName);

private:
    /**
     * @brief Does the input signal at provided index have the expected value.
     * @param[in] index of the signal.
     * @return true if the signal has expected value.
     */
    bool Compare(MARTe::uint32 index);

    template <class T>
    bool Compare(MARTe::uint32 index);

    enum OperationMode {
        And, Or, Xor, Nor
    };

    enum ComparisonMode {
        Equals, Not, Greater, EqualsOrGreater, Less, EqualsOrLess
    };

    // Input signals
    void **inputSignals;

    MARTe::TypeDescriptor *inputSignalTypes;

    // Condition operation.
    OperationMode operation;

    // Array of expected values of input signals.
    MARTe::uint32* expectedValues;

    // Expected values count (must be equal to numberOfInputSignals)
    MARTe::uint32 expectedValuesCount;

    // Array of comparators
    ComparisonMode* comparators;

    // Values to be written on output signals when input signal has the expected value.
    MARTe::uint32 *values;

    // Number of values (must be equal to numberOfOutputSignals)
    MARTe::uint32 valuesCount;

    // Output signals
    MARTe::uint32 **outputSignals;

    // Default values of output signals
    MARTe::uint32 **defaultValues;

    // Were output signals already set and we are waiting for a state change before they are set again.
    bool needsReset;
};



/*---------------------------------------------------------------------------*/
/*                        Inline method definitions                          */
/*---------------------------------------------------------------------------*/

template <class T>
bool JAConditionalSignalUpdateGAM::Compare(MARTe::uint32 index) {
    switch (comparators[index]) {
        case Equals:
            return *static_cast<T *>(inputSignals[index]) == static_cast<T>(expectedValues[index]);
        case Not:
            return *static_cast<T *>(inputSignals[index]) != static_cast<T>(expectedValues[index]);
        case Greater:
            return *static_cast<T *>(inputSignals[index]) > static_cast<T>(expectedValues[index]);
        case EqualsOrGreater:
            return *static_cast<T *>(inputSignals[index]) >= static_cast<T>(expectedValues[index]);
        case Less:
            return *static_cast<T *>(inputSignals[index]) < static_cast<T>(expectedValues[index]);
        default: // case EqualsOrLess:
            return *static_cast<T *>(inputSignals[index]) <= static_cast<T>(expectedValues[index]);
    }
}

#endif /* GAMS_JACONDITIONALSIGNALUPDATEGAM_H_ */
