/*
 infinity.c: the Book of Infinity, adapted for skalibs.
 Original work by David Madore: http://www.madore.org/~david/
 Adaptation by Laurent Bercot: http://www.skarnet.org/
 License: GNU GPL v2, with the special plea that the text of the Book
  not be modified.
 The original code can be found at:
  http://www.madore.org/~david/programs/programs-1.36.html#prog_infinity
*/

#include "bytestr.h"
#include "env.h"
#include "buffer.h"
#include "stralloc.h"
#include "md5.h"

#define LEN1 45
static unsigned char const STRING1[LEN1] = "Wake! For the Sun, who scatter'd into flight\n" ;
#define LEN2 46
static unsigned char const STRING2[LEN2] = "The Stars before him from the Field of Night,\n" ;
#define LEN3 58
static unsigned char const STRING3[LEN3] = "\240\240\240Drives Night along with them from Heav'n, and strikes\n" ;
#define LEN4 43
static unsigned char const STRING4[LEN4] = "The Sult\341n's Turret with a Shaft of Light.\n" ;

#define die() return(111)


typedef struct magic_string_state_s magic_string_state_t, *magic_string_state_t_ref ;
struct magic_string_state_s
{
  char vowel ;
  char sequent ;
} ;

#define MAGIC_STRING_STATE_ZERO { 0, 0 } ;

static char const *magic_string (unsigned char data_byte, magic_string_state_t_ref st, char final)
{
  char const *s ;
  char oldsequent = st->sequent ;
  if (((data_byte % 7) == 0 ) && st->sequent && !final)
  {
    st->sequent = 0 ;
    return " " ;
  }
  st->sequent = 1 ;
  if (st->vowel)
  {
    st->vowel = 0 ;
    if (data_byte < 40) s = "a" ;
    else if (data_byte < 80) s = "e" ;
    else if (data_byte < 115) s = "i" ;
    else if (data_byte < 145) s = "o" ;
    else if (data_byte < 175) s = "u" ;
    else if (data_byte < 195) s = "y" ;
    else if (data_byte < 200) s = "ai" ;
    else if (data_byte < 205) s = "ei" ;
    else if (data_byte < 210) s = "oi" ;
    else if (data_byte < 215) s = "au" ;
    else if (data_byte < 220) s = "eu" ;
    else if (data_byte < 225) s = "ou" ;
    else if (data_byte < 230) s = "ae" ;
    else if (data_byte < 235) s = "oe" ;
    else if (data_byte < 240) s = "aa" ;
    else if (data_byte < 250) s = "'" ;
    else
    {
      s = "" ;
      st->sequent = oldsequent ;
    }
  }
  else
  {
    st->vowel = 1 ;
    if ( data_byte < 12 ) s = "p" ;
    else if (data_byte < 24) s = "b" ;
    else if (data_byte < 36) s = "t" ;
    else if (data_byte < 48) s = "d" ;
    else if (data_byte < 60) s = "k" ;
    else if (data_byte < 72) s = "g" ;
    else if (data_byte < 84) s = "s" ;
    else if (data_byte < 96) s = "z" ;
    else if (data_byte < 108) s = "sh" ;
    else if (data_byte < 120) s = "zh" ;
    else if (data_byte < 132) s = "f" ;
    else if (data_byte < 144) s = "v" ;
    else if (data_byte < 156) s = "ch" ;
    else if (data_byte < 168) s = "j" ;
    else if (data_byte < 174) s = "ks" ;
    else if (data_byte < 180) s = "x" ;
    else if (data_byte < 192) { s = "h" ; st->vowel = (data_byte < 190) ; }
    else if (data_byte < 204) { s = "n" ; st->vowel = (data_byte < 198) ; }
    else if (data_byte < 216) { s = "r" ; st->vowel = (data_byte < 210) ; }
    else if (data_byte < 222) s = "st" ;
    else if (data_byte < 228) s = "zd" ;
    else if (data_byte < 234) s = "ts" ;
    else if (data_byte < 240) s = "dz" ;
    else if (data_byte < 242) s = "pn" ;
    else if (data_byte < 244) s = "bn" ;
    else if (data_byte < 247) s = "sht" ;
    else if (data_byte < 250) s = "zhd" ;
    else
    {
      s = "" ;
      st->sequent = oldsequent ;
    }
  }
  return s ;
}

