<?php
// This PHP script generates clickable HTML documentation files for amforth
// project. Script should be run once (offline) when new versions of sorces are
// stored. It creates new subdirectory with HTML documentation tree.
//
// Configuration file is GenerateHTML.cfg
//
// (c)miho 2007 / http://www.mlab.cz

// **********************************************************************
// History
// **********************************************************************
// 0.00 work wersion


// **********************************************************************
// Definitions/parameters
// **********************************************************************


define ('INCLUDE_PATTERN', "/^ *\.include +\"(\S+)\"/i");
define ('LABEL_PREFIX',    "(VE_|XT_|PFA)");
define ('LABEL_PATTERN',   "/^ *(".LABEL_PREFIX."\S+):/i");
define ('WORD_PATTERN',    "/((?:\\n *;.*)*)\\n *VE_Q:[^\\n]*\\n? *.db +[^,]* *, *(\S[^\\n|;]+)/im");  // Q is used instead of Word
define ('ASMFILES',        "CodeAsm/");


// **********************************************************************
// Generic Funcions
// **********************************************************************


function MyReadFile($FileName, &$FileContent)
// Reads file, returns file as a single string
{
  // Read file
  $FileContent=@file($FileName);

  // Test if file contains any content
  if ($FileContent==FALSE)
  {
    unset($FileContent);
    return "No Data in File $FileName";
  }

  // Remove CR or LF or CR/LF
  foreach($FileContent as $Key => $Line)
  {
    $FileContent[$Key]=rtrim($Line);
  }

  // No error
  return "";
}


function MyReadFileString($FileName, &$FileContent)
// Reads file and returns its content as a single string
{
  // No Error
  $Error="";
  
  // Read file
  $Error=MyReadFile($FileName, &$FileContent);

  // Convert to a single string
  if ($Error=="") $FileContent=implode("\n",$FileContent);

  // Finished
  return $Error;
}


function MyWriteFile($FileName, $FileContent)
// Creates and writes file
{
    // Create Output File
    $File=@fopen($FileName,"w");
    if ($File==FALSE)
    {
      return "Unable Write File ".$FileName;
    }
    
    // Write content
    fwrite($File,$FileContent);
    fclose($File);
    
    // No Error
    return "";
}


function FileName2HTML($FileName)
// Converts FileName to FileName of HTML file
{
  // Remove path
  $FileName=basename($FileName);
  // Change suffix
  $FileName=preg_replace("/\.asm$/i",".html",$FileName);
  // Finished
  #print $FileName;
  return $FileName;
}


function Label2Link($Link, $href, $title, &$LabelList)
// Converts Label to Link
// If label not found returns original text
// IN $Link      - label to find
// IN $Word      - word to show (on mouse)
// IN $LabelList - list of all labels (Label, FileName, LineNumber)
{
  // Find label in $LabelList
  foreach($LabelList as $Value)
  {
    if ($Link===$Value["Label"])
    {
      #$LabelListItem=$Value /////////////////////////////
      print "Found ".$Value["Label"]."\n";
    }
  }

  $FileName=$Value["FileName"]."#".$Link;
  
  // Create link
  $Link="<a href=\"".$FileName."\" title=\" ".$Word."\">".$Link.'</a>';

  return $Link;
}


// **********************************************************************
// Function for processing
// **********************************************************************


function SourceAsm($SourceDir, $FileName, &$SourceAsmFiles, &$LabelList )
// Process ASM source file, recurse all includes
// Stores file names and labels into two arrays
// IN  $SourceDir      - base directory (not printed)
// IN  $FileName       - file to process
// OUT $SourceAsmFiles - list of all processed files (Filename)
// OUT $LabelList      - list of all labels (Label, FileName, LineNumber)
{

  // Start
  print "Read Asm: $FileName\n";

  // Read file
  $Error=MyReadFile($SourceDir.$FileName, $FileContent);
  if ($Error!="")
  {
    print $Error."\n";
    return 1;
  }
  
  // Remember filename
  $SourceAsmFiles[]=$FileName;

  // Filter source file line by line - find labels
  foreach($FileContent as $Key => $Value)
  {
    // Find label definitions
    if (preg_match(LABEL_PATTERN,$Value,$Matches))
    {
      print "  label @line ".($Key+1)." ".$Matches[1]."\n";
      $LabelList[]=array("Label"=>$Matches[1],"FileName"=>$FileName,"LineNumber"=>($Key+1));
    }
  }

  // Filter source file line by line - find includes
  foreach($FileContent as $Key => $Value)
  {
    // Find .include "filename" lines
    if (preg_match(INCLUDE_PATTERN,$Value,$Matches))
    {
      print "  include @line ".($Key+1)." --> ".$Matches[1]."\n";
      // Remember links
      $Includes[]=$Matches[1];
    }
  }

  // Print delimiter
  print "\n";
  
  // Recurse includes
  if ($Includes)
  {
    foreach($Includes as $Value)
    {
      $Dir=dirname($FileName)."/";
      if ($Dir=="./")
      {
        $Dir="";
      }
      SourceAsm($SourceDir, $Dir.$Value, $SourceAsmFiles, $LabelList);
    }
    unset($Includes);
  }

  // End
  return 0;
}


