// -*- C++ -*-
/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
     Written by James Clark (jjc@jclark.com)

This file is part of groff.

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

groff 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 <http://www.gnu.org/licenses/>. */

struct hyphen_list {
  unsigned char hyphen;
  unsigned char breakable;
  unsigned char hyphenation_code;
  hyphen_list *next;
  hyphen_list(unsigned char code, hyphen_list *p = 0);
};

void hyphenate(hyphen_list *, unsigned);

enum hyphenation_type { HYPHEN_MIDDLE, HYPHEN_BOUNDARY, HYPHEN_INHIBIT };

class ascii_output_file;

struct breakpoint;
struct vertical_size;
class charinfo;

class macro;

class troff_output_file;
class tfont;
class environment;

class glyph_node;
class diverted_space_node;
class token_node;

struct node {
  node *next;
  node *last;
  statem *state;
  statem *push_state;
  int div_nest_level;
  int is_special;
  node();
  node(node *);
  node(node *, statem *, int);
  node *add_char(charinfo *, environment *, hunits *, int *, node ** = 0);

  virtual ~node();
  virtual node *copy() = 0;
  virtual int set_unformat_flag();
  virtual int force_tprint() = 0;
  virtual int is_tag() = 0;
  virtual int get_break_code();
  virtual hunits width();
  virtual hunits subscript_correction();
  virtual hunits italic_correction();
  virtual hunits left_italic_correction();
  virtual hunits skew();
  virtual int nspaces();
  virtual int merge_space(hunits, hunits, hunits);
  virtual vunits vertical_width();
  virtual node *last_char_node();
  virtual void vertical_extent(vunits *, vunits *);
  virtual int character_type();
  virtual void set_vertical_size(vertical_size *);
  virtual int ends_sentence();
  virtual node *merge_self(node *);
  virtual node *add_discretionary_hyphen();
  virtual node *add_self(node *, hyphen_list **);
  virtual hyphen_list *get_hyphen_list(hyphen_list *, int *);
  virtual void ascii_print(ascii_output_file *);
  virtual void asciify(macro *);
  virtual int discardable();
  virtual void spread_space(int *, hunits *);
  virtual void freeze_space();
  virtual void is_escape_colon();
  virtual breakpoint *get_breakpoints(hunits, int, breakpoint * = 0, int = 0);
  virtual int nbreaks();
  virtual void split(int, node **, node **);
  virtual hyphenation_type get_hyphenation_type();
  virtual int reread(int *);
  virtual token_node *get_token_node();
  virtual int overlaps_vertically();
  virtual int overlaps_horizontally();
  virtual units size();
  virtual int interpret(macro *);

  virtual node *merge_glyph_node(glyph_node *);
  virtual tfont *get_tfont();
  virtual color *get_glyph_color();
  virtual color *get_fill_color();
  virtual void tprint(troff_output_file *);
  virtual void zero_width_tprint(troff_output_file *);

  node *add_italic_correction(hunits *);

  virtual int same(node *) = 0;
  virtual const char *type() = 0;
  virtual void debug_node();
  virtual void debug_node_list();
};

inline node::node()
: next(0), last(0), state(0), push_state(0), div_nest_level(0), is_special(0)
{
}

inline node::node(node *n)
: next(n), last(0), state(0), push_state(0), div_nest_level(0), is_special(0)
{
}

inline node::node(node *n, statem *s, int divlevel)
: next(n), last(0), push_state(0), div_nest_level(divlevel), is_special(0)
{
  if (s)
    state = new statem(s);
  else
    state = 0;
}

inline node::~node()
{
  if (state != 0)
    delete state;
  if (push_state != 0)
    delete push_state;
}

// 0 means it doesn't, 1 means it does, 2 means it's transparent

int node_list_ends_sentence(node *);

struct breakpoint {
  breakpoint *next;
  hunits width;
  int nspaces;
  node *nd;
  int index;
  char hyphenated;
};

class line_start_node : public node {
public:
  line_start_node() {}
  node *copy() { return new line_start_node; }
  int same(node *);
  int force_tprint();
  int is_tag();
  const char *type();
  void asciify(macro *);
};

