/* This file is part of cqual.
   Copyright (C) 2003 The Regents of the University of California.

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

cqual 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 cqual; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "analyze.h"
#include "buffer.h"
#include "color.h"
#include "cqual.h"
#include "flow.h"
#include "utils.h"
#include "qerror.h"
#include "qtype.h"
#include "hash.h"
#include "effect.h"
#include "pam-generic.h"

static growbuf pam_vprintf_scratch_buf = NULL;
static region pam_generic_region = NULL;

/**************************************************************************
 *                                                                        *
 * Low-level PAM communication                                            *
 *                                                                        *
 **************************************************************************/

void pam_clear_buffer(const char *);      /* Erase the buffer contents */
void pam_change_buffer(const char *);     /* Switch to the buffer */
void pam_set_lower_buffer(const char *);   /* ? */
void pam_set_upper_buffer(const char *);     /* ? */
void pam_close_buffer(const char *);      /* Close (delete?) the buffer */
void pam_split_buffer(const char *);      /* ? */
void pam_goto_char_buffer(const char *, unsigned long);
                                          /* Go to character pos in a buffer */
void pam_goto_anchor_buffer(const char *, const char *);
                                          /* Go to anchor in a buffer */

void pam_clear_file(const char *);        /* See above */
void pam_change_file(const char *);
void pam_set_lower_file(const char *);
void pam_set_upper_file(const char *);
void pam_close_file(const char *);
void pam_split_file(const char *);
void pam_goto_char_file(const char *, unsigned long);
void pam_goto_anchor_file(const char *, const char *);
void pam_add_anchor(const char *name, int start, int end);

int pam_waiting(void);                  /* Tell PAM we've finished dealing
					   with the last action and we're
					   waiting for a click */
void pam_debug(const char *, ...);        /* Print a debugging message */
int pam_message(const char *, ...);       /* Print a text message */
void pam_default_path(const char *);      /* Set the default path */

void pam_blobs_begin(void);               /* Begin printing marked-up text */
void pam_blobs_end(void);                 /* End printing marked-up text */
void pam_overlays_begin(void);            /* Begin marking-up file */
void pam_overlays_end(void);              /* End marking-up file */

void pam_markup_file(const char *f);      /* Start marking up file f */
void pam_markup_buffer(const char *b);    /* Start marking up buffer b */

void prt_expression(expression, int); /* unparse.c */

/* Add some text to a mark-up */
int pam_add_text(const char *, ...);

/* Add a hyperlink to a markup.  ``text'' is displayed marked up with
   color.  ``name'' is the internal name of the hyperlink. */
void pam_add_link(const char *name, const char *color, const char *text);

/* Add some marked-up text -- no link */
void pam_add_marked_text(const char *color, const char *text);

/* Add a hyperlink on top of some text.  The text to add a link to
   ranges from positions start-end in the current markup buffer, and
   is marked up with color.  ``name'' is the internal name of the
   hyperlink. */
void pam_add_overlink(const char *name, const char *color, int start, int end);

void pam_add_overlink_anchor(const char *name, const char *anchor_name,
			     const char *color, int start, int end);

/* Add a color on top of some text -- no link */
void pam_add_overmark(const char *color, int start, int end);

/* Definitions */

#define pam_printf printf
#define pam_vprintf vprintf
#define pam_putc(c) putc(c, stdout)
#define pam_getc()  getc(stdin)

#define pam_window_buffer_msg "buffer"
#define pam_window_file_msg "file"
#define pam_click_msg "click"
#define pam_shift_click_msg "shift-click"

/* Reset any side-effected stuff */
void init_pam_generic(void)
{
  if (pam_generic_region)
    deleteregion(pam_generic_region);
  pam_generic_region = newregion();
  pam_vprintf_scratch_buf = NULL;
  pam_vprintf_scratch_buf = growbuf_new(pam_generic_region, 256);
}



/* Print out a string, adding escapes where necessary */
int pam_print_escaped_string(const char *s)
{
  int count = 0;

  pam_putc('\"');
  while (*s)
  {
    switch (*s)
    {
    case '\\': pam_printf("\\\\"); break;
    case '\"': pam_printf("\\\""); break;
    case '\n': pam_printf("\\n"); break;
    default:
      if (isprint(*s))
	pam_putc(*s);
      else
	fail("Error: 0x%x not printable\n", *s);
    }
    s++;
    count++;
  }
  pam_putc('\"');
  return count;
}

