<?php
/**
 * Documentation of a class
 *
 * @author axel
 */
class classinfos {
    
    
    var $sClassname='';
    var $oRefClass=false;
    var $sGoTop='<a href="#">top</a><br><br>';
    var $sHtmlFooter='<div id="footer">Simple class documentation - &copy <a href="http://www.axel-hahn.de/">www.axel-hahn.de</a></div>';
    
    /**
     * init function; it sets defaults.
     * @return bool (dummy)
     */
    public function __construct($sClassname){
        if($sClassname) $this->setClassname($sClassname);
    }
    
    public function setClassname($sClassname){
        $this->oRefClass=false;
        $this->sClassname=$sClassname;
        $this->oRefClass=new ReflectionClass($this->sClassname);
    }
    
    /**
     * get comment of an object
     * @param object $o
     * @return string  (html code)
     */
    private function getComment($o){
        $sReturn=$o->getDocComment();
        if (!$sReturn) return '';
        
        // all @-Tags
        $aTags=array(
            "abstract","access", "author", "category", "copyright", "deprecated", "example", 
            "final", "filesource", "global", "ignore", "internal", "license",
            "link", "method", "name", "package", "param", "property", "return",
            "see", "since", "static", "staticvar", "subpackage", "todo", "tutorial",
            "uses", "var", "version"
            );
        /*
        $aTagsFollowedByKeyword=array(
            "access", "global", 
            "method", "param", "property", "return",
            "see", "since", "static", "staticvar", "subpackage", "todo", "tutorial",
            "uses", "var"
            );
         * 
         */
        
        $sReturn=preg_replace('@[\ \t][\ \t]*@', ' ', $sReturn); // remove multiple spaces
        
        $sReturn=preg_replace('@^\/\*\*@', '', $sReturn); // remove first comment line
        $sReturn=preg_replace('@.*\*\/@', '', $sReturn);  // remove last comment line
        $sReturn=preg_replace('@ \* @', '', $sReturn);    // remove " * " 
        $sReturn=preg_replace('@\n@', '<br>', $sReturn);
        $sReturn=preg_replace('@^\<br\>@', '', $sReturn);
        

        /*
        foreach(array("param", "return", "var") as $sKey){
            $sReturn=preg_replace('/\@'.$sKey.' ([a-zA-Z\|]*)\ (.*)\<br\>/U', '@'.$sKey.' <span style="font-weight: bold; color:#000;">$1</span> <span style="font-weight: bold;">$2</span><br>', $sReturn);            
        } 
         */       
        $sReturn=preg_replace('/(\@.*)$/s', '<span class="phpdoc">$1</span>', $sReturn);
        foreach($aTags as $sKey){
            $sReturn=preg_replace('/\@('.$sKey.')/U', '<span class="doctag">@'.$sKey.'</span>', $sReturn);
        }
        return $sReturn;
        // return $sReturn='<div class="comment">'. $sReturn . '</div>';
        
    }
    
