<?php
/**
 * Plugin dbtables: Generate a series of tables 
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Stephen C <pontiac76@gmail.com>
 */

// must be run within DokuWiki
if(!defined('DOKU_INC')) die();

if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once DOKU_PLUGIN.'syntax.php';
require_once DOKU_INC.'inc/parser/parser.php';
require_once DOKU_INC . 'inc/parser/xhtml.php';

/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_dbtables extends DokuWiki_Syntax_Plugin {

    // $options is used for rendering options    
    public $options=array();
    
    function getInfo() {
        return array('author' => 'Stephen C',
                     'email'  => 'pontiac76@gmail.com',
                     'date'   => '2010-09-29',
                     'name'   => 'DB Tables',
                     'desc'   => 'Draw Database Tables in a Wiki',
                     'url'    => 'http://ginger.gotdns.com/public/wiki/download/dbtables.zip');
    }

    function getType() { return 'substition'; }
    function getSort() { return 32; }

    function connectTo($mode) {
        $this->Lexer->addEntryPattern('<dbtables *[^>]*>',$mode,'plugin_dbtables');
    }
 
    function postConnect() {
        $this->Lexer->addExitPattern('</dbtables>','plugin_dbtables');
    }

    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){
        switch ($state) {
            case DOKU_LEXER_ENTER : 
              return array($state, substr($match, 10, -1) );
              break;
            case DOKU_LEXER_MATCHED :
              return array($state,$match);
              break;
            case DOKU_LEXER_UNMATCHED :
              return array($state, $match);
              break;
            case DOKU_LEXER_EXIT :
              return array($state, '');
              break;
        }
        return array();
    }
     
    function render_tables($match,$mode,$data) {
      // $match is the full text we're to consider
      $raw=explode("\n",$match);
      
      $CurrentTable="";

      // Run through each line and decide how to render the text      
      foreach($raw as $rawline) {
        if (trim($rawline)!=""){
          // Is this row the name of a table?
          if (substr($rawline,0,1)==$this->options["thead"]) {
            // Yes, so draw the heading
            
            // Remember the current table name
            $CurrentTable=substr($rawline,1);
            // Draw the Dokuwiki table heading
            $TableData[$CurrentTable].="^$CurrentTable".substr("^^^^^^^^^^",0,$this->options["cols"])."\n";
            // Draw the descriptors of each field
            for($ColPos=1;$ColPos<=$this->options["cols"];$ColPos++)
              $TableData[$CurrentTable].="^".$this->options["c".$ColPos]." ";
            $TableData[$CurrentTable].="^\n";
          } else {
            // No, so draw the schema for the current table
            
            // If we haven't seen a table name yet, skip it.
            if ($CurrentTable!="") {
              // Split the fields up.
              $RowInfo=explode($this->options["fdelim"],$rawline);
              // $RowsOut keeps track of how many entities we've sent.  Add 1
              $RowsOut=$this->options["cols"];
              // Go through each entity and output it
              foreach($RowInfo as $RowData) {
                if ($RowsOut>0) {
                  $TableData[$CurrentTable].="|".$RowData."  ";
                  $RowsOut=$RowsOut-1;
                }
              }
              // Draw the tailing wiki table identifiers, including spaces
              $EndOfTable=substr("|  |  |  |  |  |  |  |  |  |  ",0,($RowsOut+1)*3);
              // SHIP IT!
              $TableData[$CurrentTable].=trim($EndOfTable)."\n";
            }
          }
        }
      }
      // By default, we sort the table listings.
      if ($this->options["nosort"]=="")
        sort($TableData);
      // Start the HTML table rendering
      $res="</p><table";
      if ($this->options["twidth"]!="")
        $res.=" width='".$this->options["twidth"]."'>";
      else
        $res.=">";
        
      // Used to count how many tables we've sent for rendering.
      $CurTablePos=0;
      foreach($TableData as $TableStruct) {
        // Prepare the table information
        // The option to not render from Dokuwiki to HTML is available
        if ($this->options["norender"]=="")
          $td="<td class='dbtables-td_".$CurTablePos % $this->options["groups"]."'>".p_render($mode,p_get_instructions($TableStruct),$data)."</td>";
        else
          $td="<td><pre>".$TableStruct."</pre></td>";
        
        // Decide if we're going to draw a new HTML-table row.
        if (($CurTablePos % $this->options["groups"] ) == 0) {
          // If we've NOT drawn a DB-table yet, DON'T end the TR
          if ($CurTablePos!=0)
            $res.="</tr>\n";
          // Draw the table row
          $res.="\n<tr class='dbtables-tr_".$CurTablePos % $this->options["groups"]."' valign='top'>\n";
        }
        // Write out the table data
        $res.=$td."\n";
        $CurTablePos=$CurTablePos+1;
      }
      // Close off the HTML-Table
      $res.="</tr></table><p>";
      return $res;
    }
    
    function render($mode, &$renderer, $data) {
      // This will only render in xhtml
      if($mode == 'xhtml'){
         list($state, $match) = $data;
          switch ($state) {
              // This happens when we first find the <dbtables>
              case DOKU_LEXER_ENTER :
                $parmsexp=explode(';',$match);
                // Set the relevant default values
                $this->options["groups"]=4; // How many HTML tables across we want to see
                $this->options["cols"]=4;  // How many headings for each DB field do we want?
                $this->options["c1"]="Field";  // Default texts for fields
                $this->options["c2"]="Type";   
                $this->options["c3"]="Relation";
                $this->options["c4"]="Desc";
                $this->options["fdelim"]=":"; // The character used to delimit what goes between fields
                $this->options["thead"]="_";  // The character used to indicate the table name
                // $this->options["twidth"]  // Default HTML table width in HTML specifications (IE: 95% - 960px)
                // $this->options["nosort"] -> Assign a value to NOT sort the tables
                // $this->options["norender"] -> Assign a value to NOT render from Dokuwiki to HTML
                
                // Prepare each option
                foreach($parmsexp as $pexp) {
                  $p=explode("=",$pexp);
                  $this->options[$p[0]]=$p[1];
                }
                break;
              // This happens each line between <dbtables> and </dbtables>
              case DOKU_LEXER_UNMATCHED :
                // Send to the rendering function
                $renderer->doc.=$this->render_tables($match,$mode,$data);
                //$renderer->doc .= $renderer->_xmlEntities($match);
                break;
          }
          return true;
      }
      return false;
    }
}