<?php
# Dynamap WikiMedia extension
#
# introduces
#  * <dynamap></dynamap> Tags
#  * Special:DynamapConstants
#
# Note: The output is not interpreted as WikiText but directly 
#       included in the HTML output. So Wiki markup is not supported.
# To activate the extension, include it from your LocalSettings.php
# with: include("extensions/ExampleExt.php"); 

if (!defined('MEDIAWIKI')) die();
$wgExtensionFunctions[] = "wfDynamapExtension";
 
function wfDynamapExtension() {
	global $wgParser,$IP, $wgMessageCache;
        require_once( "$IP/includes/SpecialPage.php" );


	# register the extension with the WikiText parser
	# the first parameter is the name of the new tag. 
	# In this case it defines the tag <example> ... </example>
	# the second parameter is the callback function for 
	# processing the text between the tags
	$wgParser->setHook( "dynamap", "renderDynamap" );


	# register the dynamap constant list special page      
	$wgMessageCache->addMessages(array('dynamapconstants' => 'Dynamap Constants'));
        class DynamapSpecialPage extends SpecialPage {
		function DynamapSpecialPage() {
			SpecialPage::SpecialPage( 'DynamapConstants' );
				#$this->includable( true );
			}

			function execute( $par = null ) {
				global $wgOut, $wgRequest;
				require_once( "maps.inc" );
				$out = "<h1>Dynamap Extension Constants</h1><h2>List of available Map Sources</h2><table border='1'>";
				foreach ($maplist as $key => $value ) {
					$out .= "<tr><td>$key</td><td>$value[1]</td></tr>\n";
				}
				$out .= "</table><h2>List of predefined Regions</h2>";
				//$wgOut->addHTML( $out );
			}
        }
	SpecialPage::addPage( new DynamapSpecialPage );
}

function dynamapError($text)
{
	return '<pre>'.$text."</pre>";
}

function dynamapFindTemplate($haystack,$needle,&$offset)
{
	$length=strlen($needle);
	$pos1=strpos(trim($haystack),$needle,$offset);
	if(!($pos1===false))
	{
		$pos2=strpos($haystack,'}}',$pos1);
		if(!($pos2===false))
		{
			$offset=$pos2;
			return trim(substr($haystack,$pos1+$length,$pos2-$pos1-$length));
		}
	}
	return '';   
}

function dynamapProject($x,$y,&$px,&$py,$w,$h,$minlat,$minlong,$maxlat,$maxlong)
{
	$px=(($x-$minlong)/($maxlong-$minlong)*$w);
	$py=$h-(($y-$minlat)/($maxlat-$minlat)*$h);
}

function dynamapGetRawText($title,$cur) 
{	 
	$mtitle=Title::newFromURL($title);	
	
	$dbr =& wfGetDB( DB_SLAVE );
	$t = $dbr->strencode( $mtitle->getDBKey() );
	$ns = $mtitle->getNamespace();

	if($ns == NS_MEDIAWIKI) return wfMsg($t);
	else {
		$sql = "SELECT cur_id as id,cur_timestamp as timestamp,cur_user as user,cur_user_text as user_text," .
		       "cur_restrictions as restrictions,cur_comment as comment,cur_text as text FROM $cur " .
		       "WHERE cur_namespace=$ns AND cur_title='$t'";
        $res = $dbr->query( $sql, $fname );

        if( $s = $dbr->fetchObject( $res ) ) return Article::getRevisionText( $s, "" );
		else return '';
	}
}
  