int pam_vprintf_escaped(const char *fmt, va_list args)
{
  growbuf_reset(pam_vprintf_scratch_buf);
  gvprintf(pam_vprintf_scratch_buf, fmt, args);
  return pam_print_escaped_string(growbuf_contents(pam_vprintf_scratch_buf));
}

void pam_printf_escaped(const char *fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  pam_vprintf_escaped(fmt, args);
}

typedef enum { pam_clear_window, pam_change_window, pam_set_lower_window,
	       pam_set_upper_window, pam_close_window, pam_split_window
} pam_op_kind;

void pam_window_op(pam_op_kind op, const char *wname, const char *kind)
{
  pam_putc('(');
  switch (op)
    {
    case pam_clear_window: pam_printf("pam-clear-window"); break;
    case pam_change_window: pam_printf("pam-change-window"); break;
    case pam_set_lower_window: pam_printf("pam-set-window-down"); break;
    case pam_set_upper_window: pam_printf("pam-set-window-up"); break;
    case pam_close_window: pam_printf("pam-close-window"); break;
    case pam_split_window: pam_printf("pam-split-window"); break;
    default:
      fail("Unexpected window op %d\n", op);
    }
  pam_printf(" (");
  pam_print_escaped_string(wname);
  pam_putc(' ');
  pam_print_escaped_string(kind);
  pam_printf("))\n");
}

