<?php
# handle {{linkcheck>on|off}}  to enable/disable link-checking within the page.
if(!defined('DOKU_INC')) die();
require_once(DOKU_PLUGIN.'syntax.php');
require_once __DIR__.'/helperfunctions.php';

class syntax_plugin_linkcheck extends DokuWiki_Syntax_Plugin {
	protected $helper;
	public function __construct()
	{
			$this->helper = $this->loadHelper('linkcheck', false);
	}
	
	function getType(){ return 'substition'; }
  function getPType(){ return 'normal'; }
	function getSort(){ return 196; }
	function connectTo($mode) {
		$this->Lexer->addSpecialPattern('{{linkcheck>[^}]+}}',$mode,'plugin_linkcheck');
	}

	#$s is $R->doc that we are modifying.
function injectclassforlinks(&$doc,$urlmap,$otherurls){
	#We ignore the unlikely possibility that a url may appear in different linkcheck:on/off blocks. If a url appears in a linkcheck:on block, the same url will also be registered for linkcheck in other blocks.
	if(strpos($doc,'urlextern')===false) return;

	preg_match_all('#<a [^>]*href="([^"]+)" [^>]*class="[^"]*\b(urlextern)\b[^"]*"#',$doc,$ums,PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
	if(!$ums) return;

	if($this->helper->getusecache()){
			$expirytime = $this->helper->getcacheexpirytime();
			$otherurlmap=array_flip($otherurls);

			for($j=sizeof($ums)-1; $j>=0; $j--){
					$m=$ums[$j];
					$url=$m[1][0];
					$r=&$urlmap[$url];

					#up-to-date urls are written javascript:LINKCHECKDATA variable. in action.php: ontplmetaheaderoutput(). 
					#We handle them in javascript and update their class accordingly. no need to inject a class here.
					#CAVEAT: We get lastcheck/codegroup information during parse time, so we may not have the latest information here. For such cases, the link will be ajax-check in javascript. No big deal. If the page is edited/rendered again, the latest information will be available.
					if(isset($r)&&$r['lastcheck']>=$expirytime){
							#msg("Skipping [ $url ], b/c it will be included in javascript variable LINKCHECKDATA ...");
					}
					#for others, inject the linkcheck class to trigger an ajax call in javascript.
					elseif(isset($otherurlmap[$url])){
							$doc=substr($doc,0,$m[2][1])."linkcheck ".substr($doc,$m[2][1]);
					}
			}
	}
	#when cache is not being used, we inject the linkcheck class to trigger an ajax call in javascript for all links.
	else{
		#$doc=preg_replace('#(<a [^>]*href="[^"]+" [^>]*class="[^"]*)(\burlextern\b[^"]*")#','$1linkcheck $2',$doc);
		#only inject for the urls that are in lincheck:on blocks.
		for($j=sizeof($ums)-1; $j>=0; $j--){
			$m=$ums[$j];
			$url=$m[1][0];
			if(isset($otherurlmap[$url])) $doc=substr($doc,0,$m[2][1])."linkcheck ".substr($doc,$m[2][1]);
		}		
	}
}

	#--------------------------------------------------------------
	function render($mode, Doku_Renderer $R, $data) {
		static $currentstate;
		if(!isset($currentstate)) $currentstate=$this->getConf('enabledbydefault');
		list($state,$data)=$data;
		
		#these is the $urlmap injected by the action.php: onparserhandlerdone() function.
		#this also marks the end of a page, so we go back and modify the externurl links in R->doc.
		if(is_array($data)){
			$id=$data[0]; #this would not be the same as the global $ID when this is a rendering of an include'd page.
			$urlmap=$data[1];
			$otherurls=$data[2];
			if($mode=='metadata'){
				#we may have multiple wiki pages being processed (sidebar, mainpage, etc.)
				if(!isset($R->meta['linkcheck_urlmap'])) $R->meta['linkcheck_urlmap']=$urlmap;
				else $R->meta['linkcheck_urlmap']=array_merge($R->meta['linkcheck_urlmap'],$urlmap);
			}
			elseif($mode=='xhtml'){
				$this->injectclassforlinks($R->doc,$urlmap,$otherurls);
				#restore the default state
				$currentstate=$this->getConf('enabledbydefault');
			}
		}
		elseif($mode=='xhtml'){
			if($data=='on'||$data=='off'){
				$currentstate=$data=='on';
			}
			else{
				$R->info['cache'] = FALSE; #otherwise msg() will not work after the first call.
				msg("Invalid linkcheck state: [".htmlspecialchars($data)."]. Must be 'on' or 'off'.");
				$currentstate=$this->getConf('enabledbydefault');
			}
		}
		return false;
	}

	#parse task, args, and options.
	function handle($match, $state, $pos, Doku_Handler $handler)
	{
		preg_match('#{{linkcheck>([^}]+)}}#',$match,$m);
		$data=trim($m[1]);
		return [$state,$data];
	}
}