/* Simple BBC News ticker client
 * (c) Darren Salt
 * GPL applies
 * $Id: save.c,v 1.10 2007/04/25 14:11:15 ds Exp $
 */

/* System includes */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <swis.h>

/* Program includes */

#include "globals.h"

#include "configure.h"
#include "fetchers.h"
#include "wimp.h"

static int Browse;


static int
save_close (FILE *out, const char *name, int type)
{
  int error = 0;

  if (!out || ferror (out))
  {
    report_oserrno ();
    error = 1;
  }

  if (out && out != stdout && fclose (out))
  {
    report_oserrno ();
    error = 1;
  }

  if (!error && name)
  {
    _kernel_oserror *e = _swix (OS_File, _INR (0, 2), 18, name, type);
    if (e)
    {
      report_oserror (e);
      error = 1;
    }
  }

  return error;
}


static int
get_server (struct server *server)
{
  *server = fetcher_rdf;
  switch (parse_server
	  (server, get_icon_text (wind.servers, 4),
	   get_icon_text (wind.servers, 5), get_icon_text (wind.servers, 6)))
  {
  case 1:
    report_error ("Invalid URL");
    return 1;
  case 2:
    report_error ("Invalid port number");
    return 1;
  }
  return 0;
}


static void
fprint_server (FILE *out, struct server *server)
{
  if (server->port == 80)
    fprintf (out, "http://%s/%s", server->server, server->stories_path);
  else
    fprintf (out, "http://%s:%i/%s", server->server, server->port,
	     server->stories_path);
}


/* Save an ANT URL file. */

static int
save_ant_url (const char name[], const struct story *story)
{
  FILE *out;
  struct server server;

  if (story)
  {
    out = fopen (name, "w");
    if (out)
      fputs (story->url, out);
    return save_close (out, name, 0xB28);
  }

  if (get_server (&server))
    return 1;

  out = fopen (name, "w");
  if (out)
    fprint_server (out, &server);

  return save_close (out, name, 0xB28);
}


/* Save an Acorn URI file. */

static int
save_acorn_uri (const char name[], const struct story *story)
{
  FILE *out;
  struct server server;

  if (!story && get_server (&server))
    return 1;

  out = fopen (name, "w");
  if (out)
  {
    fprintf (out, "URI\t100\n\t# Ticker v%s (%s)\n\n\t",
	     version, date);
    if (story)
      fprintf (out, "%s\n\t%s: %s", story->url, fetcher.label, story->headline);
    else
    {
      fprint_server (out, &server);
      fprintf (out, "\n\t%s", server.label);
    }
  }

  return save_close (out, name, 0xF91);
}


/* Non-WIMP HTML output code */

static const char ael_glyphs[32][8] = {
  "euro", "#372", "#373", "#131", "#132", "#374", "#375", "#135",
  "#136", "#137", "#138", "#139", "hellip", "trade", "permil", "bull",
  "lsquo", "rsquo", "lsaquo", "rsaquo", "ldquo", "rdquo", "bdquo",
  "endash", "emdash", "minus", "OElig", "oelig", "dagger", "Dagger",
  "xFB01", "xFB02"
};


static void
print_encoded (FILE *html, const char *string)
{
  char c;
  while ((c = *string++) != 0)
    if (c >= 128 && 159 >= c)
      fprintf (html, "&%s;", ael_glyphs[c & 31]);
    else if (c < 32 || c > 126)
      fprintf (html, "&#%i;", c);
    else
      switch (c)
      {
      case '"':
	fputs ("&quot;", html);
	break;
      case '&':
	fputs ("&amp;", html);
	break;
      case '<':
	fputs ("&lt;", html);
	break;
      case '>':
	fputs ("&gt;", html);
	break;
      default:
	fputc (c, html);
      }
}


static void
print_urlencoded (FILE *html, const char *string)
{
  char c;
  while ((c = *string++) != 0)
    if (c == '&' && !Browse)
      fprintf (html, "&amp;");
    else if (c < 32 || c > 126 || c == '"' || c == '<' || c == '>')
      fprintf (html, "%%%02x", c);
    else
      fputc (c, html);
}


static void
print_headline (FILE *html, const struct story *story,
		const char ltag[], const char atag[], const char dtag[])
{
  if (ltag)
    fprintf (html, "<%s>", ltag);

  if (atag)
    fprintf (html, "<%s>", atag);

  if (story->url && *story->url)
  {
    fputs ("<a href=\"", html);
    print_urlencoded (html, story->url);
    fputs ("\">", html);
    print_encoded (html, story->headline);
    fputs ("</a>", html);
  }
  else
    print_encoded (html, story->headline);

  if (atag)
    fprintf (html, "</%s>", atag);

  if (story->description)
  {
    fprintf (html, dtag ? "<%s>" : " - ", dtag);
    print_encoded (html, story->description);
    if (dtag)
      fprintf (html, "</%s>", dtag);
  }

  if (story->publish_date)
  {
    fprintf (html, dtag ? " <%s><small>" : " <small>- ", dtag);
    print_encoded (html, story->publish_date);
    fprintf (html, dtag ? "</small></%s>" : "</small>", dtag);
  }

  fprintf (html, ltag ? "</%s>\n" : "\n", ltag);
}