#define mkpam_buffer_op(op) void pam_ ## op ## _buffer(const char *wname) \
    { pam_window_op(pam_ ## op ## _window, wname, pam_window_buffer_msg); }

#define mkpam_file_op(op) void pam_ ## op ## _file(const char *wname) \
    { pam_window_op(pam_ ## op ## _window, wname, pam_window_file_msg); }

mkpam_buffer_op(clear);
mkpam_buffer_op(change);
mkpam_buffer_op(set_lower);
mkpam_buffer_op(set_upper);
mkpam_buffer_op(close);
mkpam_buffer_op(split);
mkpam_file_op(clear);
mkpam_file_op(change);
mkpam_file_op(set_lower);
mkpam_file_op(set_upper);
mkpam_file_op(close);
mkpam_file_op(split);

void pam_goto_char_buffer(const char *buffer, unsigned long pos)
{
  pam_printf("(pam-goto-char (");
  pam_print_escaped_string(buffer);
  pam_putc(' ');
  pam_print_escaped_string(pam_window_buffer_msg);
  pam_printf(") %ld)\n", pos);
}

void pam_goto_char_file(const char *buffer, unsigned long pos)
{
  pam_printf("(pam-goto-char (");
  pam_print_escaped_string(buffer);
  pam_putc(' ');
  pam_print_escaped_string(pam_window_file_msg);
  pam_printf(") %ld)\n", pos);
}

void pam_goto_anchor_buffer(const char *buffer, const char *anchor)
{
  pam_printf("(pam-goto-anchor (");
  pam_print_escaped_string(buffer);
  pam_putc(' ');
  pam_print_escaped_string(pam_window_buffer_msg);
  pam_printf(") ");
  pam_print_escaped_string(anchor);
  pam_printf(")\n");
}

void pam_goto_anchor_file(const char *buffer, const char *anchor)
{
  pam_printf("(pam-goto-anchor (");
  pam_print_escaped_string(buffer);
  pam_putc(' ');
  pam_print_escaped_string(pam_window_file_msg);
  pam_printf(") ");
  pam_print_escaped_string(anchor);
  pam_printf(")\n");
}

int pam_waiting(void)
{
  return pam_printf("(pam-waiting)\n");
}

void pam_debug(const char *str, ...)
{
  va_list args;

  va_start(args, str);
  pam_printf("(pam-debug ");
  pam_vprintf_escaped(str, args);
  pam_printf(")\n");
}

int pam_message(const char *str, ...)
{
  va_list args;
  int count = 0;

  va_start(args, str);
  count += pam_printf("(pam-message ");
  count += pam_vprintf_escaped(str, args);
  count += pam_printf(")\n");
  return count;
}

void pam_default_path(const char *path)
{
  pam_printf("(pam-default-path ");
  pam_print_escaped_string(path);
  pam_printf(")\n");
}

void pam_blobs_begin(void) { pam_printf("(pam-blobs"); }
void pam_blobs_end(void)   { pam_printf(")\n"); }

void pam_overlays_begin(void) { pam_printf("(pam-overlays"); }
void pam_overlays_end(void)   { pam_printf(")\n"); }

void pam_markup_file(const char *name)
{
  pam_printf(" (file (");
  pam_print_escaped_string(name);
  pam_printf(" ");
  pam_print_escaped_string(pam_window_file_msg);
  pam_printf("))");
}

void pam_markup_buffer(const char *name)
{
  pam_printf(" (file (");
  pam_print_escaped_string(name);
  pam_printf(" ");
  pam_print_escaped_string(pam_window_buffer_msg);
  pam_printf("))");
}

int pam_add_text(const char *text, ...)
{
  int result;
  va_list args;

  va_start(args, text);
  pam_printf(" (text ");
  result = pam_vprintf_escaped(text, args);
  pam_printf(")");
  return result;
}

/* XXX Hax */
void pam_prt_expression(expression e)
{
  pam_printf(" (text ");
  prt_expression(e, 0);
  pam_printf(")");
}

/*
void pam_print_color(pam_color c)
{
  switch (c)
    {
    case pam_color_1: pam_printf("(pam-color-1)"); break;
    case pam_color_2: pam_printf("(pam-color-2)"); break;
    case pam_color_3: pam_printf("(pam-color-3)"); break;
    case pam_color_4: pam_printf("(pam-color-4)"); break;
    case pam_color_5: pam_printf("(pam-color-5)"); break;
    case pam_color_6: pam_printf("(pam-color-6)"); break;
    case pam_color_7: pam_printf("(pam-color-7)"); break;
    case pam_color_8: pam_printf("(pam-color-8)"); break;
    case pam_color_mouse: pam_printf("(pam-color-mouse)"); break;
    default:
      fail("Unexpected markup color %d\n", c);
    }
}
*/

void pam_add_markup(int start, int end, const char *color, const char *text)
{
  pam_printf(" (markup (%d %d) (%s) ", start, end, color);
  pam_print_escaped_string(text);
  pam_printf(")");
}

void pam_add_hyper(const char *name, int start, int end, const char *color,
		   const char *text)
{
  pam_printf(" (hyper ");
  pam_print_escaped_string(name);
  pam_printf(" (%d %d) (%s) ", start, end, color);
  pam_print_escaped_string(text);
  pam_printf(")");
}

void pam_add_hyper_anchor(const char *name, const char *anchor_name,
			  int start, int end, const char *color,
			  const char *text)
{
  pam_printf(" (hyper-anchor ");
  pam_print_escaped_string(name);
  pam_printf(" ");
  pam_print_escaped_string(anchor_name);
  pam_printf(" (%d %d) (%s) ", start, end, color);
  pam_print_escaped_string(text);
  pam_printf(")");
}

void pam_add_anchor(const char *name, int start, int end) {
  pam_printf(" (anchor ");
  pam_print_escaped_string(name);
  pam_printf(" (%d %d))", start, end);
}

/* Add a hyperlink to a markup.  ``text'' is displayed marked up with
   color.  ``name'' is the internal name of the hyperlink. */
void pam_add_link(const char *name, const char *color, const char *text)
{
  pam_add_hyper(name, -1, -1, color, text);
}

/* Add some marked-up text -- no link */
void pam_add_marked_text(const char *color, const char *text)
{
  pam_add_markup(-1, -1, color, text);
}

/* Add a hyperlink on top of some text.  The text to add a link to
   ranges from positions start-end in the current markup buffer, and
   is marked up with color.  ``name'' is the internal name of the
   hyperlink. */
void pam_add_overlink(const char *name, const char *color, int start, int end)
{
  pam_add_hyper(name, start, end, color, "");
}

void pam_add_overlink_anchor(const char *name, const char *anchor_name,
			     const char *color, int start, int end)
{
  pam_add_hyper_anchor(name, anchor_name, start, end, color, "");
}

/* Add a color on top of some text -- no link */
void pam_add_overmark(const char *color, int start, int end)
{
  pam_add_markup(start, end, color, "");
}