function PrintSourceAsm(&$SourceAsmFiles)
// Prints all procesed ASM files
{
  print "Asm Source File List\n";
  foreach($SourceAsmFiles as $Key => $Value)
  {
    print "  ".$Value."\n";
  }
  print "\n";
}


function PrintLabels(&$LabelList)
// Prints all found labels
{
  print "Label List\n";
  foreach($LabelList as $Key => $Value)
  {
    print "  ".$Value["Label"]." ".$Value["FileName"]." ".$Value["LineNumber"]."\n";
  }
  print "\n";
}


function CreateWordList($SourceDir, &$LabelList, &$WordList)
// Goes through LabelList and looks for word definitions
// IN  $LabelList - Found labels
// OUT $WordList  - Word List (ShortLabel, Word, Comment, Label, FileName)
{

  print "Word List\n";
  
  if ($LabelList)
  foreach ($LabelList as $Value)
  {
    // Take all VE definitions
    if (stristr(substr($Value["Label"],0,3),"VE_"))
    {
      // Prepare Label without VE_
      $ShortLabel=substr($Value["Label"],3);

      // Prepare search pattern
      $Pattern=str_replace("Q",$ShortLabel,WORD_PATTERN);
      #print "Pattern: ".$Pattern." ".$Value["FileName"]."\n";

      // Read source file
      $FileName=$SourceDir.$Value["FileName"];
      $Error=MyReadFileString($SourceDir.$FileName, $FileContent);
      if ($Error!="")
      {
        print "  ".$Error."\n";
        return 1;
      }
      $FileContent="\n".$FileContent;
      
      // Find label
      if (preg_match($Pattern,$FileContent,$Matches))
      {
        // Coments - remove semiculomn
        $Comment = rtrim(preg_replace("/\\n; ?(.*)/","$1\n",$Matches[1]));

        // Convert .db parameters into string
        $Word="";
        #$Word=$Matches[2]." : ";

        foreach(explode(",",$Matches[2]) as $Val)
        {
          // String element
          preg_match("/^ *\"([^\"]*)(\" *)/",$Val,$Tmp);
          #print "S:".$Tmp[1]."\n";
          if ($Tmp[1]!="")
          {
            $Word.=$Tmp[1];
          }

          // Hexa number
          preg_match("/\\$([0-9A-F]+)/i",$Val,$Tmp);
          #print "H:".$Tmp[1]."\n";
          if ($Tmp[1]!="")
          {
            $Number=hexdec($Tmp[1]);
            if ($Number!=0)
              $Word.=chr($Number);
          }
          
          // Decimal number
          preg_match("/^([0-9]+)/i",$Val,$Tmp);
          #print "D:".$Tmp[1]."\n";
          if ($Tmp[1]!="")
          {
            $Number=$Tmp[1];
            if ($Number!=0)
              $Word.=chr($Number);
          }
          
        }

        // Store label into array
        $WordList[]=array("Word"=>$Word, "ShortLabel"=>$ShortLabel, "Comment"=>$Comment,
                          "Label"=>$Value["Label"] , "FileName"=>$Value["FileName"]
                        );
        print "  ".$Word." = ".$ShortLabel."\n";
        if($Comment) print "    ".preg_replace("/\\n/","\n    ",$Comment)."\n";
      }

      // Sort Words
      array_multisort($WordList);

      // Clean Up
      unset($FileContent);
    }
  }
  print "\n";
}


