
object sql;
object engine;

string ft_endpoint = "http://xapian.welliver.org:8124";
string ft_handle = "PikeWiki";
string ft_auth = "PikeWiki";

object ft_client;

mixed display_search(RequestID id)
{
  string res = "Search results for: <b>" + id->variables->q + "</b><p>";
  array qr = ft_client->search(id->variables->q);
  if(!sizeof(qr)) res += "No Results.";
  else
  {
    res += "<dl>";
    foreach(qr;;mapping r)
    {
      res += "<dt><a href=\"" + id->not_query + "?page=" + r->handle + "\">";
      res += r->title + "</a> (" + r->date + ")  [" + (int)(r->score * 100.0)+ "%]</dt><dd>";
      res += r->excerpt + "</dd><p>";
    }
    res +="</dl>";
  }
  return res;
}

mixed display_versions(RequestID id)
{
  string res="";
  string page;
  int version;
  string contents;

  page = id->variables->page;

  res+="<b>Versions of this page</b><p>\n";

  array rs = sql->query("SELECT * FROM GotPikeWiki WHERE page=%s ORDER BY version asc", page);

  if(!sizeof(rs))
  {
    res += "No versions available.";
    return res;
  }

  res +="<table>\n";
  res +="<tr><th>Version</th><th>Date Created</th><th>Author</th><th>Change Summary</th></tr>\n";
  foreach(rs; int i; mapping row)
  {
    res += "<tr><td>" + make_internal_link(id, (["page": page, "use_version": row->version]), row->version) + 
          "</td><td>" + row->created + "</td><td>"
           + row->author + "</td><td>" + row->summary + "</td>"
          "<td>" + make_internal_link(id, (["page": page, "mode": "diff",
              "diff_from": rs[i-1]->version, "diff_to": row->version]),
              "Diff to Previous") + "</tr>";
  }

  res += "</table>";

  return res;
} 


mixed display_edit(RequestID id)
{
  string res="";
  string page;
  int version;
  string contents;

  page = id->variables->page;
  version = (int)id->variables->version;

  contents = get_contents(page, version);

  if(!contents)
  {
    contents = "";
  }

  string summary = "";

  if(id->variables->summary) summary = id->variables->summary;

  res += "<a href=\"formatting.html\">Wiki Markup Help</a><br>";
  res += "<form action=\"" + id->not_query + "\" method=\"post\">\n";
  res += "<input type=\"hidden\" name=\"page\" value=\"" + page + "\">\n";
  res += "<input type=\"text\" name=\"summary\" size=\"40\" value=\"" + summary + "\"> summary<p>\n";
  res += "<textarea name=\"contents\" rows=\"20\" cols=\"80\">" + contents + "</textarea>\n";
  res += "<p>\n";
  res += "<input type=\"submit\" name=\"mode\" value=\"preview\"> ";
  res += "<input type=\"submit\" name=\"mode\" value=\"save\"> ";
  res += "</form>\n";
  res += "\n";
  return res;
} 

mixed display_preview(RequestID id)
{
  string page;
  int version;

  page = id->variables->page;
  version = (int)id->variables->version;

  string summary = "";

  if(id->variables->summary) summary = id->variables->summary;

  string res="";
  res += render_contents(id, id->variables->contents);

  res += "<form action=\"" + id->not_query + "\" method=\"post\">\n";
  res += "<input type=\"text\" name=\"summary\" size=\"40\" value=\"" + summary + "\"> summary<p>\n";
  res += "<textarea name=\"contents\" rows=\"20\" cols=\"80\">" + id->variables->contents + "</textarea>\n";
  res += "<p>\n";
  res += "<input type=\"hidden\" name=\"page\" value=\"" + page + "\">\n";
  res += "<input type=\"submit\" name=\"mode\" value=\"preview\"> ";
  res += "<input type=\"submit\" name=\"mode\" value=\"save\"> ";
  res += "</form>\n";

  return res;
}

