/** @file

  A brief file description

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you 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

  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 _Event_h_
#define _Event_h_

#include "libts.h"
#include "I_Action.h"

//
//  Defines
//

#define MAX_EVENTS_PER_THREAD     100000

// Events

#define EVENT_NONE                CONTINUATION_EVENT_NONE       // 0
#define EVENT_IMMEDIATE           1
#define EVENT_INTERVAL            2
#define EVENT_ERROR               3
#define EVENT_CALL                4     // used internally in state machines
#define EVENT_POLL                5     // negative event; activated on poll or epoll

// Event callback return functions

#define EVENT_DONE                CONTINUATION_DONE     // 0
#define EVENT_CONT                CONTINUATION_CONT     // 1
#define EVENT_RETURN              5
#define EVENT_RESTART             6
#define EVENT_RESTART_DELAYED     7

// Event numbers block allocation
// ** ALL NEW EVENT TYPES SHOULD BE ALLOCATED FROM BLOCKS LISTED HERE! **

#define VC_EVENT_EVENTS_START                     100
#define NET_EVENT_EVENTS_START                    200
#define DISK_EVENT_EVENTS_START                   300
#define CLUSTER_EVENT_EVENTS_START                400
#define HOSTDB_EVENT_EVENTS_START                 500
#define DNS_EVENT_EVENTS_START                    600
#define CONFIG_EVENT_EVENTS_START                 800
#define LOG_EVENT_EVENTS_START	                  900
#define MULTI_CACHE_EVENT_EVENTS_START            1000
#define CACHE_EVENT_EVENTS_START                  1100
#define CACHE_DIRECTORY_EVENT_EVENTS_START        1200
#define CACHE_DB_EVENT_EVENTS_START               1300
#define HTTP_NET_CONNECTION_EVENT_EVENTS_START    1400
#define HTTP_NET_VCONNECTION_EVENT_EVENTS_START   1500
#define GC_EVENT_EVENTS_START                     1600
#define ICP_EVENT_EVENTS_START                    1800
#define TRANSFORM_EVENTS_START                    2000
#define STAT_PAGES_EVENTS_START                   2100
#define HTTP_SESSION_EVENTS_START                 2200
#define HTTP_TUNNEL_EVENTS_START                  2300
#define HTTP_SCH_UPDATE_EVENTS_START              2400
#define NT_ASYNC_CONNECT_EVENT_EVENTS_START       3000
#define NT_ASYNC_IO_EVENT_EVENTS_START            3100
#define RAFT_EVENT_EVENTS_START                   3200
#define SIMPLE_EVENT_EVENTS_START                 3300
#define UPDATE_EVENT_EVENTS_START                 3500
#define LOG_COLLATION_EVENT_EVENTS_START          3800
#define AIO_EVENT_EVENTS_START                    3900
#define BLOCK_CACHE_EVENT_EVENTS_START            4000
#define UTILS_EVENT_EVENTS_START                  5000
#define CONGESTION_EVENT_EVENTS_START             5100
#define CLUSTER_MSG_START                         6000
#define INK_API_EVENT_EVENTS_START                60000
#define SRV_EVENT_EVENTS_START	                  62000
#define REMAP_EVENT_EVENTS_START                  63000

//define misc events here
#define ONE_WAY_TUNNEL_EVENT_PEER_CLOSE (SIMPLE_EVENT_EVENTS_START+1)
#define PREFETCH_EVENT_SEND_URL         (SIMPLE_EVENT_EVENTS_START+2)

typedef int EventType;
const int ET_CALL = 0;
const int MAX_EVENT_TYPES = 8; // conservative, these are dynamically allocated

class EThread;

/**
  A type of Action returned by the EventProcessor. The Event class
  is the type of Action returned by the EventProcessor as a result
  of scheduling an operation. Unlike asynchronous operations
  represented by actions, events never call reentrantly.

  Besides being able to cancel an event (because it is an action),
  you can also reschedule it once received.

  <b>Remarks</b>

  When reschedulling an event through any of the Event class
  schedulling fuctions, state machines must not make these calls
  in other thread other than the one that called them back. They
  also must have acquired the continuation's lock before calling
  any of the schedulling functions.

  The rules for cancelling an event are the same as those for
  actions:

  The canceller of an event must be the state machine that will be
  called back by the task and that state machine's lock must be
  held while calling cancel. Any reference to that event object
  (ie. pointer) held by the state machine must not be used after
  the cancellation.

  Event Codes:

  At the completion of an event, state machines use the event code
  passed in through the Continuation's handler function to distinguish
  the type of event and handle the data parameter accordingly. State
  machine implementers should be careful when defining the event
  codes since they can impact on other state machines presents. For
  this reason, this numbers are usually allocated from a common
  pool.

  Time values:

  The schedulling functions use a time parameter typed as ink_hrtime
  for specifying the timeouts or periods. This is a nanosecond value
  supported by libts and you should use the time functions and
  macros defined in ink_hrtime.h.

  The difference between the timeout specified for schedule_at and
  schedule_in is that in the former it is an absolute value of time
  that is expected to be in the future where in the latter it is
  an amount of time to add to the current time (obtained with
  ink_get_hrtime).

*/
class Event:public Action
{
public:

