// Стек и операции с ним. Динамическая функциональная реализация.

#include <conio.h>
#include <stdio.h>
#include <locale.h>
#include <windows.h>

/// <summary>
/// Код, стек пуст.
/// </summary>
const char S_EMPTY = 2;

struct stack
{
   stack(LONGLONG _elem = 0, stack *_next = NULL) : elem(_elem), next(_next) {}

   LONGLONG elem = 0;
   stack *next = NULL;
};

/// <summary>
/// Добавляет элемент в стек.
/// </summary>
/// <param name="s">Стек, в который нужно добавить элемент</param>
/// <param name="elem">Элемент, который нужно добавить в стек</param>
/// <returns>Код выполнения операции: S_FULL - операция не выполнена (стек s полон), 0 - операция выполнена</returns>
void push(stack **s, LONGLONG elem);

/// <summary>
/// Берет последний элемент из стека.
/// </summary>
/// <param name="s">Стек, из которого нужно взять элемент</param>
/// <param name="elem">Ссылка, куда нужно положить взятый элемент из стека</param>
/// <returns>Код выполнения операции: S_EMPTY - операция не выполнена (стек s пуст), 0 - операция выполнена</returns>
int pop(stack **s, LONGLONG &elem);

/// <summary>
/// Проверяет пустой ли стек.
/// </summary>
/// <param name="s">Стек, который нужно проверить</param>
/// <returns>true - пустой, false - не пустой</returns>
bool is_empty(stack *s);

/// <summary>
/// Очищает стек.
/// </summary>
/// <param name="s">Стек, который нужно очистить</param>
void clear(stack **s);

/// <summary>
/// Выводит все элементы стека на консоль.
/// </summary>
/// <param name="s">Стек, элементы которого нужно вывести на консоль</param>
void print(stack **s);

int main()
{
   setlocale(0, "");

   stack *S = NULL;

   // process - продолжать работу программы, f - цикл отрабатывает первый раз.
   bool process = true, f = true;
   do
   {
      LONGLONG n = 0;
      int a = 0, r = 0;

      if (!f) // Отступ на консоли после предыдущего круга, чтобы разделять информацию.
         printf_s("\n\n");
      f = false;

      printf_s("Выберите действие:\nОчистить стек [0]\tПроверить, пустой ли стек [1]\tДобавить число в стек [2]\tВзять число из стека[3]\tРаспечатать содержимое стека [4]\tЗавершить работу [5]\nДействие: ");
      scanf_s("%d", &a);
      printf_s("\n");

      switch (a)
      {
         case 0: // Очистить стек.
            clear(&S);
            printf_s("Стек очищен."); break;
         case 1: // Проверить, пустой ли стек.
            printf_s(is_empty(S) ? "Стек пуст." : "Стек не пуст."); break;
         case 2: // Добавить число в стек.
            printf_s("Введите число: ");
            scanf_s("%lld", &n);

            push(&S, n);
            printf_s("Число добавлено."); break;
         case 3: // Взять число из стека.
            r = pop(&S, n);
            r == S_EMPTY ? printf_s("Не удалось взять число: стек пуст.") : printf_s("Взятое число: %lld.", n); break;
         case 4: // Распечатать стек.
            printf_s("Содержимое стека:\n");
            print(&S); break;
         case 5: // Заверешение работы программы.
            process = false; break;
         default: break;
      }
   }
   while(process);

   return 0 * _getch();
}

void push(stack **s, LONGLONG elem)
{
   *s = new stack(elem, *s);
}
int pop(stack **s, LONGLONG &elem)
{
   bool is_e = is_empty(*s);

   if (!is_e)
   {
      stack *t = *s;
      elem = t->elem;

      *s = t->next;
      delete t;
   }

   return is_e ? S_EMPTY : 0;
}

bool is_empty(stack *s)
{
   return !s;
}

void clear(stack **s)
{
   for (LONGLONG n = 0 ; !is_empty(*s); pop(s, n));
}
void print(stack **s)
{
   LONGLONG n = 0;
   stack *sb = NULL; // Стек-буфер.

   while (!is_empty(*s))
   {
      pop(s, n);
      push(&sb, n);
      printf_s("%lld\n", n);
   }
   while (!is_empty(sb)) // Заполнение пустого s его же элементами.
   {
      pop(&sb, n);
      push(s, n);
   }
}