/******************************************************************************
 * Copyright (c) 2000-2021 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
 *   Forstner, Matyas
 *   Gecse, Roland
 *   Raduly, Csaba
 *   Szabo, Janos Zoltan – initial implementation
 *
 ******************************************************************************/
#ifndef _Common_stack_HH
#define _Common_stack_HH

#include "error.h"
#include "../common/memory.h"

/**
 * LIFO container optimized to store elements sequentially,
 * and access only the element on the top of the stack.
 * Accessing the top of the stack has amortized constant time cost.
 *
 * The container stores pointers to objects of type T; it doesn't own them.
 * It is the responsibility of the caller to create and destroy the objects
 * and supply the pointers to the container.
 *
 * \ingroup containers
 */
template<class T>
class stack {

private:

  size_t num_s; ///< used size
  size_t max_s; ///< allocated size
  T **s_ptr; ///< array of pointers to objects of type T

  static const size_t initial_size = 1, increment_factor = 2;

  /** Copy constructor: DO NOT IMPLEMENT! */
  stack(const stack&);
  /** Copy assignment: DO NOT IMPLEMENT! */
  stack& operator=(const stack&);

public:

  static const size_t max_stack_length = static_cast<size_t>(-1 );

  /** Creates an empty stack. */
  stack() : num_s(0), max_s(0), s_ptr(NULL) { }

  /** Deallocates its memory. If the container is not empty,
   * FATAL_ERROR occurs, so before destructing, clear()
   * must be invoked explicitly.
   */
  ~stack() {
    if(num_s > 0) FATAL_ERROR("stack::~stack(): stack is not empty");
    Free(s_ptr);
  }

  /** Returns the number of elements in the container. */
  size_t size() const { return num_s; }

  /** Returns true if the container has no elements. */
  bool empty() const { return num_s == 0; }

  /** Forgets all elements in the container.
   * No memory is freed. */
  void clear() { num_s = 0; }

  /** Push the elem onto the stack. */
  void push(T *elem) {
    if (max_s <= num_s) {
      if (max_s == 0) {
	max_s = initial_size;
	s_ptr = static_cast<T**>(Malloc(initial_size * sizeof(*s_ptr)));
      } else {
	if(max_s >= max_stack_length / increment_factor)
	  FATAL_ERROR("stack::push(): stack index overflow");
	max_s *= increment_factor;
	s_ptr = static_cast<T**>(Realloc(s_ptr, max_s * sizeof(*s_ptr)));
      }
    }
    s_ptr[num_s++] = elem;
  }

  /** Returns the top of the stack or FATAL_ERROR if empty. */
  T* top() const {
    if(num_s == 0) FATAL_ERROR("stack::top() const: stack is empty");
    return s_ptr[num_s - 1];
  }

  /** Returns the top of the stack, and removes it from the
   * container. If the stack is empty then FATAL_ERROR occurs. */
  T* pop() {
    if(num_s == 0) FATAL_ERROR("stack::pop(): stack is empty");
    return s_ptr[--num_s];
  }

};

#endif // _Common_stack_HH
