// Pretty-printing utility -*- c++ -*-

#ifndef PRINTER_H_
# define PRINTER_H_
# ifdef __GNUC__
#  pragma interface
# endif // __GNUC__

# include "typedefs.h"
# include "StringBuffer.h"
# include <stdio.h>

/** @file Printer.h
 * Pretty-printing utility (output to character strings and file streams)
 */

/* Copyright © 1999-2002 Marko Mäkelä (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA 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 2, or (at your option)
   any later version.

   MARIA 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.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/** Pretty-printing utility */
class Printer
{
public:
  /** Radix for displaying numbers */
  enum Radix { Octal, Hexadecimal, Decimal };

  /** Constructor
   * @param width	maximum width of output in columns
   * @param radix	radix for displaying numbers
   */
  Printer (unsigned width = 80,
	   enum Radix radix = Decimal) :
    myWidth (width), myRadix (radix),
    myNesting (0), myColumn (0), myBuffer (), myBOL (0),
    myOutput (0), myStream (stderr) {}
private:
  /** Copy constructor */
  Printer (const class Printer& old);
  /** Assignment operator */
  class Printer& operator= (const class Printer& old);
public:
  /** Destructor */
  ~Printer () { finish (); }

  /** Set the maximum width of the output */
  void setWidth (unsigned width) { myWidth = width; }
  /** Get the maximum width of the output */
  unsigned getWidth () const { return myWidth; }
  /** Set the radix */
  void setRadix (enum Radix radix) { myRadix = radix; }
  /** Get the radix */
  enum Radix getRadix () const { return myRadix; }
  /** Set the function to be called at beginning of line */
  void setBOL (unsigned (*bol) (void)) { myBOL = bol; }
  /** Set the output buffer (NULL=myStream) */
  void setOutput (class StringBuffer* output) { myOutput = output; }
  /** Set the output stream */
  void setOutput (FILE* stream) { myStream = stream; }
  /** Get the output stream */
  FILE* getOutput () { return myStream; }

  /** Increment the nesting level */
  const class Printer& operator++ () const { myNesting++; return *this; }
  /** Increment the nesting level */
  const class Printer& operator++ (int) const { myNesting++; return *this; }
  /** Decrement the nesting level */
  const class Printer& operator-- () const { myNesting--; return *this; }
  /** Decrement the nesting level */
  const class Printer& operator-- (int) const { myNesting--; return *this; }

  /** Finish printing (add a newline if necessary) */
  void finish () const;
  /** Add an unconditional line break */
  void linebreak () const;
  /** Break the line if necessary
   * @param margin	safety margin required
   */
  void linebreak (unsigned margin) const {
    if (myOutput) return;
    if (myWidth && myColumn + margin >= myWidth)
      linebreak ();
    else if (!myColumn && myBOL && myStream == stderr)
      myColumn = (*myBOL) ();
  }

  /** Display a delimiter character
   * @param character	delimiter character to be displayed
   * @return		*this
   */
  const class Printer& delimiter (char character) const;

  /** Determine whether a name needs quotes
   * @param name	the name
   * @return		true if print () would enclose the name in quotes
   */
  static bool isPrintable (const char* name);

  /** Display raw text
   * @param string	NUL-terminated string to be displayed
   */
  void printRaw (const char* string) const;

  /** Display quoted text
   * @param string	NUL-terminated string to be displayed in double quotes
   */
  void printQuoted (const char* string) const;

  /** Display a state number
   * @param state	state number to be displayed
   */
  void printState (card_t state) const;
  /** Display a strongly connected component number
   * @param comp	component number to be displayed
   */
  void printComponent (card_t comp) const;
  /** Display an unsigned prefixed with a string
   * @param prefix	prefix to be displayed
   * @param number	number to be displayed
   */
  void printNumber (const char* prefix, card_t number) const;
  /** Display a boolean value
   * @param value	value to be displayed
   */
  void print (bool value) const;
  /** Display a cardinality
   * @param number	number to be displayed
   */
  void print (card_t number) const;
  /** Display a signed number
   * @param number	number to be displayed
   */
  void print (int_t number) const;
  /** Display a name
   * @param name	NUL-terminated name to be displayed
   */
  void print (const char* name) const;
  /** Display a character string in double quotes
   * @param string	characters to be displayed
   * @param length	length of the string
   */
  void print (const char* string, unsigned length) const;
  /** Display a character in single quotes
   * @param character	character to be displayed
   */
  void print (char_t character) const;

private:
  /** maximum width of output in columns */
  unsigned myWidth;
  /** radix for displaying numbers */
  enum Radix myRadix;
  /** nesting depth of output */
  mutable unsigned myNesting;
  /** current output column */
  mutable unsigned myColumn;
  /** buffer for building up strings */
  mutable class StringBuffer myBuffer;
  /** function to be called at beginning of line (NULL=none) */
  unsigned (*myBOL) (void);
  /** output buffer (NULL=myStream) */
  class StringBuffer* myOutput;
  /** output stream (or stderr) */
  FILE* myStream;
};

#endif // PRINTER_H_