    public function renderProperties($sMode){
        if(!$this->oRefClass) return false;
        $sHtml='';
        
        $sHtml.='<div class="properties">';
        if ($sMode=="html"){
            $sHtml.='<h2 id="properties">Properties</h2>';
        } else {
            $sHtml.='
                    <a href="#" class="propertyswitch" onclick="$(\'.propertyswitch\').toggle(); $(\'.property\').hide(); return false;">Properties verstecken</a>
                    <a href="#" class="propertyswitch" onclick="$(\'.propertyswitch\').toggle(); $(\'.property\').show(); return false;" style="display: none">Properties anzeigen</a>
                    |
                    <a href="#" class="publicpropertyswitch" onclick="$(\'.publicpropertyswitch\').toggle(); $(\'.property.private\').hide(); return false;" style="display: none">private verstecken</a>
                    <a href="#" class="publicpropertyswitch" onclick="$(\'.publicpropertyswitch\').toggle(); $(\'.property.private\').show(); return false;">private anzeigen</a>
                    <br><br>';
        }
        $oDefaulProps=$this->oRefClass->getDefaultProperties();
        
        // foreach($this->oRefClass->getProperties(ReflectionProperty::IS_PROTECTED) as $o){
        foreach($this->oRefClass->getProperties() as $o){
            $sType='';
            if($o->isPublic()) $sType.='public';
            if($o->isPrivate()) $sType.='private';
            if($o->isProtected()) $sType.='protected';
            if($o->isStatic()) $sType.='static';
            
            $sHtml.='<div class="property '.$sType.'">';
            $sHtml.='<span class="propertytype">'.$sType.'</span><span class="propertyname"> $'.$o->getName().'</span>';
            $sHtml.='<span class="propertyvalue"> = ';
            if ($oDefaulProps[$o->getName()]) $sHtml.=$oDefaulProps[$o->getName()];
            else $sHtml.='false';
            $sHtml.='</span>';
            
            $sHtml.=$this->getComment($o);
            $sHtml.='</div>';
        }
        $sHtml.='</div>';
        return $sHtml;
    }
    public function renderMethods($sMode){
        if(!$this->oRefClass) return false;
        $sHtml='';
        $sHtml.='<div class="methods">';
        if ($sMode=="html"){
            $sHtml.='<h2 id="methods">Methods</h2>';
        } else {
            $sHtml.='
                    <a href="#" class="methodswitch" onclick="$(\'.methodswitch\').toggle(); $(\'.method\').hide(); return false;">Methoden verstecken</a>
                    <a href="#" class="methodswitch" onclick="$(\'.methodswitch\').toggle(); $(\'.method\').show(); return false;" style="display: none">Methoden anzeigen</a>
                    |
                    <a href="#" class="publicmethodswitch" onclick="$(\'.publicmethodswitch\').toggle(); $(\'.method.private\').hide(); return false;" style="display: none">private verstecken</a>
                    <a href="#" class="publicmethodswitch" onclick="$(\'.publicmethodswitch\').toggle(); $(\'.method.private\').show(); return false;">private anzeigen</a>
                    <br><br>';
        }
        $sHtml.='<table>';
        foreach($this->oRefClass->getMethods() as $o){
            $sMethodname=$o->name;
            $sType='';
            if($o->isPublic()) $sType.='public';
            if($o->isPrivate()) $sType.='private';
            if($o->isProtected()) $sType.='protected';
            if($o->isStatic()) $sType.='static';
            if($o->isAbstract()) $sType.='abstract';
            if($o->isFinal()) $sType.='final';

            /*
            $sHtml.='<div class="method '.$sType.'">';
                $sHtml.=$sType . ' <span class="methodname">' .$sMethodname . "</span>(";
                $oMethod = $this->oRefClass->getMethod($sMethodname);

                
                $sHtml.='<span class="parameters">' ;
                $iCount=0;
                $iRequired=$oMethod->getNumberOfRequiredParameters();
                foreach($oMethod->getParameters() as $oParam){
                    $iCount++;
                    $sHtml.='<span class="';
                    $sHtml.=$iCount<=$iRequired ? "required":"optional";
                    $sHtml.='">'. preg_replace('@Parameter\ \#.*\[\ (.*)\ \]@', '$1', $oParam->__toString()).'</span>,';
                }
                $sHtml=preg_replace('@,$@', "", $sHtml);
                $sHtml.="</span>)";
                if ($iRequired){
                    if ($iCount==$iRequired) $sHtml.=' ... all param(s) required';
                    else $sHtml.=' ... '.$iRequired . ' of '.$iCount.' param(s) required';

                }
                
                $sHtml.=$this->getComment($oMethod);
            $sHtml.='</div>';
             */
            $sHtml.='<tr class="method '.$sType.'">';
                $sHtml.='<td><span class="methodname">' .$sMethodname . '</span></td>'
                        . '<td>'.$sType . ' </td>'
                        . '<td><span class="methodname">' .$sMethodname . '</span>(';
                $oMethod = $this->oRefClass->getMethod($sMethodname);

                
                $sHtml.='<span class="parameters">' ;
                $iCount=0;
                $iRequired=$oMethod->getNumberOfRequiredParameters();
                foreach($oMethod->getParameters() as $oParam){
                    $iCount++;
                    $sHtml.='<span class="';
                    $sHtml.=$iCount<=$iRequired ? "required":"optional";
                    $sHtml.='">'. preg_replace('@Parameter\ \#.*\[\ (.*)\ \]@', '$1', $oParam->__toString()).'</span>,';
                }
                $sHtml=preg_replace('@,$@', "", $sHtml);
                $sHtml.="</span>)";
                if ($iRequired){
                    if ($iCount==$iRequired) $sHtml.=' ... all param(s) required';
                    else $sHtml.=' ... '.$iRequired . ' of '.$iCount.' param(s) required';

                }
                // $sHtml.='</td><td>';
                $sHtml.=$this->getComment($oMethod);
            $sHtml.='<br></td></tr>';
        }
        $sHtml.='</table>';
        $sHtml.='</div>';
        return $sHtml;
    }
    