function renderDynamap( $input, $argv="" ) 
{
	# $argv is an array containing any arguments passed to the extension like <example argument="foo" bar>..

	$w=0; $h=0;
	$maparea="";
	$data=false;
	$css="text {font-family:Verdana; font-size:12px; fill:black;}\n"; 
	$alt_text="Map showing: ";
	$paths="";
	$outfilenamebase="images/dynamap/".md5($input);

	$dbr =& wfGetDB( DB_SLAVE );
	extract( $dbr->tableNames( 'cur' ) );

	$level=1; $color="black"; $radius=3; $stroke=1; $textcolor="black";

	$lines = explode("\n", $input);
	foreach($lines as $line)
	{
		if($line[0]!='#')
		{
			$command = explode(":", $line);
			$output.=$command[0];
			if(!$data) switch($command[0])
			{
				case "image": $values=explode(",",$command[1]);
					      $w=intval($values[0]);
					      $h=intval($values[1]);
					      break;
				case "area" : $maparea=$command[1];
					      list($minlat,$minlong,$maxlat,$maxlong)=explode(",",$command[1]);
					      if($minlat>$maxlat) list($maxlat,$minlat) = array($minlat,$maxlat);
					      if($minlong>$maxlong) list($maxlong,$minlong) = array($minlong,$maxlong);
					      $ah=abs($maxlat-$minlat);
					      $aw=abs($maxlong-$minlong);
					      //return "minlat=$minlat,minlong=$minlong,maxlat=$maxlat,maxlong=$maxlong, ah=$ah,aw=$aw, maparea=$maparea";
					      break;
				case "start": if($maparea=="" || $ah==0) return dynamapError("No propper area:latmin,longmin,latmax,longmax command found!");
					      if($w!=0 && $h==0)
					      {
					      		$h=intval(($ah/$aw)*floatval($w));
					      }
					      if($w==0 || $h==0) return dynamapError("No propper image:w,h or image:w command found!");
					      $data=true;
					      //return "$w x $h";
					      break;
			}
			else switch($command[0])
			{
				case "level": $level=intval($command[1]);
					      break;
				case "color": $color=$command[1];
					      break;
				case "radius":$radius=floatval($command[1]);
					      break;
				case "stroke":$stroke=floatval($command[1]);
					      break;

				case "layer": $dirtylayer=trim($command[1]);
					      $layer=str_replace(array('"','/','.','%','\\','?','*','!','(',')','{','}'),'',$dirtylayer);

					      # check if raw map exists, otherwise create
					      $map_raw_cache="images/dynamap/".md5($maparea.'_'.$w.'x'.$h.$layer.$level)."raw.svg";
					      if ( is_readable($map_raw_cache) ) 
					      {
							$paths.=file_get_contents($map_raw_cache);
					      }
					      else
					      {
							$command="extensions/dynamap/drawmap ".escapeshellcmd($maparea)." $w,$h $level $layer";
							//exec("$command 2>&1", $out1, $err);
							exec("$command", $out1, $err);
							$out2=implode("\n",$out1);

							//file_put_contents($map_raw_cache,$out2);
							$res=fopen($map_raw_cache,'w');
							fwrite($res,$out2);
							fclose($res);

							$paths.=$out2;
					      }
					      $css.="      path.$layer {fill: gray; stroke: $color; stroke-width:$stroke }\n";
					      break;
 
				case "poly":  $text=dynamapGetRawText(trim($command[1]),$cur);
					      $alt_text.=$text.', ';
					      $mtitle=Title::newFromURL(trim($command[1]));					      
					      $link=$mtitle->getDBKey();
					      $pos=0;
					      $coords=explode('|',dynamapFindTemplate($text,'{{geopoly',$pos));
					      $mode=trim(array_shift($coords));
					      $px=0; $py=0; $first=true;
					      $paths.="<a xlink:href=\"/index.php/$link\">";
					      while(count($coords)>=4)
					      {
					      		$y=trim(array_shift($coords));
							if(strtoupper(trim(array_shift($coords)))=='S') $y*=-1;
					      		$x=trim(array_shift($coords));
							if(strtoupper(trim(array_shift($coords)))=='W') $x*=-1;

							dynamapProject($x,$y,$px,$py,$w,$h,$minlat,$minlong,$maxlat,$maxlong);
							if($first)
							{
								$paths.="<path d=\"M $px $py ";
								$first=false;
							}
							else $paths.="L $px $py ";
					      }
					      if(!$first) $paths.="\" style=\"stroke:$color; fill:none; stroke-width:$stroke\" />\n";
					      $paths.="</a>\n";
					      break;
				case "point": $text=dynamapGetRawText(trim($command[1]),$cur);
					      $alt_text.=$text.' ';
					      $mtitle=Title::newFromURL(trim($command[1]));					      
					      $link=$mtitle->getDBKey();
					      $pos=0;
					      $coords=explode('|',dynamapFindTemplate($text,'{{coor',$pos));
					      $mode=trim(array_shift($coords));
					      $px=0; $py=0; $first=true;
					      while(count($coords)>=4)
					      {
					      		$y=trim(array_shift($coords));
							if(strtoupper(trim(array_shift($coords)))=='S') $y*=-1;
					      		$x=trim(array_shift($coords));
							if(strtoupper(trim(array_shift($coords)))=='W') $x*=-1;

							dynamapProject($x,$y,$px,$py,$w,$h,$minlat,$minlong,$maxlat,$maxlong);
							$paths.="<a xlink:href=\"/index.php/$link\">";
							$paths.="<circle cx=\"$px\" cy=\"$py\" r=\"$radius\" style=\"fill:$color; stroke:none\" />\n";
							$px+=$radius; $py-=$radius;
							$paths.="<text x=\"$px\" y=\"$py\" style=\"fill:$textcolor; stroke:none\" >".trim($command[1])."</text></a>\n";
					      }
					      break;
			}
		}
	}

	$res=fopen($outfilenamebase.'.svg','w');
	
	$buffer = <<<EOH1
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="$w" height="$h">
<defs>
    <style type="text/css">
    <![CDATA[
      path.level0 {fill: #9EC7F3; stroke: #9EC7F3; stroke-width:3 }
      path.level1 {fill: #FFFFD0; stroke: #4090D0; stroke-width:1 }
      path.level2 {fill: #9EC7F3; stroke: #4090D0; stroke-width:1 }
      path.level3 {fill: #FFFFD0; stroke: #4090D0; stroke-width:1 }
      path.level4 {fill: #9EC7F3; stroke: #4090D0; stroke-width:1 }
$css
    ]]>
    </style>
  </defs>
  <path class="level0" d="M 0 0 L $w 0 L $w $h L 0 $h L 0 0" />
EOH1;

	fwrite($res,$buffer);
	fwrite($res,$paths);
	fwrite($res,"</svg>\n");
	fclose($res);

	exec("rsvg $outfilenamebase.svg $outfilenamebase.png");

	# return '<img src="/'.$outfilenamebase.'.png"><br><a href="/wiki/'.$outfilenamebase.'.svg">SVG version with clickable links</a>';
	return '<object data="/'.$outfilenamebase.'.svg" width="'.$w.'" height="'.$h.'" type="image/svg+xml"><img src="/'.$outfilenamebase.'.png" width="'.$w.'" height="'.$h.'" border="0"><br><a href="/'.$outfilenamebase.'.svg">SVG version with clickable links</a></object>';
}

?>