class space_node : public node {
private:
protected:
  hunits n;
  char set;
  char was_escape_colon;
  color *col;			/* for grotty */
  space_node(hunits, int, int, color *, statem *, int, node * = 0);
public:
  space_node(hunits, color *, node * = 0);
  node *copy();
  int nspaces();
  hunits width();
  int discardable();
  int merge_space(hunits, hunits, hunits);
  void freeze_space();
  void is_escape_colon();
  void spread_space(int *, hunits *);
  void tprint(troff_output_file *);
  breakpoint *get_breakpoints(hunits, int, breakpoint * = 0, int = 0);
  int nbreaks();
  void split(int, node **, node **);
  void ascii_print(ascii_output_file *);
  int same(node *);
  void asciify(macro *);
  const char *type();
  int force_tprint();
  int is_tag();
  hyphenation_type get_hyphenation_type();
};

struct width_list {
  hunits width;
  hunits sentence_width;
  width_list *next;
  width_list(hunits, hunits);
  width_list(width_list *);
};

class word_space_node : public space_node {
protected:
  width_list *orig_width;
  unsigned char unformat;
  word_space_node(hunits, int, color *, width_list *, int, statem *, int,
		  node * = 0);
public:
  word_space_node(hunits, color *, width_list *, node * = 0);
  ~word_space_node();
  node *copy();
  int reread(int *);
  int set_unformat_flag();
  void tprint(troff_output_file *);
  int same(node *);
  void asciify(macro *);
  const char *type();
  int merge_space(hunits, hunits, hunits);
  int force_tprint();
  int is_tag();
};

class unbreakable_space_node : public word_space_node {
  unbreakable_space_node(hunits, int, color *, statem *, int, node * = 0);
public:
  unbreakable_space_node(hunits, color *, node * = 0);
  node *copy();
  int reread(int *);
  void tprint(troff_output_file *);
  int same(node *);
  void asciify(macro *);
  const char *type();
  int force_tprint();
  int is_tag();
  breakpoint *get_breakpoints(hunits, int, breakpoint * = 0, int = 0);
  int nbreaks();
  void split(int, node **, node **);
  int merge_space(hunits, hunits, hunits);
  node *add_self(node *, hyphen_list **);
  hyphen_list *get_hyphen_list(hyphen_list *, int *);
  hyphenation_type get_hyphenation_type();
};

