/* Simple test program for the term-style-control module.
   Copyright (C) 2019-2024 Free Software Foundation, Inc.
   Written by Bruno Haible <bruno@clisp.org>, 2019.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#include <config.h>

/* Specification.  */
#include "term-style-control.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "full-write.h"

/* This program outputs the line:     Hello Dr. Linus Pauling!
   with underlining here:                   _________________
   and a cyan background color here:            _____
 */

/* ECMA-48 / ISO 6429 escape sequences.  See
   https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
 */
static const char set_underline_on[] = "\033[4m";
static const char set_underline_off[] = "\033[24m";
static const char set_background_color_cyan[] = "\033[46m";
static const char set_background_color_default[] = "\033[49m";

struct term_style_user_data
{
  /* These fields are marked volatile, because they are accessed from the
     async-safe function async_set_attributes_from_default.  */
  bool volatile underline;
  bool volatile background_color_cyan;

  struct term_style_control_data ctrl_data;
};

static struct term_style_control_data *
get_control_data (struct term_style_user_data *user_data)
{
  return &user_data->ctrl_data;
}

static void
restore (_GL_UNUSED struct term_style_user_data *user_data)
{
  fputs (set_underline_off, stdout);
  fputs (set_background_color_default, stdout);
  fflush (stdout);
}

static _GL_ASYNC_SAFE void
async_restore (_GL_UNUSED struct term_style_user_data *user_data)
{
  /* No <stdio.h> calls here!  */
  full_write (STDOUT_FILENO, set_underline_off,
              strlen (set_underline_off));
  full_write (STDOUT_FILENO, set_background_color_default,
              strlen (set_background_color_default));
}

static _GL_ASYNC_SAFE void
async_set_attributes_from_default (struct term_style_user_data *user_data)
{
  /* No <stdio.h> calls here!  */
  if (user_data->underline)
    full_write (STDOUT_FILENO, set_underline_on,
                strlen (set_underline_on));
  if (user_data->background_color_cyan)
    full_write (STDOUT_FILENO, set_background_color_cyan,
                strlen (set_background_color_cyan));
}

static const struct term_style_controller controller =
{
  get_control_data,
  restore,
  async_restore,
  async_set_attributes_from_default
};

int
main ()
{
  struct term_style_user_data user_data;

  /* Initialization.  */
  user_data.underline = false;
  user_data.background_color_cyan = false;

  activate_term_style_controller (&controller, &user_data, STDOUT_FILENO,
                                  TTYCTL_AUTO);

  /* As long as no styling is needed, we can stay in the default mode.  */
  fputs ("Hello ", stdout);
  fflush (stdout);

  /* Before any styling, enable the non-default mode.  */
  activate_term_non_default_mode (&controller, &user_data);

  /* Set user_data.underline *before* emitting the appropriate
     escape sequences, otherwise async_set_attributes_from_default will not
     do its job correctly.  */
  user_data.underline = true;
  fputs (set_underline_on, stdout);
  fflush (stdout);

  fputs ("Dr. ", stdout);
  fflush (stdout);

  /* Set user_data.background_color_cyan *before* emitting the appropriate
     escape sequences, otherwise async_set_attributes_from_default will not
     do its job correctly.  */
  user_data.background_color_cyan = true;
  fputs (set_background_color_cyan, stdout);
  fflush (stdout);

  fputs ("Linus", stdout);
  fflush (stdout);

  user_data.background_color_cyan = false;
  fputs (set_background_color_default, stdout);
  fflush (stdout);

  fputs (" Pauling", stdout);
  fflush (stdout);

  user_data.underline = false;
  fputs (set_underline_off, stdout);
  fflush (stdout);

  /* Needed as a prerequisite of the deactivate_term_style_controller call
     below.  */
  deactivate_term_non_default_mode (&controller, &user_data);

  fputs ("!\n", stdout);

  /* If the user_data was allocated in heap memory, with indefinite extent,
     this call would be optional.  But since we have allocated it on the
     stack, we must deactivate it before it goes out of scope.  Otherwise
     we get undefined behaviour in an atexit() handler.  */
  deactivate_term_style_controller (&controller, &user_data);

  return 0;
}
