<?

	include_once( PLOG_CLASS_PATH."class/object/object.class.php" );
	
	class PloGeshi extends Object
	{
	
		function colorText ( $text )
		{
			return $this->parse( $text );
		}
	
		/**
		* The main parser function. Accepts raw data and returns
		* valid xhtml
		*/  
		function parse($text){
			global $parser;
			global $conf;
			$table   = array();
			$hltable = array();
			
			//preparse
			$text = $this->preparse($text,$table,$hltable);
			
			//padding with a newline
			$text  = "\n".$text."\n";
			
			#for link matching
			$urls = '(https?|telnet|gopher|file|wais|ftp|ed2k|irc)';
			$ltrs = '\w';
			$gunk = '/\#~:.?+=&%@!\-';
			$punc = '.:?\-;,';
			$host = $ltrs.$punc;
			$any  = $ltrs.$gunk.$punc;
			
			/* first pass */
			
			//preformated texts
			$this->firstpass($table,$text,"#<code( (\w+))?>(.*?)</code>#se","\$this->preformat('\\3','code','\\2')");
			
			
			/* second pass for simple formating */
			$text = $this->simpleformat($text);
			
			/* third pass - insert the matches from 1st pass */
			reset($table);
			while (list($key, $val) = each($table)) {
				$text = str_replace($key,$val,$text);
			}
	
			/* remove empty paragraphs */
			$text = preg_replace('"<p>\n*</p>"','',$text);
			
			/* remove padding */
			$text = trim($text);
			return $text;
		}
		
		
		/**
		* This preparses the text by walking it line by line. This
		* is the only place where linenumbers are still available (needed
		* for section edit. Some precautions have to be taken to not change
		* any noparse block.
		*/
		function preparse($text,&$table,&$hltable){
			$lines = split("\n",$text);
			
			// initialize variabled
			$noparse = '';
			$po = '';
			$pc = '';
			
			//prepare a tokens for paragraphs
			// comento este texto porque me agrega un parrafo en la noticia <p> {noticia} </p>
			/*
			$po = $this->mkToken();
			$table[$po] = "<p>";
			$pc = $this->mkToken();
			$table[$pc] = "</p>";
			*/
			
			for ($l=0; $l<count($lines); $l++){
				//temporay line holder
				$line = $lines[$l];
				
				//look for end of multiline noparse areas
				if(strlen($noparse) > 0){
					if(preg_match("#^.*?$noparse#",$line)){
						$noparse = '';
						$line = preg_replace("#^.*?$noparse#",$line,1);
					}else{
						continue;
					}
				}
				
				if(strlen($noparse) == 0){
					//skip indented lines
					if(preg_match('#^(  |\t)#',$line)) continue;
					//remove norparse areas which open and close on the same line
					$line = preg_replace("#<code( (\w+))?>(.*?)</code>#","",$line);
					//check for start of multiline noparse areas
					if(preg_match('#^.*?<(code)( (\w+))?>#',$line,$matches)){
						list($noparse) = split(" ",$matches[1]); //remove options
						$noparse = '</'.$noparse.'>';
						continue;
					}elseif(preg_match('#^.*?%%#',$line)){
						$noparse = '%%';
						continue;
					}
				}
				
				//handle headlines
				if(preg_match('/^(\s)*(==+)(.+?)(==+)(\s*)$/',$lines[$l],$matches)){
					//get token
					$tk = $this->tokenize_headline($hltable,$matches[2],$matches[3],$l);
					//replace line with token
					$lines[$l] = $tk;
				}
				
				//handle paragraphs
				if(empty($lines[$l])){
					$lines[$l] = "$pc\n$po";
				}
			}
			
			//reassemble full text
			$text = join("\n",$lines);
			//open first and close last paragraph
			$text = "$po\n$text\n$pc";
			
			return $text;
		}
		
		function firstpass(&$table,&$text,$regexp,$replace,$lpad='',$rpad=''){
			//extended regexps have to be disabled for inserting the token
			//and later reenabled when handling the actual code:
			$ext='';
			if(substr($regexp,-1) == 'e'){
				$ext='e';
				$regexp = substr($regexp,0,-1);
			}
			
			while(preg_match($regexp,$text,$matches)){
				$token = $this->mkToken();
				$match = $matches[0];
				$text  = preg_replace($regexp,$lpad.$token.$rpad,$text,1);
				$table[$token] = preg_replace($regexp.$ext,$replace,$match);
			}
		}
		
		/**
		* Simple text formating and typography is done here
		*/
		function simpleformat($text){
			$text = preg_replace('/\'\'(.+?)\'\'/s','<code>\1</code>',$text);  //code
			return $text;
		}
	
		/**
		* This function adds some information about the given headline
		* to a lookuptable to be processed later. Returns a unique token
		* that idetifies the headline later
		*/
		function tokenize_headline(&$hltable,$pre,$hline,$lno){
			switch (strlen($pre)){
				case 2:
					$lvl = 5;
					break;
				case 3:
					$lvl = 4;
					break;
				case 4:
					$lvl = 3;
					break;
				case 5:
					$lvl = 2;
					break;
				default:
					$lvl = 1;
					break;
			}
			$token = $this->mkToken();
			$hltable[] = array( 'name'  => htmlspecialchars(trim($hline)),
					'level' => $lvl,
					'line'  => $lno,
					'token' => $token );
			return $token;
		}
		
		function mkToken(){
			return '~'.md5(uniqid(rand(), true)).'~';
		}
		
		function preformat($text,$type,$option=''){
			global $conf;
			//unescape
			$text = str_replace('\\"','"',$text);
			
			if($type == 'php' && !$conf['phpok']) $type='file';
			if($type == 'html' && !$conf['htmlok']) $type='file';
			
			switch ($type){
				case 'code':
					if(empty($option)){
						//$text = htmlspecialchars($text);
						$text = $this->unhtmlspecialchars($text);
						$text = '<pre class="code">'.$text.'</pre>';
					}else{
						//strip leading blank line
						$text = $this->unhtmlspecialchars($text);
						$text = preg_replace('/^\s*?\n/','',$text);
						//use geshi for highlighting
						require_once( PLOGESHI_GESHI_PATH . "geshi.php");
						$geshi = new GeSHi($text, strtolower($option), PLOGESHI_GESHI_PATH . "geshi");
						$geshi->enable_classes();
						$geshi->set_header_type(GESHI_HEADER_PRE);
									$geshi->set_overall_class('code');
						$geshi->set_link_target($conf['target']['extern']);
						$text = $geshi->parse_code();
						
					}
					$text = "</p>\n".$text."\n<p>";
					break;
			}
			return $text;
		}
	
		function unhtmlspecialchars( $string )
		{
			$string = str_replace ( '&amp;', '&', $string );
			$string = str_replace ( '&#039;', '\'', $string );
			$string = str_replace ( '&quot;', '\"', $string );
			$string = str_replace ( '&lt;', '<', $string );
			$string = str_replace ( '&gt;', '>', $string );
			
			return $string;
		}	
	
	
	}
?>