<?php
/**
 * Plugin calc : petite calculatrice.
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Etienne Mauvais <emauvaisfr@yahoo.fr>
 */

// based on http://wiki.splitbrain.org/plugin:tutorial

// 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');

/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_calc extends DokuWiki_Syntax_Plugin {
    function getInfo() {
        return array(
        'author'  => 'Etienne Mauvais',
        'email'   => 'emauvaisfr@yahoo.fr',
        'date'    => @file_get_contents(DOKU_PLUGIN.'calc/VERSION'),
        'name'    => 'Calc Plugin',
        'desc'    => $this->getLang('calc_description'),
        'url'     => 'http://www.dokuwiki.org/plugin:calc'
        );
    }

    function connectTo($mode) {
        $this->Lexer->addSpecialPattern('calc:.*?=', $mode, 'plugin_calc');
    }

    //function getType() { return 'substition'; }
    function getType() { return 'disabled'; }

    function getSort() { return 667; }

    function handle($match, $state, $pos, &$handler) {
        return array($match, $state, $pos);
    }

    function render($mode, &$renderer, $data) {
        if ($mode == 'xhtml') {
	    //On recupere l'expression
	    $calc=split("calc:",$data[0]);
	    if (isset($calc) && isset($calc[1])) $calc=$calc[1];

	    //On enleve le "=" final
	    if ($calc[strlen($calc)-1]=='=') $calc[strlen($calc)-1]=" ";
	    $calc=rtrim($calc);

	    //Si on a un ">" final on l'enleve et on affichera l'expression avant le resultat
	    if ($calc[strlen($calc)-1]=='>') {
	      $calc[strlen($calc)-1]=" ";
	      $affiche=true;
	    }
	    $calc=rtrim($calc);
	    
	    //On retire les fonctions interdites
            $encore=1;
            while($encore) {
  	      $encore=0;
	      $calc=preg_replace_callback("/([a-z].+?\(.*?\))/",
	                                  create_function('$f',
					                  'GLOBAL $encore;
							   $autorisees=Array("abs", "acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh",
							                     "base_convert", "bindec",
							                     "ceil", "cos", "cosh",
							                     "decbin", "dechex", "decoct", "deg2rad",
							                     "exp", "expm1",
							                     "floor", "fmod",
							                     "getrandmax",
							                     "hexdec", "hypot",
							                     "is_finite", "is_infinite", "is_nan",
							                     "lcg_value", "log", "log10", "log1p",
							                     "max", "min", "mt_getrandmax", "mt_rand", "mt_srand",
							                     "octdec",
							                     "pi", "pow",
							                     "rad2deg", "rand", "round",
							                     "sin", "sinh", "sqrt", "srand",
							                     "tan", "tanh");

							   //print "  Trouve : ".$f[1];

							   $nomF=explode("(",$f[1]);
							   $nomF=$nomF[0];

							   if(in_array($nomF, $autorisees)) {
							     //print " -> autorisee\n";
							     return $f[1];
							   }
							   else {
							     //print " -> interdite !!!\n";
							     $encore=1;
							     return "";
							   }'
					                  ),
					  $calc);
	    }
	    //On retire les ";" et les "$"
	    $calc=str_replace(";","",$calc);
            $calc=str_replace("\$","",$calc);

            //On affiche un commentaire dans le source de la page
            $renderer->doc .= "\n<!-- Calc : $calc -->\n";
	    $calc_propre=$calc;
	    $calc="\$n=$calc;";

	    //On passe en mode track_errors (apres avoir recupere l'ancien mode)
	    $track=ini_get('track_errors');
	    ini_set('track_errors', 'true');
	    $php_errormsg="";
	    try {
	      eval($calc);
	    }
	    catch (Exception $e) {
	      $renderer->doc .= "\"<b>".$this->getLang('calc_erreur')."</b> ".$e->getMessage()."\"";
	      return true;
	    }
	    //On repasse dans l'ancien mode track_error
	    ini_set('track_error', $track);

            //S'il y a eu une erreur (non catchee par try)
            if ($php_errormsg) {
	      $renderer->doc .= "\"<b>".$this->getLang('calc_erreur')."</b> ".$php_errormsg."\""; 
	      return true;
	    }

            //Si la valeur retournee n'est pas un nombre (ex : sqrt(-1)
            if (is_nan($n)) {
	      $renderer->doc .= "\"".$this->getLang('calc_valeurincorrecte')."\"";
	      return true;
	    }

            //Mise en forme du resultat
	    //Si on a obtenu un nombre (et pas une chaine, suite a base_convert, par exemple)
	    if ($n*1===$n) {
              $tmp=explode('.',$n);
	      $out=number_format($tmp[0], 0, $this->getLang('calc_sepdec'), $this->getLang('calc_sepmil'));
	      if (isset($tmp[1])) $out.= $this->getLang('calc_sepdec').$tmp[1];
	    }
	    else $out=$n;
	    
	    $out=preg_replace("/ /","&nbsp;", $out);

            //Si on est en mode affichage, on affiche d'abord l'expression avant le resultat
            if ($affiche) $renderer->doc .= $calc_propre." = ";
	    $renderer->doc .= $out;

            return true;
        }
        return false;
    }
}
?>