class diverted_space_node : public node {
public:
  vunits n;
  diverted_space_node(vunits, node * = 0);
  diverted_space_node(vunits, statem *, int, node * = 0);
  node *copy();
  int reread(int *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class diverted_copy_file_node : public node {
  symbol filename;
public:
  vunits n;
  diverted_copy_file_node(symbol, node * = 0);
  diverted_copy_file_node(symbol, statem *, int, node * = 0);
  node *copy();
  int reread(int *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class extra_size_node : public node {
  vunits n;
public:
  extra_size_node(vunits);
  extra_size_node(vunits, statem *, int);
  void set_vertical_size(vertical_size *);
  node *copy();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class vertical_size_node : public node {
  vunits n;
public:
  vertical_size_node(vunits, statem *, int);
  vertical_size_node(vunits);
  void set_vertical_size(vertical_size *);
  void asciify(macro *);
  node *copy();
  int set_unformat_flag();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class hmotion_node : public node {
protected:
  hunits n;
  unsigned char was_tab;
  unsigned char unformat;
  color *col;			/* for grotty */
public:
  hmotion_node(hunits i, color *c, node *nxt = 0)
    : node(nxt), n(i), was_tab(0), unformat(0), col(c) {}
  hmotion_node(hunits i, color *c, statem *s, int divlevel, node *nxt = 0)
    : node(nxt, s, divlevel), n(i), was_tab(0), unformat(0), col(c) {}
  hmotion_node(hunits i, int flag1, int flag2, color *c, statem *s,
	       int divlevel, node *nxt = 0)
    : node(nxt, s, divlevel), n(i), was_tab(flag1), unformat(flag2),
      col(c) {}
  hmotion_node(hunits i, int flag1, int flag2, color *c, node *nxt = 0)
    : node(nxt), n(i), was_tab(flag1), unformat(flag2), col(c) {}
  node *copy();
  int reread(int *);
  int set_unformat_flag();
  void asciify(macro *);
  void tprint(troff_output_file *);
  hunits width();
  void ascii_print(ascii_output_file *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  node *add_self(node *, hyphen_list **);
  hyphen_list *get_hyphen_list(hyphen_list *, int *);
  hyphenation_type get_hyphenation_type();
};

class space_char_hmotion_node : public hmotion_node {
public:
  space_char_hmotion_node(hunits, color *, node * = 0);
  space_char_hmotion_node(hunits, color *, statem *, int, node * = 0);
  node *copy();
  void ascii_print(ascii_output_file *);
  void asciify(macro *);
  void tprint(troff_output_file *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  node *add_self(node *, hyphen_list **);
  hyphen_list *get_hyphen_list(hyphen_list *, int *);
  hyphenation_type get_hyphenation_type();
};

class vmotion_node : public node {
  vunits n;
  color *col;			/* for grotty */
public:
  vmotion_node(vunits, color *);
  vmotion_node(vunits, color *, statem *, int);
  void tprint(troff_output_file *);
  node *copy();
  vunits vertical_width();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class hline_node : public node {
  hunits x;
  node *n;
public:
  hline_node(hunits, node *, node * = 0);
  hline_node(hunits, node *, statem *, int, node * = 0);
  ~hline_node();
  node *copy();
  hunits width();
  void tprint(troff_output_file *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class vline_node : public node {
  vunits x;
  node *n;
public:
  vline_node(vunits, node *, node * = 0);
  vline_node(vunits, node *, statem *, int, node * = 0);
  ~vline_node();
  node *copy();
  void tprint(troff_output_file *);
  hunits width();
  vunits vertical_width();
  void vertical_extent(vunits *, vunits *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class dummy_node : public node {
public:
  dummy_node(node *nd = 0) : node(nd) {}
  node *copy();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  hyphenation_type get_hyphenation_type();
};

class transparent_dummy_node : public node {
public:
  transparent_dummy_node(node *nd = 0) : node(nd) {}
  node *copy();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  int ends_sentence();
  hyphenation_type get_hyphenation_type();
};

class zero_width_node : public node {
  node *n;
public:
  zero_width_node(node *);
  zero_width_node(node *, statem *, int);
  ~zero_width_node();
  node *copy();
  void tprint(troff_output_file *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  void append(node *);
  int character_type();
  void vertical_extent(vunits *, vunits *);
};

class left_italic_corrected_node : public node {
  node *n;
  hunits x;
public:
  left_italic_corrected_node(node * = 0);
  left_italic_corrected_node(statem *, int, node * = 0);
  ~left_italic_corrected_node();
  void tprint(troff_output_file *);
  void ascii_print(ascii_output_file *);
  void asciify(macro *);
  node *copy();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  hunits width();
  node *last_char_node();
  void vertical_extent(vunits *, vunits *);
  int ends_sentence();
  int overlaps_horizontally();
  int overlaps_vertically();
  hyphenation_type get_hyphenation_type();
  tfont *get_tfont();
  int character_type();
  hunits skew();
  hunits italic_correction();
  hunits subscript_correction();
  hyphen_list *get_hyphen_list(hyphen_list *, int *);
  node *add_self(node *, hyphen_list **);
  node *merge_glyph_node(glyph_node *);
};

class overstrike_node : public node {
  node *list;
  hunits max_width;
public:
  overstrike_node();
  overstrike_node(statem *, int);
  ~overstrike_node();
  node *copy();
  void tprint(troff_output_file *);
  void overstrike(node *);	// add another node to be overstruck
  hunits width();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  node *add_self(node *, hyphen_list **);
  hyphen_list *get_hyphen_list(hyphen_list *, int *);
  hyphenation_type get_hyphenation_type();
};

class bracket_node : public node {
  node *list;
  hunits max_width;
public:
  bracket_node();
  bracket_node(statem *, int);
  ~bracket_node();
  node *copy();
  void tprint(troff_output_file *);
  void bracket(node *);	// add another node to be overstruck
  hunits width();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class special_node : public node {
  macro mac;
  tfont *tf;
  color *gcol;
  color *fcol;
  int no_init_string;
  void tprint_start(troff_output_file *);
  void tprint_char(troff_output_file *, unsigned char);
  void tprint_end(troff_output_file *);
public:
  special_node(const macro &, int = 0);
  special_node(const macro &, tfont *, color *, color *, statem *, int,
	       int = 0);
  node *copy();
  void tprint(troff_output_file *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  int ends_sentence();
  tfont *get_tfont();
};

class suppress_node : public node {
  int is_on;
  int emit_limits;	// must we issue the extent of the area written out?
  symbol filename;
  char position;
  int  image_id;
public:
  suppress_node(int, int);
  suppress_node(symbol, char, int);
  suppress_node(int, int, symbol, char, int, statem *, int);
  suppress_node(int, int, symbol, char, int);
  node *copy();
  void tprint(troff_output_file *);
  hunits width();
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
private:
  void put(troff_output_file *, const char *);
};

class tag_node : public node {
public:
  string tag_string;
  int delayed;
  tag_node();
  tag_node(string, int);
  tag_node(string, statem *, int, int);
  node *copy();
  void tprint(troff_output_file *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
  int ends_sentence();
};

struct hvpair {
  hunits h;
  vunits v;
  hvpair();
};

class draw_node : public node {
  int npoints;
  font_size sz;
  color *gcol;
  color *fcol;
  char code;
  hvpair *point;
public:
  draw_node(char, hvpair *, int, font_size, color *, color *);
  draw_node(char, hvpair *, int, font_size, color *, color *, statem *, int);
  ~draw_node();
  hunits width();
  vunits vertical_width();
  node *copy();
  void tprint(troff_output_file *);
  int same(node *);
  const char *type();
  int force_tprint();
  int is_tag();
};

class charinfo;
node *make_node(charinfo *, environment *);
bool character_exists(charinfo *, environment *);

int same_node_list(node *, node *);
node *reverse_node_list(node *);
void delete_node_list(node *);
node *copy_node_list(node *);

int get_bold_fontno(int);

inline hyphen_list::hyphen_list(unsigned char code, hyphen_list *p)
: hyphen(0), breakable(0), hyphenation_code(code), next(p)
{
}

extern void read_desc();
extern bool mount_font(int, symbol, symbol = NULL_SYMBOL);
extern int check_font(symbol, symbol);
extern int check_style(symbol);
extern bool mount_style(int, symbol);
extern int is_good_fontno(int);
extern int symbol_fontno(symbol);
extern int next_available_font_position();
extern void init_size_table(int *);
extern int get_underline_fontno();

class output_file {
  char make_g_plus_plus_shut_up;
public:
  output_file();
  bool is_dying;
  virtual ~output_file();
  virtual void trailer(vunits);
  virtual void flush() = 0;
  virtual void transparent_char(unsigned char) = 0;
  virtual void print_line(hunits x, vunits y, node *n,
			  vunits before, vunits after, hunits width) = 0;
  virtual void begin_page(int pageno, vunits page_length) = 0;
  virtual void copy_file(hunits x, vunits y, const char *filename) = 0;
  virtual int is_printing() = 0;
  virtual void put_filename(const char *, int);
  virtual void on();
  virtual void off();
#ifdef COLUMN
  virtual void vjustify(vunits, symbol);
#endif /* COLUMN */
  mtsm state;
};

#ifndef POPEN_MISSING
extern char *pipe_command;
#endif

extern output_file *the_output;
extern void init_output();
int in_output_page_list(int);

class font_family {
  int *map;
  int map_size;
public:
  const symbol nm;
  font_family(symbol);
  ~font_family();
  int make_definite(int);
  static void invalidate_fontno(int);
};

font_family *lookup_family(symbol);
symbol get_font_name(int, environment *);
symbol get_style_name(int);
extern search_path include_search_path;
