<?php
/**
 * APIDoc plugin by marcus.runsten@stilit.se
 * 
 * ToDo: Add coude highlighting
 */

if(!defined('DOKU_INC')) die("Caller error");

class syntax_plugin_apidoc_data_class {
	public $name="", $filename="", $extends="", $description="";
	public $methods=array();
	public $fields=array();
	
	public function __construct( $f ) {
		switch( min( count( $f ), 4 ) ) {
			case 4:
				$this->description = $f[3];
			case 3:
				$this->extends = $f[2];
			case 2:
				$this->filename = $f[1];
			case 1:
				$this->name = $f[0];
			default:
		}
	}
}

class syntax_plugin_apidoc_data_field {
	public $definition="", $name="", $description="";
	
	public function __construct( $f ) {
		switch( min( count( $f ), 3 ) ) {
			case 3:
				$this->description = $f[2];
			case 2:
				$this->definition = $f[1];
			case 1:
				$this->name = $f[0];
			default:
		}
	}
}

class syntax_plugin_apidoc_data_method {
	public $name="", $definition="", $description="", $parameters=array(), $returns="";
	
	public function __construct( $f ) {
		switch( min( count( $f ), 4 ) ) {
			case 4:
				$this->description = $f[3];
			case 3:
				$this->returns = $f[2];
			case 2:
				$this->definition = $f[1];
			case 1:
				$this->name = $f[0];
			default:
		}
	}
}

class syntax_plugin_apidoc_data_parameter {
	public $name="", $type="", $defaultvalue="", $description="";
	
	public function __construct( $f, &$c ) {
		switch( min( count( $f ), 4 ) ) {
			case 4:
				$this->description = $f[3];
			case 3:
				$this->defaultvalue = $f[2];
			case 2:
				$this->type = $f[1];
			case 1:
			$this->name = $f[0];
			default:
		}
	}
}

class syntax_plugin_apidoc extends DokuWiki_Syntax_Plugin {
	
	function getInfo(){
      return array(
        'author' => 'Marcus Runsten',
        'email'  => 'marcus@stilit.se',
        'date'   => '2015-11-12',
        'name'   => 'API Documentation Plugin',
        'desc'   => 'Adds syntax for API Documentation to DokuWiki. Version 1.1',
        'url'    => 'http://www.stilit.se/?page_id=107'
      );
	}  
	
	function getType(){ return 'protected';}
    function getPType(){ return 'block';}
    function getSort(){ return 100; }
	
	function connectTo($mode) {
		$this->Lexer->addEntryPattern( '<apidoc(?=>.*?</apidoc>)', $mode, 'plugin_apidoc' );
	}
	 
    function postConnect() {
      $this->Lexer->addExitPattern('</apidoc>', 'plugin_apidoc');
    }
	
	function handle($match, $state, $pos, &$handler){
        switch ($state) {
          case DOKU_LEXER_ENTER:
	        $this->syntax = substr($match, 1);
			return false;
 
          case DOKU_LEXER_UNMATCHED:
			  file_put_contents( "/tmp/handle.txt", "Hanterar " . $match );
			  if( $this->syntax == 'apidoc' ) {
				  return $this->apidoc_parse( substr( $match, 1 ) );
			  }
        }
        return false;
    }
 
    /**
     * Create output
     */
    function render($mode, &$renderer, $data) {
		if( $data ) $renderer->doc .= $data[0];
		return true;
    }
	
	private function apidoc_highlight( $str, $bool ) {
		$p = array( "/[\"\'].*?[\"\']/",
					"/\[\[(.*?);(.*?)\]\]/",
					"/\[\[(.*?)\]\]/",
					"/[\d]x[\d]+?|[\d]/",
					"/(^)([\w]+?)(\(.*?\))|(\s)([\w]+?)(\(.*?\))/",
					"/[()]/",
					"/[\\[\\]]/",
					"/[\\{\\}]/",
					'/(\$\w+?)([\s$])/',
					"/(?i)const\s|public|private|protected|new/",
					"/(?i)string|integer|boolean|bool|int|char|function/"
					 );
		
		$r = array( '<span class="apidoc_highlight_string">$0</span>',
					'<a href="$1" class="apidoc_link">$2</a>',
					'<a href="$1" class="apidoc_link">$1</a>',
					'<span class="apidoc_highlight_digit">$0</span>',
					'$1$4<span class="apidoc_highlight_method">$2$5</span>$3$6 ',
					'<span class="apidoc_highlight_bracket">$0</span>',
					'<span class="apidoc_highlight_squarebracket">$0</span>',
					'<span class="apidoc_highlight_birdbracket">$0</span>',
					'<span class="apidoc_highlight_variable">$1</span>$2',
					'<span class="apidoc_highlight_avail">$0</span>',
					'<span class="apidoc_highlight_type">$0</span>',
					 );
		return preg_replace( $p, $r, $str );
	}
	