static int page_make_name (stralloc *sa, unsigned char const *seed, unsigned int seedlen)
{
  MD5Schedule context = MD5_INIT() ;
  magic_string_state_t st = MAGIC_STRING_STATE_ZERO ;
  unsigned char data[64] ;

  md5_update(&context, STRING1, LEN1) ;
  md5_update(&context, seed, seedlen) ;
  md5_final(&context, data) ;
  md5_init(&context) ;
  md5_update(&context, STRING2, LEN2) ;
  md5_update(&context, seed, seedlen) ;
  md5_final(&context, data + 16) ;
  md5_init(&context) ;
  md5_update(&context, STRING3, LEN3) ;
  md5_update(&context, seed, seedlen) ;
  md5_final(&context, data + 32) ;
  md5_init(&context) ;
  md5_update(&context, STRING4, LEN4) ;
  md5_update(&context, seed, seedlen) ;
  md5_final(&context, data + 48) ;

  {
    register unsigned int i = 0 ;
    for (; i < 64 ; i++)
      if (!stralloc_cats(sa, magic_string(data[i], &st, i == 63)))
        return 0 ;
  }
  return 1 ;
}

int main (void)
{
  stralloc url_start = STRALLOC_ZERO ;
  stralloc page_name = STRALLOC_ZERO ;
  stralloc new_path = STRALLOC_ZERO ;
  stralloc new_page_name = STRALLOC_ZERO ;
  char const *script_name = env_get("SCRIPT_NAME") ;
  unsigned int new_path_start ;
  unsigned int i = 0 ;

  if (!script_name) script_name = "infinity.cgi" ;

  {
    register char const *x = env_get("PATH_INFO") ;
    if (!x || !*x) x = "/" ;
    if (!stralloc_cats(&new_path, x)) die() ;
    x = env_get("SERVER_NAME") ;
    if (x)
    {
      if (!stralloc_cats(&url_start, "http://")) die() ;
      if (!stralloc_cats(&url_start, x)) die() ;
      x = env_get("SERVER_PORT") ;
      if (x && str_diff(x, "80"))
      {
        if (!stralloc_catb(&url_start, ":", 1)) die() ;
        if (!stralloc_cats(&url_start, x)) die() ;
      }
    }
  }

  if (!page_make_name(&page_name, new_path.s, new_path.len)) die() ;
  buffer_putsalign(buffer_1, "Last-Modified: Sun, 28 Dec 2003 20:15:00 GMT\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\r\n<html>\r\n<head>\r\n<title>") ;
  buffer_putalign(buffer_1, page_name.s, page_name.len) ;
  buffer_putsalign(buffer_1, "</title>\r\n<meta name=\"robots\" content=\"noindex, nofollow\" />\r\n<meta name=\"description\" content=\"One Page from the Book of Infinity\" />\r\n<meta name=\"keywords\" content=\"infinite web space ") ;
  buffer_putalign(buffer_1, page_name.s, page_name.len) ;
  buffer_putsalign(buffer_1, "\" />\r\n</head>\r\n<body>\r\n<h1>") ;
  buffer_putalign(buffer_1, page_name.s, page_name.len) ;
  buffer_putsalign(buffer_1, "</h1>\r\n<p> You are in a maze of twisty little web pages, all alike. </p>\r\n<p> From here you can get to the following places: </p>\r\n<ul>\r\n") ;

  new_path_start = new_path.len ;
  for (; i < 14 ; i++)
  {
    new_path.len = new_path_start ;
    buffer_putsalign(buffer_1, "<li>") ;
    switch (i)
    {
      case 0 :
      case 13 :
      {
        while (new_path.len > 1) if (new_path.s[--new_path.len - 1] == '/') break ;
        if (!i) buffer_putsalign(buffer_1, "Back to ") ;
        else
        {
          if (!stralloc_catb(&new_path, page_name.s, byte_chr(page_name.s, page_name.len, ' '))) die() ;
          if (!stralloc_catb(&new_path, "/", 1)) die() ;
        }
        break ;
      }
      default:
      {
        char const *const zodiac[12] = { "aries", "taurus", "gemini", "cancer", "leo", "virgo", "libra", "scorpius", "sagittarius", "capricornus", "aquarius", "pisces" } ;
        if (!stralloc_cats(&new_path, zodiac[i-1])) die() ;
        if (!stralloc_catb(&new_path, "/", 1)) die() ;
      }
    }
    buffer_putsalign(buffer_1, "<a href=\"") ;
    if (url_start.s) buffer_putalign(buffer_1, url_start.s, url_start.len) ;
    buffer_putsalign(buffer_1, script_name) ;
    buffer_putalign(buffer_1, new_path.s, new_path.len) ;
    buffer_putsalign(buffer_1, "\">") ;
    new_page_name.len = 0 ;
    if (!page_make_name(&new_page_name, new_path.s, new_path.len)) die() ;
    buffer_putalign(buffer_1, new_page_name.s, new_page_name.len) ;
    buffer_putsalign(buffer_1, "</a></li>\r\n") ;
  }
  buffer_putsflush(buffer_1, "</ul>\r\n</body>\r\n</html>\r\n") ;
  return 0 ;
}