    function renderSource($sMode){
        
        $source_code=file_get_contents($this->oRefClass->getFileName());
        
        $sHtml='<div class="sourcecode">';
        if ($sMode=="html"){
            $sHtml.='<h2 id="source">Source</h2>';
        }
        else{
            $sHtml.='
                    <a href="#" class="sourceswitch" onclick="$(\'.sourceswitch\').toggle(); $(\'.source\').hide(); return false;">Sourcecode verstecken</a>
                    <a href="#" class="sourceswitch" onclick="$(\'.sourceswitch\').toggle(); $(\'.source\').show(); return false;" style="display: none">Sourcecode anzeigen</a><br> 
                    <br>';
        }
        
        $sHtml.='<pre class="source">' . highlight_string($source_code, true) .'</pre></div>';
        
        return $sHtml;
        
        /*
        $source_code = explode("\n", str_replace(array("\r\n", "\r"), "\n", $source_code));
        $line_count = 1;

        foreach ($source_code as $code_line)
        {
            $formatted_code .= '<tr><td>'.$line_count.'</td>';
            $line_count++;
          
            if (ereg('<\?(php)?[^[:graph:]]', $code_line))
                $formatted_code .= '<td>'. str_replace(array('<code>', '</code>'), '', highlight_string($code_line, true)).'</td></tr>';
            else
                $formatted_code .= '<td>'.ereg_replace('(&lt;\?php&nbsp;)+', '', str_replace(array('<code>', '</code>'), '', highlight_string('<?php '.$code_line, true))).'</td></tr>';
        }

        return '<table style="font: 1em Consolas, \'andale mono\', \'monotype.com\', \'lucida console\', monospace;">'.$formatted_code.'</table>';
        */
    }

