/******************************************************************************
 * Copyright (c) 2000-2025 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Raduly, Csaba
 *   Szabo, Janos Zoltan – initial implementation
 *
 ******************************************************************************/
#ifndef TIMER_HH
#define TIMER_HH

#include <stdlib.h>

#include "Types.h"
#include "Error.hh"

class FLOAT;
class Index_Redirect;

/** Runtime class for TTCN-3 timers.
 *  All durations are in seconds with fractional values,
 *  all times are counted from the Unix epoch (1970).
 */
class TIMER {
  // linked list of running timers
  static TIMER *list_head, *list_tail, *backup_head, *backup_tail;
  static boolean control_timers_saved;

  const char *timer_name;
  boolean has_default;
  boolean is_started;
  double default_val; ///< default timeout duration
  double t_started; ///< time when the timer started running
  double t_expires; ///< time when the timer expires
  TIMER *list_prev, *list_next;

  void add_to_list();
  void remove_from_list();

  /// Copy constructor disabled.
  TIMER(const TIMER& other_timer);
  /// Assignment disabled.
  TIMER& operator=(const TIMER& other_timer);

public:
  /// Create a timer with no default duration.
  TIMER(const char *par_timer_name = NULL);
  /// Create a timer with a default timeout value.
  TIMER(const char *par_timer_name, double def_val);
  /// Create a timer with a default timeout value.
  /// @pre \p def_val must be bound
  TIMER(const char *par_timer_name, const FLOAT& def_val);
  ~TIMER();

  /// Change the name of the timer.
  void set_name(const char * name);

  /// Change the default duration.
  void set_default_duration(double def_val);
  /// Change the default duration.
  /// @pre \p def_val must be bound
  void set_default_duration(const FLOAT& def_val);

  /// Start the timer with its default duration.
  void start();
  /// Start the timer with the specified duration.
  void start(double start_val);
  /// Start the timer with the specified duration.
  /// @pre \p start_val must be bound
  void start(const FLOAT& start_val);
  /// Stop the timer.
  void stop();
  /// Return the number of seconds until the timer expires.
  double read() const;
  /** Is the timer running.
   *  @return \c TRUE if is_started and not yet expired, \c FALSE otherwise.
   */
  boolean running(Index_Redirect* p = NULL) const;
  /** Return the alt status.
   *  @return ALT_NO    if the timer is not started.
   *  @return ALT_MAYBE if it's started and the snapshot was taken before the expiration time
   *  @return ALT_YES   if it's started and the snapshot is past the expiration time
   *
   *  If the answer is ALT_YES, a TIMEROP_TIMEOUT message is written to the log
   *  and \p is_started becomes FALSE.
   */
  alt_status timeout(Index_Redirect* p = NULL);

  void log() const;

  /// Stop all running timers (empties the list).
  static void all_stop();
  static boolean any_running();
  static alt_status any_timeout();

  /** Get the earliest expiration time for a running timer.
   *
   *  This includes the testcase guard timer.
   *  @param[out] min_val set to the earliest expiration time.
   *  @return \c TRUE if an active timer was found, \c FALSE otherwise.
   */
  static boolean get_min_expiration(double& min_val);

  static void save_control_timers();
  static void restore_control_timers();

  boolean operator==(const TIMER& right_val) const;
  boolean operator!=(const TIMER& right_val) const;
  boolean operator==(null_type) const;
  boolean operator!=(null_type) const;
};

boolean operator==(null_type, const TIMER&);
boolean operator!=(null_type, const TIMER&);

extern TIMER testcase_timer;

class TIMER_REF
{
  friend boolean operator==(null_type, const TIMER_REF& right_val);
  friend boolean operator!=(null_type, const TIMER_REF& right_val);
  friend boolean operator==(const TIMER& left_val, const TIMER_REF& right_val);
  friend boolean operator!=(const TIMER& left_val, const TIMER_REF& right_val);
private:
  TIMER* ptr; // NULL if it's a null reference
  boolean bound_flag;
public:
  inline void must_bound(const char* err_msg) const {
    if (!bound_flag) {
      TTCN_error("%s", err_msg);
    }
  }
  
  TIMER_REF();

  TIMER_REF(null_type);

  TIMER_REF(TIMER& p_timer);

  TIMER_REF(const TIMER_REF& p_other);

  ~TIMER_REF() {}

  TIMER_REF& operator=(null_type); // assignment operator for null reference

  TIMER_REF& operator=(TIMER& p_port); // assignment operator for timer object

  TIMER_REF& operator=(const TIMER_REF& p_other); // assignment operator for another timer reference

  boolean operator==(null_type) const; // equality operator (with null reference)

  boolean operator!=(null_type) const; // inequality operator (with null reference)
  
  boolean operator==(const TIMER_REF& p_other) const; // equality operator (with another timer reference)

  boolean operator!=(const TIMER_REF& p_other) const; // inequality operator (with another timer reference)
  
  boolean operator==(const TIMER& p_port) const; // equality operator (with timer object)

  boolean operator!=(const TIMER& p_port) const; // inequality operator (with timer object)

  TIMER& operator()() const; // all access to the referenced timer is done through this operator
  
  void log() const;

  void clean_up();

  boolean is_bound() const;

  boolean is_value() const;

  boolean is_present() const;
};

boolean operator==(null_type, const TIMER_REF& right_val); // equality operator (with null reference, inverted)

boolean operator!=(null_type, const TIMER_REF& right_val); // inequality operator (with null reference, inverted)

boolean operator==(const TIMER& left_val, const TIMER_REF& right_val); // equality operator (with timer object, inverted)

boolean operator!=(const TIMER& left_val, const TIMER_REF& right_val); // inequality operator (with timer object, inverted)

#endif