mixed display_save(RequestID id)
{
  string page;
  int version;

  page = id->variables->page;
  version = (int)id->variables->version;

  string res="";

  string c = get_contents(page);
  if(c && String.trim_all_whites(id->variables->contents) == String.trim_all_whites(c))
  {
    return Caudium.HTTP.redirect(id->not_query + "?page=" + page + "&message=Null edit discarded.");

  }

  else  
  {
    string v = sql->query("SELECT MAX(version)+1 AS m FROM GotPikeWiki WHERE page=%s", page)[0]->m;
    string author;

    mapping u = id->get_user();

    if(u) author = u->username;
    else author = "anonymous";

    if((int)v == 0)
      v = "1";

    if((int) v==1)
    {
      Protocols.SMTP.Client("smtp.welliver.org")->simple_mail("william@welliver.org", "New Wiki Page: " + page, 
        "wiki@gotpike.org",
        "A new wiki page has been created.\n\n"
        "http://www.gotpike.org" + id->not_query + "?page=" + page);
    }

    sql->query("INSERT INTO GotPikeWiki (version, contents, page, author, created, summary) "
       "VALUES(%d, %s, %s, %s, NOW(), %s)", (int)v, id->variables->contents, page, author, 
       id->variables->summary);

   string cnt, excerpt;
    cnt = textify((render_contents(id, id->variables->contents)));
    excerpt = make_excerpt(cnt);

    object c = FullText.UpdateClient(ft_endpoint, ft_handle, ft_auth);
    c->delete_by_handle(page);
    c->add((page/"/")[-1], Calendar.Second(), (page/"/")[-1] + " " + cnt,
               page, excerpt, "text/wiki"); 

    return Caudium.HTTP.redirect(id->not_query + "?page=" + page);
  }
}

string reindex(object id)
{
    object c = FullText.UpdateClient(ft_endpoint, ft_handle, ft_auth);
string r = "";
  string q = "SELECT *,UNIX_TIMESTAMP(created) as c FROM GotPikeWiki ORDER BY created ASC";
  foreach(sql->query(q);;mapping row)
  {
    string cnt, excerpt;
    cnt = textify((render_contents(id, row->contents)));
    excerpt = make_excerpt(cnt);
    c->delete_by_handle(row->page);
    r += row->page + "<br>\n";
    r += sprintf("<pre>%O\n\n</pre>", c->add((row->page/"/")[-1], Calendar.Second((int)row->c),
               (row->page/"/")[-1] + " " + cnt,
               row->page, excerpt, "text/wiki")); 
  }

  return "OK" + "<p>" + r;
}

string make_excerpt(string c)
{
        if(sizeof(c)<500)
          return c;
   int loc = search(c, " ", 499);

        // we don't have a space?
   if(loc == -1)
        {
                c = c[0..499] + "...";
        }
        else
        {
                c = c[..loc] + "...";
        }
        
        return c;
}


string textify(string html)
{
  object p = Parser.HTML();
  p->_set_tag_callback(lambda(object parser, mixed val){return " ";});

  return p->finish(html)->read();
}


string get_contents(string page, int|void version)
{
  array res;

  if(version)
    res = sql->query("SELECT contents from GotPikeWiki WHERE page=%s AND version=%d", page, version);
  else
    res = sql->query("SELECT contents from GotPikeWiki WHERE page=%s ORDER BY version DESC LIMIT 1", page);

  if(sizeof(res)) 
    return res[0]->contents;
  else return 0;
}

string get_page_name(string page)
{
  return (page/"/")[-1];
}

string get_page_breadcrumbs(RequestID id, string page)
{
  array s = ({});
  string newcomponent;
  if(page!="Start")
    s += ({ make_internal_link(id, (["page": "Start"]), "Start") });

  foreach(page/"/", string component)
  {
    if(newcomponent)
      newcomponent=newcomponent+"/" + component;
    else
      newcomponent = component;

    s += ({ make_internal_link(id, (["page": newcomponent]), component) });
  }

  return "[ " + (s * " &gt; ") + " ]";
}