    public function render($sMode=false){
        $sHtml='';
        
        if(!$this->oRefClass) return false;
        if ($sMode=="html") $sHtml.='
        <html>
            <head>
                <title>PHP class '.$this->sClassname.'</title>
            </head>
            <style>
                body{font-family: Arial,Helvetica,Verdana,sans-serif; background: #fafafa; color: #444;}
                a{color:#348; text-decoration: none;}
                a:hover{text-decoration: underline;}
                h1{color:#88c; border-bottom: 3px solid #ddf;}
                h2{color:#44a; border-bottom: 3px solid #ddf;}
                #footer{border-top: 3px solid #eee; background:#f4f4f4; margin-top:20px; padding: 5px; text-align: center;}
            </style>
            <body>
            <h1>Simple class documentation - '.$this->sClassname.'</h1>
            
            local links:
                <a href="#methods">Methods</a> ::
                <a href="#properties">Properties</a> ::
                <a href="#source">Source</a><br>
                <br>
            
            ';
            
        
        $sHtml.='
        <style>
            .protected, .private, .public, .static, .abstract, .final{padding-left: 34px; }
            .classinfo .protected {background: url(/axel/images/nuvola/16x16/actions/kgpg.png) no-repeat 10px 10px; }
            .classinfo .private {background: rgba(255,220,220,0.3) url(/axel/images/nuvola/16x16/actions/kgpg_key1.png) no-repeat 10px 10px; display:none; }
            .classinfo .public {background: rgba(220,255,220,0.3) url(/axel/images/nuvola/16x16/actions/ledlightgreen.png) no-repeat 10px 10px; }
            .classinfo .static {background: url(/axel/images/nuvola/16x16/actions/ledyellow.png) no-repeat 10px 10px;}
            .classinfo .abstract {background: url(/axel/images/nuvola/16x16/actions/ledpurple.png) no-repeat 10px 10px;}
            .classinfo .final {background: url(/axel/images/nuvola/16x16/actions/ledred.png) no-repeat 10px 10px;}

            .classinfo{border: 0px solid #aac; padding: 5px;}
            .classinfo .classname{font-weight: bold; font-size: 130%;}
            .classinfo .methods, .classinfo .properties, .classinfo .sourcecode{margin-left: 20px; border: 0px solid #ccc; margin-top: 20px; }
            .classinfo .property, .classinfo .method, .classinfo .source{margin-left: 30px; margin-top: 10px; border-top: 0px dotted #ccc; padding-top: 10px;}
            .classinfo .methodname{font-weight: bold;}
            .classinfo .propertyname{font-weight: bold;}
            .classinfo .propertyvalue{font-style: italic; color:#aaa;}
            .classinfo .parameters{font-style: italic; color:#aaa;}
            .classinfo .parameters .required {background:rgba(255,210,210,1);color:#666;}
            .classinfo .comment{margin: 10px 0 0 0px; padding: 10px; background-color:rgba(255,255,255,0.4);}
            .classinfo .comment .phpdoc{font-style: italic; color:#8aa; font-weight: normal;}
            .classinfo .comment .phpdoc .doctag{color:#a8a; font-weight: normal;}

            .classinfo .sourcecode .source{border: 1px solid #eee; padding: 10px;}

        </style>
        ';
        $sHtml.='<div class="classinfo">';
        $sHtml.='<div class="classname">Class: '.$this->sClassname.'</div>';

    
        /*
        foreach (array("public","private","protected", "static", "abstract", "final") as $sType){
            print '<input type="checkbox" onclick="$(\'.'.$sType.'\').toggle();"';
            if ($sType=="public") print " checked ";
            print '> '. $sType . ' ... ';
        }
         */
        if (!$sMode) $sHtml.='
            <style>
            .classinfo .comment{display:none;}
            </style>
            <!--
                    Ansicht:
                    <ul class="naviH" >
                        <li class="active"><a href="#" class="viewswitch"     onclick="$(\'.naviH >li\').removeClass(\'active\'); $(this.parentNode).addClass(\'active\'); $(\'.comment\').hide(); return false;">einfach</a>
                        <li><a href="#" class="viewswitch"            onclick="$(\'.naviH >li\').removeClass(\'active\'); $(this.parentNode).addClass(\'active\'); $(\'.comment\').show(); return false;">erweitert</a>
                    </ul>
                    -->
                </span>
                <br>';
    
        $sHtml.=$this->renderMethods($sMode);
        if ($sMode=="html") $sHtml.=$this->sGoTop;
        $sHtml.=$this->renderProperties($sMode);
        if ($sMode=="html") $sHtml.=$this->sGoTop;
        
        if ($sMode=="html") $sHtml.=$this->renderSource($sMode);
        if ($sMode=="html") $sHtml.=$this->sGoTop;
        
        if ($sMode=="html") $sHtml.=$this->sHtmlFooter;
        
        $sHtml.='</div>';
        // print "DocComment:<br>".$this->$oRefClass->getDocComment();
        // $this->renderMethods();
        
        if ($sMode=="html") $sHtml.='
            </body>
        </html>';
        
        // echo $sHtml;
        return $sHtml;
    }
    
    
}