/*=========================================================================
 *
 *  Copyright UMC Utrecht and contributors
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef __itkOpenCLEventList_h
#define __itkOpenCLEventList_h

#include "itkOpenCLEvent.h"
#include <vector>

namespace itk
{
/** \class OpenCLEventList
 * \brief OpenCLEventList class represents a list of OpenCLEvent objects.
 *
 * \ingroup OpenCL
 * \sa OpenCLEvent
 */
class ITKOpenCL_EXPORT OpenCLEventList
{
public:

  /** Standard class typedefs. */
  typedef OpenCLEventList Self;

  typedef std::vector< cl_event > OpenCLEventListArrayType;

  /** Constructs an empty list of OpenCL events. */
  OpenCLEventList() {}

  /** Constructs a list of OpenCL events that contains event. If event is null,
   * this constructor will construct an empty list.
   * \sa Append() */
  OpenCLEventList( const OpenCLEvent & event );

  /** Constructs a copy of other.
   * \sa operator=() */
  OpenCLEventList( const OpenCLEventList & other );

  /** Destroys this list of OpenCL events. */
  ~OpenCLEventList();

  /** Assigns the contents of other to this object. */
  OpenCLEventList & operator=( const OpenCLEventList & other );

  /** Returns true if this is an empty list, false otherwise.
   * \sa GetSize() */
  bool IsEmpty() const { return this->m_Events.empty(); }

  /** Returns the size of this event list.
   * \sa IsEmpty(), Get() */
  std::size_t GetSize() const { return this->m_Events.size(); }

  /** Appends event to this list of OpenCL events if it is not null.
   * Does nothing if event is null.
   * \sa Remove() */
  void Append( const OpenCLEvent & event );

  /** Appends the contents of other to this event list. */
  void Append( const OpenCLEventList & other );

  /** Removes event from this event list.
   * \sa Append(), Contains() */
  void Remove( const OpenCLEvent & event );

  /** Returns the event at index in this event list, or a null OpenCLEvent
   * if index is out of range.
   * \sa GetSize(), Contains() */
  OpenCLEvent Get( const std::size_t index ) const;

  /** Returns true if this event list contains event, false otherwise.
   * \sa Get(), Remove() */
  bool Contains( const OpenCLEvent & event ) const;

  /** Returns a const pointer to the raw OpenCL event data in this event list;
   * null if the list is empty. This function is intended for use with native
   * OpenCL library functions that take an array of cl_event objects as
   * an argument.
   * \sa GetSize() */
  const cl_event * GetEventData() const;

  /** Returns a const reference to the array of OpenCL events.
   * \sa GetSize() */
  const OpenCLEventListArrayType & GetEventArray() const;

  /** Same as append event.
   * \sa Append() */
  OpenCLEventList & operator+=( const OpenCLEvent & event );

  /** Same as append event.
   * \sa Append() */
  OpenCLEventList & operator+=( const OpenCLEventList & other );

  /** Same as append event.
   * \sa Append() */
  OpenCLEventList & operator<<( const OpenCLEvent & event );

  /** Same as append event.
   * \sa Append() */
  OpenCLEventList & operator<<( const OpenCLEventList & other );

  /** Waits for all of the events in this list to be signaled as finished.
   * The calling thread is blocked until all of the events are signaled.
   * If the list is empty, then this function returns immediately.
   * \sa OpenCLEvent::WaitForFinished() */
  cl_int WaitForFinished();

private:

  OpenCLEventListArrayType m_Events;
};

/** Operator ==
* Returns true if \a lhs OpenCL event list is the same as \a rhs, false otherwise.
* \sa operator!= */
bool ITKOpenCL_EXPORT operator==( const OpenCLEventList & lhs, const OpenCLEventList & rhs );

/** Operator !=
* Returns true if \a lhs OpenCL event list is not the same as \a rhs, false otherwise.
* \sa operator== */
bool ITKOpenCL_EXPORT operator!=( const OpenCLEventList & lhs, const OpenCLEventList & rhs );

/** Stream out operator for OpenCLEventList */
template< typename charT, typename traits >
inline
std::basic_ostream< charT, traits > &
operator<<( std::basic_ostream< charT, traits > & strm,
  const OpenCLEventList & eventlist )
{
  if( !eventlist.GetSize() )
  {
    strm << "OpenCLEventList()";
    return strm;
  }

  OpenCLEventList::OpenCLEventListArrayType::const_iterator it;
  const OpenCLEventList::OpenCLEventListArrayType &         eventsArray = eventlist.GetEventArray();

  std::size_t id = 0;
  strm << "OpenCLEventList contains:" << std::endl;
  for( it = eventsArray.begin(); it < eventsArray.end(); ++it )
  {
    // const OpenCLEvent &event = *it;
    // strm << "array id: " << id << " " << event << std::endl;

    // Let's print only address, printing the OpenCLEvent
    // could halt execution of the program
    strm << "array id: " << id << " " << *it << std::endl;
    ++id;
  }

  return strm;
}


} // end namespace itk

#endif /* __itkOpenCLEventList_h */