string get_header(RequestID id, string page)
{
  string res = "<use file=\"/gotpike_package\">";
  res += "<page title=\"" + page + "\">\n";
  res += "<font size=\"7\">" + get_page_name(page) + "</font><p>";
  res += "<table><tr><td valign=middle>";
  res += get_page_breadcrumbs(id, page);
  res += " [ ";

  if(!id->variables->mode || id->variables->mode != "edit")
  {
    res += " ";
    res += make_internal_link(id, (["page": page, "mode": "edit"]), "Edit this Page"); 
  }

  if(!id->variables->use_version && (!id->variables->mode || id->variables->mode != "versions"))
  {
    res += " | ";
    res += make_internal_link(id, (["page": page, "mode": "versions"]), "Show Page Versions"); 
  }

  if(!id->variables->show_raw && (!id->variables->mode || id->variables->mode != "versions") &&  
      (!id->variables->use_version))
  {
    res += " | ";
    res += make_internal_link(id, (["page": page, "show_raw": "1"]), "Show Raw Source"); 
  }

  else if(id->variables->show_raw)
  {
    res += " | ";
    res += make_internal_link(id, (["page": page]), "Show Formatted" );
  }

  else if(id->variables->use_version)
  {
    res += " | <i>Viewing Version " + id->variables->use_version + "</i>";
  }

  res += " ] ";
  res +="</td><td valign=middle>";
  res += "<form><input type=text name=q size=20><input type=submit value=search></form>";
  res += "</td></tr></table>";
  if(id->variables->message)
    res +="<br><font color=\"red\">" + id->variables->message + "</font>\n";

  res += "<p>\n<hr>\n";

  return res;
}

string make_internal_link(RequestID id, mapping args, string name)
{
  return "<a href=\"" + id->not_query + "?" + make_args(args) + "\">" + name + "</a>";

}

string make_args(mapping args)
{
  array a = ({});
  
  foreach(args; string i; string v)
  {
    a+=({i + "=" + v});
  }
  return (a * "&");
}

string get_footer(RequestID id, string page)
{
  string res = "";

  res += "<hr><p>\n"
         "<i>Powered by PikeWiki2</i>\n"
         "</page>";

  return res;
}

mixed parse(RequestID id)
{
  string contents;
  string page;
  array res;
  string header="";
  string footer="";
  int version;

    //werror("have query1: %O\n", id->variables);
    //werror("have query1: %O\n", id->misc);

  if(id->variables->_reindex)
  {
    return reindex(id);
  }

  if(id->misc->path_info && strlen(id->misc->path_info))
    id->variables->page = replace(id->misc->path_info, "+", " ");

  if(!id->variables->page || !strlen(id->variables->page))
    page = "Start";
  else page = id->variables->page;

  if(page[0] == '/') page=page[1..];

  if(id->variables->use_version)
    version = (int)id->variables->use_version;

  if(id->variables->mode && id->variables->mode=="save" && id->variables->contents)
  {
    return display_save(id);
  }

  header = get_header(id, page);
  footer = get_footer(id, page);

  if(id->variables->q)
  {
    //werror("have query.\n");
    return header + display_search(id) + footer;
  }

  if(id->variables->mode && id->variables->mode=="edit")
  {
    if(!id->get_user())
      return Caudium.HTTP.auth_required("wiki@gotpike.org - use your modules.gotpike.org account");

    return header + display_edit(id) + footer;
  }

  if(id->variables->mode && id->variables->mode=="preview" && id->variables->contents)
  {
    return header + display_preview(id) + footer;
  }

  if(id->variables->mode && id->variables->mode=="versions")
  {
    return header + display_versions(id) + footer;
  }

  contents = get_contents(page, version);

  if(!contents)
    contents = "__Foo!__\n\n No contents for page ~~" + page + "~~!\n"
               "\nWhy don't you contribute? Click on the ~~Edit this Page~~"
               " link above to get started.\n";

  return header + (id->variables->show_raw?("<pre>" + contents + "</pre>"):render_contents(id, contents)) + footer;

}

string render_contents(RequestID id, string contents)
{
   return engine->render(contents);
}

void create() {
  sql = Sql.Sql("mysql://gotpike:pike4u@localhost/gotpike");
  ft_client = FullText.SearchClient(ft_endpoint, ft_handle, ft_auth);
  engine = ((program)"/home/web/htdocs/gotpike/PikeWiki/GotPikeWikiEngine")(sql, this);     
 }