function GenerateWordList($TemplateDir, $DestinationDir, &$LabelList, &$WordList)
// Creates HTML pages with Word List
// IN  $TemplateDir    - template directory (file WordList.*.html)
// IN  $LabelList      - list of labels (Label, FileName, LineNumber)
// IN  $WordList       - list of words (ShortLabel, Word, Comment, Label, FileName)
// OUT WordList.*.html - create HTML pages (file WordList.*.html)
{
  // Find all templates
  print "Word List in HTML\n";
  foreach (glob($TemplateDir."WordList.*.html") as $FileName)
  {
    // Process template file
    print "  Temlate $FileName\n";

    // Read template file
    $Error=MyReadFileString($FileName, $FileContent);
    if ($Error!="")
    {
      print "  ".$Error."\n";
      return 1;
    }

    // Find <<WordList>>
    if (!preg_match("/( *)(<<WordList>>)/i",$FileContent,$Matches))
    {
      print "  Missing <<WordList>> in template file\n";
      unset($FileContent);
      return -1;
    }
    $Indent=$Matches[1];
    
    // Create HTML code - table header
    $WordListHTML[]="<table>";
    $WordListHTML[]="  <tr>";
    $WordListHTML[]="    <th>Word</th>";
    $WordListHTML[]="    <th>Label</th>";
    $WordListHTML[]="    <th>Definition</th>";
    $WordListHTML[]="  <tr>";
    
    // Create HTML code - table lines
    foreach($WordList as $Key => $Value)
    {
      // Prepare (just for readibility)
      $Word=$Value["Word"];
      $Link="<a href=\"".ASMFILES.FileName2HTML($Value["FileName"])."#".$Value["ShortLabel"].
              "\" title=\"".$Value["Label"]."\">".$Value["ShortLabel"]."</a>";
      $Comment=$Value["Comment"];
      // Generate HTML
      $WordListHTML[]="  <tr>";
      $WordListHTML[]="    <td>".htmlspecialchars($Word)."</td>";
      $WordListHTML[]="    <td>".$Link."</td>";
      $WordListHTML[]="    <td>".htmlspecialchars($Comment)."</td>";
      $WordListHTML[]="  </tr>";
    }
    // Create HTML code - table end
    $WordListHTML[]="</table>";
    
    // Indent and Concatenate lines
    foreach($WordListHTML as $Key => $Value)
    {
      $WordListHTML[$Key]=$Indent.preg_replace("/\n/","<br>",$Value);
    }
    $WordListHTML=implode("\n",$WordListHTML);
    
    // Put it into HTML template
    $FileContent=str_ireplace("<<WordList>>", $WordListHTML, $FileContent);
    #print $FileContent;
    
    // Create Output File
    $Error=MyWriteFile($DestinationDir.basename($FileName), $FileContent);
    if ($Error!="")
    {
      print "  ".$Error."\n";
      return -1;
    }

    // Clear memory
    unset($FileContent);
    unset($WordListHTML);
  }

  // Delimiter
  print "\n";
}


function GenerateAsmFiles($TemplateDir, $SourceDir, &$SourceAsmFiles, $DestinationDir)
// Cretaes HTML files from all processed ASM files
// IN
//
{
  // Info
  print "Copy ASM Files\n";

  // Destination directory exists
  $DestinationDir.=ASMFILES;
  if (!is_dir($DestinationDir))
  {
    if (!@mkdir($DestinationDir))
    {
      print "  Unable Create Dir ".$DestinationDir."\n";
      return -1;
    }
  }
  
  // Read template
  $Error=MyReadFileString($TemplateDir."FileAsm.en.html", $Template);
  if ($Error!="")
  {
    print "  ".$Error."\n";
    return -1;
  }

  // Copy all source files
  foreach($SourceAsmFiles as $Key => $FileName)
  {
    print "  ".$FileName."\n";

    // Read ASM file
    $Error=MyReadFileString($SourceDir.$FileName, $FileContent);
    if ($Error!="")
    {
      print "  ".$Error."\n";
      return 1;
    }

    // Prepare HTML
    $FileContent=htmlspecialchars($FileContent);
    $FileContent="<pre>\n".$FileContent."\n</pre>";

    // Use Template
    $TemplateWork=$Template;
    $TemplateWork=str_ireplace("<<FileName>>",    $FileName,    $TemplateWork);
    $TemplateWork=str_ireplace("<<FileContent>>", $FileContent, $TemplateWork);

    // Write ASM file in HTML
    $Error=MyWriteFile($DestinationDir.FileName2HTML($FileName), $TemplateWork);
    if ($Error!="")
    {
      print "  ".$Error."\n";
      return -1;
    }
  }
  
  // Delimiter
  print "\n";
}


// **********************************************************************
// Main Block
// **********************************************************************


// This file contains configurations for this script
require_once("GenerateHTML.cfg");


// Global Like Variables
//$SourceAsmFiles - All processed ASM files (filenames)
//$LabelList      - All label definitions (Label, FileName, LineNumber)
//$WordList       - Word List (ShortLabel, Word, Comment, Label, FileName)


// Process all ASM files from the root level
SourceAsm($CFG_SourceDir, $CFG_SourceAsm, $SourceAsmFiles, $LabelList);
PrintSourceAsm($SourceAsmFiles);
PrintLabels($LabelList);

// Destilate Labels and Words
CreateWordList($CFG_SourceDir, $LabelList, $WordList);

// Create HTML WordList
GenerateWordList($CFG_TemplateDir, $CFG_DestinationDir, $LabelList, $WordList);

// Copy ASM files and convert them into HTML
GenerateAsmFiles($CFG_TemplateDir, $CFG_SourceDir, $SourceAsmFiles, $CFG_DestinationDir);


// Zpracování readme autora + verze

// Zpracování templejtů do samostatného podprogramu (vyřešit indent...)
//    tím se vyřeší i en/cs verze Asm souboru

// Generovat log do souboru místo printu (zvážit) oddělit chyby a varování
// Vyčistit cílový adresář
// Process all FORTH files
// Problém s rekurzí (potenciální nekonečno)
// Ctělo by to do stránek vkládat info o verzi a (c)
?>