	private function apidoc_out_html( $data ) {
		$html = "<p class='apidoc_head'><span class='apidoc_classname apidoc_code'>";
		$html .= $this->apidoc_highlight( $data->name, true );
		$html .= "</span></p><table class='apidoc'><tr><td><div class='apidoc_left'><p class='apidoc_left_head'>Fields</p><ul class='apidoc_left_content'>";
		
		foreach( $data->fields AS $f ) {
			$html .= "<li><a href='#field_" . $f->name . "'><span class='apidoc_code'>" . $this->apidoc_highlight( $f->name, true ) . "</span></a></li>";
		}
		
		$html .= "</ul><p class='apidoc_left_head'>Methods</p><ul class='apidoc_left_content'>";
		foreach( $data->methods AS $m ) {
			$html .= "<li><a href='#method_" . $m->name . "'><span class='apidoc_code'>" . $this->apidoc_highlight( $m->name, true ) . "</span></a></li>";
		}
		
		$html .= "</ul></div></td><td class='apidoc_right'>";
		
		$html .= "<p class='apidoc_right_bighead apidoc_code'>" . $this->apidoc_highlight( $data->name, true ) . "</p>";
		$html .= "<p class='apidoc_right'><span class='apidoc_bold'>Filename : </span> " . $data->filename . "<br />";
		$html .= "<span class='apidoc_bold'>Extends : </span><span class='apidoc_code'>" . $this->apidoc_highlight( $data->extends, true ) . "</span></p>";
		$html .= "<p class='apidoc_right_head'>Description</p>";
		$html .= "<p class='apidoc_right'>" . $data->description . "</p>";
		
		$html .= "<p class='apidoc_right_bighead'>Fields</p>";
		foreach( $data->fields AS $f ) {
			$html .= "<div id='field_" . $f->name . "' class='apidoc_right_field'>";
			$html .= "<p class='apidoc_right_head apidoc_code'>" . $this->apidoc_highlight( $f->name, true ) . "</p>";
			$html .= "<p class='apidoc_right'><span class='apidoc_bold'>Definition : </span><span class='apidoc_code'>" . $this->apidoc_highlight( $f->definition, true ) . "<br />";
			$html .= "<span class='apidoc_bold'>Description</span><br />" . $f->description . "</p>";
			$html .= "</div>";
		}
		
		$html .= "<p class='apidoc_right_bighead'>Methods</p>";
		foreach( $data->methods AS $m ) {
			$html .= "<div id='method_" . $m->name . "' class='apidoc_right_method'>";
			$html .= "<p class='apidoc_right_head apidoc_code'>" . $this->apidoc_highlight( $m->name, true ) . "</p>";
			$html .= "<p class='apidoc_right'><span class='apidoc_bold'>Definition : </span><span class='apidoc_code'>" . $this->apidoc_highlight( $m->definition, true ) . "</span></p>";
			$html .= "<p class='apidoc_right'><span class='apidoc_bold'>Returns : </span><span class='apidoc_code'>" . $this->apidoc_highlight( $m->returns, true ) . "</span></p>";
			$html .= "<p class='apidoc_right'><span class='apidoc_bold'>Description</span><br />" . $m->description . "</p>";
			
			$html .= "<div class='apidoc_right_parameters'>";
			$html .= "<p class='apidoc_right_head'>Parameters</p>";
			foreach( $m->parameters AS $p ) {
				$html .= "<div class='apidoc_right_param'>";
				$html .= "<p class='apidoc_right_head apidoc_code'>" . $this->apidoc_highlight( $p->name, true ) . "</p>";
				$html .= "<p class='apidoc_right'>";
				$html .= "<span class='apidoc_bold'>Type : </span><span class='apidoc_code'>" . $this->apidoc_highlight( $p->type, true ) . "</span><br />";
				$html .= "<span class='apidoc_bold'>Default value : </span><span class='apidoc_code'>" . $this->apidoc_highlight( $p->defaultvalue, true ) . "</span>";
				$html .= "</p>";
				$html .= "<p class='apidoc_right'><span class='apidoc_bold'>Description</span><br />" . $p->description . "</p>";
				$html .= "</div>";
			}
			
			$html .= "</div>";
			
			$html .= "</div>";
		}
		
		
		return $html;
	}
	
	private function apidoc_parse( $str ) {
		$data = $this->apidoc_parse_data( $str );
		if( !$data ) return false;
		
		$data = $this->apidoc_sort_data( $data );
		if( !$data ) return false;
		
		$html = $this->apidoc_out_html( $data );
		
		return array( $html );
	}
	
	private function apidoc_parse_data( $str ) {
		$rData = null;
		$co = null;
		
		$matches = preg_split( "/(CLASS|FIELD|METHOD|PARAMETER)\|/", $str, -1, PREG_SPLIT_DELIM_CAPTURE );
		for( $i=0; $i < count( $matches )-1; $i++ ) {
			switch( $matches[$i] ) {
				case 'CLASS':
					$rData = new syntax_plugin_apidoc_data_class( explode( "|", $matches[++$i] ) );
					break;
				case 'FIELD':
					if( $rData == null ) return false;
					$co = $rData->fields[] = new syntax_plugin_apidoc_data_field( explode( "|", $matches[++$i] ) );
					break;
				case 'METHOD':
					if( $rData == null ) return false;
					$co = $rData->methods[] = new syntax_plugin_apidoc_data_method( explode( "|", $matches[++$i] ) );
					break;
				case 'PARAMETER':
					if( $rData == null || $co == null ) return false;
					$co->parameters[] = new syntax_plugin_apidoc_data_parameter( explode( "|", $matches[++$i] ) );
				default:	// Do nothing
					break;
			}
		}
		return $rData;
	}
		
	private function apidoc_sort( $arr ) {
		$tgt = array();
		while( count( $arr ) > 0 ) {
			$smallest = 0;
			for( $i=0; $i < count( $arr ); $i++ ) if( strcasecmp( $arr[$smallest]->name, $arr[$i]->name ) > 0 ) $smallest = $i;
			$tgt[] = $arr[$smallest];
			unset( $arr[$smallest] );
			$arr = array_values( $arr );
		}
		return $tgt;
	}
	
	private function apidoc_sort_data( $data ) {
		$data->fields = $this->apidoc_sort( $data->fields );
		$data->methods = $this->apidoc_sort( $data->methods );
		for( $i=0; $i < count( $data->methods ); $i++ ) $data->methods[$i]->parameters = $this->apidoc_sort( $data->methods[$i]->parameters );
		return $data;
	}
}

?>
