/*******************************************************************************
 * Copyright (c) 2005 - 2014 ACIN, Profactor GmbH, fortiss GmbH
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Alois Zoitl, Gunnar Grabmaier, Thomas Strasser, Rene Smodic, Ingo Hegny
 *      - initial implementation and rework communication infrastructure
 *******************************************************************************/
#ifndef _ECET_H_
#define _ECET_H_

#include "event.h"
#include "datatypes/forte_time.h"
#include <forte_thread.h>
#include <forte_sync.h>
#include <forte_sem.h>

/*! \ingroup CORE\brief Class for executing one event chain.
 *
 */
class CEventChainExecutionThread : public CThread{
  public:
    CEventChainExecutionThread();
    virtual ~CEventChainExecutionThread();

    /*!\brief Start the a new event chain with the given event.
     *
     *
     * \param pa_poEventToAdd event of the EC to start
     */
    void startEventChain(SEventEntry *paEventToAdd);

    /*!\brief Add an new event entry to the event chain
     *
     * \param pa_poEventToAdd new event entry
     */
    void addEventEntry(SEventEntry *paEventToAdd);

    /*!\brief allow to start, stop, and kill the execution of the event chain execution thread
     *
     * @param pa_unCommand the management command to be executed
     */
    void changeExecutionState(EMGMCommandType paCommand);

    void joinEventChainExecutionThread(){
      CThread::join();
    }

    void setDeadline(const CIEC_TIME &paVal){
      CThread::setDeadline(paVal);
    }

    bool isProcessingEvents() const {
      return mProcessingEvents;
    }

    void resumeSelfSuspend(){
      mSuspendSemaphore.inc();
    }

    static CEventChainExecutionThread* createEcet();

  protected:
    //@{
    /*! \brief List of input events to deliver.
     *
     * This list stores the necessary information for all events to deliver that occurred within this event chain.
     */

    TEventEntryPtr mEventList[cg_nEventChainEventListSize];
    TEventEntryPtr* mEventListStart;
    TEventEntryPtr* mEventListEnd;
    //@}

    void mainRun();

  private:
    /*! \brief The thread run()-method where the events are sent to the FBs and the FBs are executed in.
     *
     * If there is an entry in the Event List the event will be delivered and the FB executed.
     * If there is no entry in this list the CEventChainExecutionThread will suspend itself and remove itself from the
     * active EventChainExecutionlist in CFBNExecution.
     * \return true if thread execution ended successfully
     */
    virtual void run(void);

    /*! \brief Clear the event chain.
     */
    void clear(void);

    bool externalEventOccured(){
      /* we should not need a protection here as we are just comparing the both
       * the only value that could change during the compare is m_pstExternalEventListEnd. In the worst case
       * this results in a to early or to late detection. However as the transfer is protected this should be no
       * big issue.
       * TODO perform test to verify this assumption
       */
      return (mExternalEventListStart != mExternalEventListEnd);
    }

    //! Transfer elements stored in the external event list to the main event list
    void transferExternalEvents();

    void selfSuspend(){
      mSuspendSemaphore.waitIndefinitely();
    }

    //@{
    /*! \brief List of external events that occurred during one FB's execution
     *
     * This list stores external events that may have occurred during the execution of a FB or during when the
     * Event-Chain execution was sleeping. with this second list we omit the need for a mutex protection of the event
     * list. This is a great performance gain.
     */
    TEventEntryPtr mExternalEventList[cg_nEventChainExternalEventListSize];
    TEventEntryPtr* mExternalEventListStart;
    TEventEntryPtr* mExternalEventListEnd;
    //@}

    //! SyncObject for protecting the list in regard to several accesses
    CSyncObject mExternalEventListSync;

    forte::arch::CSemaphore mSuspendSemaphore;

    /*! \brief Flag indicating if this event chain execution thread is currently processing any events
     *
     * Initially this flag is false.
     * This flag is activated when a new event chain is started and deactivated when the event queue is empty.
     *
     * Currently this flag is only needed for the FB tester.
     * TODO consider surrounding the usage points of this flag with #defines such that it is only used for testing.
     */
    bool mProcessingEvents;
};

#endif /*ECET_H_*/