/* Output the ticker data as HTML. Returns non-0 on error (and reports it).
 */

int
ticker_html (const char file[])
{
  FILE *html;

  static const char header[] =
    "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
    "<html>\n"
    "<head>\n"
    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n"
    "<title>";
  static const char postheader[] =
    "</title>\n"
    "</head>\n"
    "<body>\n";
  static const char footer[] = "\n</body>\n</html>\n";

  _kernel_last_oserror ();
  html = file ? fopen (file, "w") : stdout;

  if (html && ticker_data.stories)
  {
    int i;

    fprintf (html, "%s%s%s", header, ticker_data.title.headline, postheader);
    print_headline (html, &ticker_data.title, 0, "h1", "p");
    fputs ("<dl>\n", html);
    for (i = 0; i < ticker_data.num_headings; i++)
    {
      print_headline (html, &ticker_data.stories[i][0], "dt", 0, 0);
      if (ticker_data.stories[i])
      {
	int j = 0;
	while (ticker_data.stories[i][++j].headline)
	  print_headline (html, &ticker_data.stories[i][j], "dd", 0, 0);
      }
    }
    fprintf (html, "%s%s", "</dl>", footer);
  }
  else if (html == stdout)
    fprintf (html, "%s%s%s", header, "<h1>Nothing fetched.</h1>", footer);

  {
    int length;
    char *name;
    if (html && html == stdout && stdout->__file && !ferror (html)
	&& !_swix (OS_Args, _INR (0, 2) | _IN (5) | _OUT (5), 7,
		   html->__file, 0, 0, &length)
	&& (name = malloc (length + 1)) != 0)
    {
      int error;
      _swi (OS_Args, _INR (0, 2) | _IN (5), 7, html->__file, name, length);
      error = save_close (html, name, 0xFAF);
      free (name);
      return error;
    }
  }
  return save_close (html, file, 0xFAF);
}


/* Output the ticker data as plain text.
 * Returns non-0 on error (and reports it).
 */

int
ticker_text (const char file[])
{
  FILE *text;

  _kernel_last_oserror ();
  text = file ? fopen (file, "w") : stdout;

  fprintf (text, "%s\n", ticker_data.title.headline);
  if (ticker_data.title.description)
    fprintf (text, "  %s\n", ticker_data.title.description);
  if (ticker_data.title.url)
    fprintf (text, "=> %s\n", ticker_data.title.url);
  fputs ("\n", text);

  if (text && ticker_data.stories)
  {
    int i;

    for (i = 0; i < ticker_data.num_headings; i++)
    {
      if (ticker_data.stories[i])
      {
	int j = 0;
	fprintf (text, "%s\n\n", ticker_data.stories[i][0].headline);
	while (ticker_data.stories[i][++j].headline)
	{
	  fprintf (text, "  %s\n", ticker_data.stories[i][j].headline);
	  if (ticker_data.stories[i][j].description)
	    fprintf (text, "    %s\n", ticker_data.stories[i][j].description);
	  if (ticker_data.stories[i][j].publish_date)
	    fprintf (text, "   @ %s\n", ticker_data.stories[i][j].publish_date);
	  if (ticker_data.stories[i][j].url)
	    fprintf (text, "  => %s\n", ticker_data.stories[i][j].url);
	  fputs ("\n", text);
	}
      }
    }
  }
  else if (text == stdout)
    fprintf (text, "Nothing fetched.\n");

  {
    int length;
    char *name;
    if (text && text == stdout && stdout->__file && !ferror (text)
	&& !_swix (OS_Args, _INR (0, 2) | _IN (5) | _OUT (5), 7,
		   text->__file, 0, 0, &length)
	&& (name = malloc (length + 1)) != 0)
    {
      int error;
      _swi (OS_Args, _INR (0, 2) | _IN (5), 7, text->__file, name, length);
      error = save_close (text, name, 0xFFF);
      free (name);
      return error;
    }
  }
  return save_close (text, file, 0xFFF);
}


/* Return an appropriate filetype. */

int
save_type (int window)
{
  return window == wind.save_text
	 ? (icon_ticked (wind.save_text, 4) ? 0xFAF : 0xFFF)
	 : (icon_ticked (wind.save_link, 4) ? 0xF91 : 0xB28);
}


/* Save control. Returns 0 if successful. */

int
do_save (int window, const char name[], const struct story *story, int task)
{
  const char *taskname;
  Browse = task
    && !_swix (TaskManager_TaskNameFromHandle, _IN (0) | _OUT (0), task,
	       &taskname) && !memcmp (taskname, "Browse", 6)
    && taskname[6] < ' ';

  switch (save_type (window))
  {
  case 0xB28:
    return save_ant_url (name, story);
  case 0xF91:
    return save_acorn_uri (name, story);
  case 0xFAF:
    return ticker_html (name);
  case 0xFFF:
    return ticker_text (name);
  }
  return 1;
}
