<?php

class Weborg {
  private $todo_keywords = array("TODO");
  private $done_keywords = array("DONE");

  function Weborg($file) {
    $this->filename = $file;
  }

  function next_line() {
    $this->line = fgets($this->file);
    $this->lineno++;
    return $this->line;
  }

  function parse_title() {
    while (preg_match("/^\s*$/", $this->line)) {
      $this->next_line();
    }
    if (preg_match("/^\*+/", $this->line)) {
      $title = preg_replace("/\.org$/", "", $this->filename);
      $title = array($title, "filename");
    }
    else {
      $title = array($this->line, "line_" . $this->lineno);
      $this->next_line();
    }
    return $title;
  }

  function parse_timestamps() {
    $timestamps = array();
    $keyword = "CLOSED|SCHEDULED|DEADLINE";
    $date = "(?:<.*>)|(?:\[.*\])";

    $continue = true;
    while ($this->line && $continue) {
      $matches = array();
      if (preg_match("/^\s*($date)\s*$/", $this->line, $matches)) {
        $t = array("timestamp", htmlentities($matches[1]));
        array_push($timestamps, $t);
      }
      else
        if (preg_match("/^\s*($date)--($date)\s*$/", $this->line, $matches)) {
          $t = array("range", htmlentities($matches[1]), htmlentities($ranges[2]));
          array_push($timestamps, $t);
        }
        else
          if (preg_match("/^\s*($keyword)\s*:\s*($date)$/", $this->line, $matches)) {
            $t = array($matches[1], htmlentities($matches[2]));
            array_push($timestamps, $t);
          }
          else
            return $timestamps;
      $this->next_line();
    }
  }

  function parse_text() {
    $parsed = array();
    $raw = "";

    while ($this->line && ! preg_match("/^\*+/", $this->line)) {
      $found_stars = false;
      if (preg_match("/^\s+-\s+/", $this->line)) {
        $itemize = array();
        $current = array();
        $previous = "";
        while ($this->line && ! (preg_match("/^\s*$/", $this->line) || preg_match("/^\*+/", $this->line))) {
          $matches = array();
          if (preg_match("/^\s+-\s*(\s\[(?: |X)\])?\s+(.*)/", $this->line, $matches)) {
            if ($previous == " [ ]")
              array_push($itemize, array(1, $this->lineno, $current));
            else
              if ($previous == " [X]") {
                array_push($itemize, array(2, $this->lineno, $current));
              }
              else
                array_push($itemize, array(0, $this->lineno, $current));
            $previous = $matches[1];
            $current = array($matches[2]);
          }
          else
            array_push($current, $this->line);

          $raw .= $this->line;
          $this->next_line();
        }
        if (preg_match("/^\*+/", $this->line))
          $found_stars = true;
        if ($previous == " [ ]")
          array_push($itemize, array(1, $this->lineno, $current));
        else
          if ($previous == " [X]") {
            array_push($itemize, array(2, $this->lineno, $current));
          }
          else
            array_push($itemize, array(0, $this->lineno, $current));
        array_push($parsed, array_slice($itemize, 1));
      }
      else {
        array_push($parsed, $this->line);
        $raw .= $this->line;
      }
      if (! $found_stars)
        $this->next_line();
    }
    return array($parsed, $raw);
  }

  function parse_headings($n) {
    $headings = array();

    $level = $n;
    while ($this->line && $level >= $n) {
      $matches = array();

      $keywords = join("|", $this->todo_keywords) . "|" . join("|", $this->done_keywords);
      if (preg_match("/^(\*+)\s*($keywords)?\s+(.*?)\s+(:.*:)?$/", $this->line, $matches)) {
        $level = strlen($matches[1]);

        if ($level == $n) {
          $line_begin = $this->lineno;
          $this->next_line($file);
          $timestamps = $this->parse_timestamps();
          $text = $this->parse_text();
          $children = $this->parse_headings($n + 1);
          $heading = array(
                           "level"      => $level,
                           "keyword"    => $matches[2],
                           "title"      => $matches[3],
                           "tags"       => $matches[4],
                           "timestamps" => $timestamps,
                           "text"       => $text,
                           "children"   => $children,
                           "line_begin" => $line_begin,
                           "line_end"   => $children[-1][line_end],
                           );
          array_push($headings, $heading);
        }
      }
      else
        $this->next_line($file);
    }
    return $headings;
  }

  function parse() {
    $this->file = fopen($this->filename, "r");
    $this->lineno = 0;
    $this->next_line();

    $this->title = $this->parse_title();
    $this->headings = $this->parse_headings(1);

    fclose($this->file);
  }

  function render_html_timestamps($timestamps) {
    print "<div class=\"timestamps\">\n";
    foreach ($timestamps as $t) {
      if ($t[0] == "timestamp") {
        print "<span class=\"timestamp\">$t[1]</span><br/>\n";
      }
      else
        if ($t[0] == "range") {
          print "<span class=\"timestamp-kwd\">$t[0]: </span> <span class=\"timestamp\">$t[1]--$t[2]</span><br/>\n";
        }
        else
          print "<span class=\"timestamp-kwd\">$t[0]: </span> <span class=\"timestamp\">$t[1]</span><br/>\n";
    }
    print "</div>\n";
  }
 
  function render_html_text($text) {
    print "<div class=\"text\">\n";
    foreach ($text as $p) {
      if (is_array($p)) {
        print "<ul>\n";
        foreach ($p as $li) {
          print "<li>\n";
          if ($li[0] == 1) {
            print "<input type=\"checkbox\" id=\"$li[1]\"/>";
          }
          else
            if ($li[0] == 2) {
              print "<input type=\"checkbox\" checked=\"1\" id=\"$li[1]\"/>";
            }
          foreach ($li[2] as $l) {
            print $l . "<br />";
          }
          print "</li>\n";
        }
        print "</ul>\n";
      }
      else {
        print $p . "</br>";
      }
    }
    print "</div>\n";
  }
  function render_html_headings($headings) {
    foreach ($headings as $h) {
      $level      = $h['level'];
      $keyword    = $h['keyword'];
      $title      = $h['title'];
      $tags       = $h['tags'];
      $line       = $h['line_begin'];
      $timestamps = $h['timestamps'];
      $text       = $h['text'];

      $n = $level + 1;
      print "<h$n class=\"heading\" id=\"$line\">\n";
      print "<span class=\"stars\">";
      for ($i = 0; $i < $level; $i++)
        print "*";
      print "</span> ";
      if ($keyword != "") {
        print "<span class=\"$keyword\">$keyword</span> ";
        }
      print $title;
      if ($tags != "") {
        print " <span class=\"tag\">$tags</span>\n";
      }
      print "</h$n>\n";
      print "<div class=\"content\">\n";
      $this->render_html_timestamps($timestamps);
      $this->render_html_text($text[0]);
      $this->render_html_headings($h[children]);
      print "</div>\n";
    }
  }

  function render_html() {
    $title = $this->title;
    print "<h1 class=\"title\" id=\"$title[1]\">$title[0]</h1>\n";

    $this->render_html_headings($this->headings);
  }

  }

?>