  ///////////////////////////////////////////////////////////
  // Common Interface                                      //
  ///////////////////////////////////////////////////////////

  /**
     Reschedules this event immediately. Instructs the event object
     to reschedule itself as soon as possible in the EventProcessor.

     @param callback_event Event code to return at the completion
      of this event. See the Remarks section.

  */
  void schedule_imm(int callback_event = EVENT_IMMEDIATE);

  /**
     Reschedules this event to callback at time 'atimeout_at'.
     Instructs the event object to reschedule itself at the time
     specified in atimeout_at on the EventProcessor.

     @param atimeout_at Time at which to callcallback. See the Remarks section.
     @param callback_event Event code to return at the completion of this event. See the Remarks section.

  */
  void schedule_at(ink_hrtime atimeout_at, int callback_event = EVENT_INTERVAL);

  /**
     Reschedules this event to callback at time 'atimeout_at'.
     Instructs the event object to reschedule itself at the time
     specified in atimeout_at on the EventProcessor.

     @param atimeout_in Time at which to callcallback. See the Remarks section.
     @param callback_event Event code to return at the completion of this event. See the Remarks section.

  */
  void schedule_in(ink_hrtime atimeout_in, int callback_event = EVENT_INTERVAL);

  /**
     Reschedules this event to callback every 'aperiod'. Instructs
     the event object to reschedule itself to callback every 'aperiod'
     from now.

     @param aperiod Time period at which to callcallback. See the Remarks section.
     @param callback_event Event code to return at the completion of this event. See the Remarks section.

  */
  void schedule_every(ink_hrtime aperiod, int callback_event = EVENT_INTERVAL);

  // inherited from Action::cancel
  // virtual void cancel(Continuation * c = NULL);

  void free();

  EThread *ethread;

  unsigned int in_the_prot_queue:1;
  unsigned int in_the_priority_queue:1;
  unsigned int immediate:1;
  unsigned int globally_allocated:1;
  unsigned int in_heap:4;
  int callback_event;

  ink_hrtime timeout_at;
  ink_hrtime period;

  /**
    This field can be set when an event is created. It is returned
    as part of the Event structure to the continuation when handleEvent
    is called.

  */
  void *cookie;

  // Private

  Event();


  Event *init(Continuation * c, ink_hrtime atimeout_at = 0, ink_hrtime aperiod = 0);

#ifdef ENABLE_TIME_TRACE
  ink_hrtime start_time;
#endif

private:
  void *operator  new(size_t size);     // use the fast allocators

private:
  // prevent unauthorized copies (Not implemented)
    Event(const Event &);
    Event & operator =(const Event &);

public:
  LINK(Event, link);

  /*-------------------------------------------------------*\
  | UNIX/non-NT Interface                                   |
  \*-------------------------------------------------------*/

#ifdef ONLY_USED_FOR_FIB_AND_BIN_HEAP
  void *node_pointer;
  void set_node_pointer(void *x)
  {
    node_pointer = x;
  }
  void *get_node_pointer()
  {
    return node_pointer;
  }
#endif

#if defined(__GNUC__)
  virtual ~ Event() {
  }
#endif
};

//
// Event Allocator
//
extern ClassAllocator<Event> eventAllocator;

#define EVENT_ALLOC(_a, _t) THREAD_ALLOC(_a, _t)
#define EVENT_FREE(_p, _a, _t)               \
  _p->mutex = NULL;                          \
  if (_p->globally_allocated) ::_a.free(_p); \
  else THREAD_FREE_TO(_p, _a, _t, MAX_EVENTS_PER_THREAD)

#endif /*_Event_h_*/
