/** @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.

 */

#pragma once

#include "tscore/ink_platform.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 HOSTDB_EVENT_EVENTS_START 500
#define DNS_EVENT_EVENTS_START 600
#define CONFIG_EVENT_EVENTS_START 800
#define LOG_EVENT_EVENTS_START 900
#define REFCOUNT_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 TRANSFORM_EVENTS_START 2000
#define STAT_PAGES_EVENTS_START 2100
#define HTTP_SESSION_EVENTS_START 2200
#define HTTP2_SESSION_EVENTS_START 2250
#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 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 rescheduling an event through any of the Event class
  scheduling functions, 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 scheduling functions.

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

  The canceler 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 scheduling 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 = nullptr);

  void free();

  EThread *ethread = nullptr;

  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 = 0;

  ink_hrtime timeout_at = 0;
  ink_hrtime period     = 0;

  /**
    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 = nullptr;

  // Private

  Event();

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

#ifdef ENABLE_TIME_TRACE
  ink_hrtime start_time;
#endif

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

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

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__)
  ~Event() override {}
#endif
};

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

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