<?php
/**
 * DokuWiki Plugin bible (Syntax Component)
 *
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 * @author  Allen Stetson <allen.stetson@gmail.com>
 */

// must be run within Dokuwiki
if (!defined('DOKU_INC')) die();

if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');

require_once DOKU_PLUGIN.'syntax.php';

class syntax_plugin_bible extends DokuWiki_Syntax_Plugin {
    # Required. Define plugin type (see https://www.dokuwiki.org/devel:syntax_plugins)
    public function getType() {
        return 'substition';
    }

	# Required. Define paragraph handling (see https://www.dokuwiki.org/devel:syntax_plugins)
    public function getPType() {
        return 'normal';
    }

	# Required. Define sort order to determine which plugin trumps which. This value was somewhat arbitrary
	## And can be changed without really affecting the plugin. (see https://www.dokuwiki.org/devel:syntax_plugins)
    public function getSort() {
        return 275;
    }

	#Plugin Part 1 of 3. This is the actual plugin. This part defines how to invoke the plugin
    public function connectTo($mode) {
		# Define all 73 books of the Catholic Bible. 
		## There are 66 books in the Protestant Bible, so this list will need altering if you plan to use that.
		$validBibleBooksArr = array("Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Josue", "Judges", "Ruth",
		 "1 Kings", "2 Kings", "3 Kings", "4 Kings", "1 Paralipomenon", "2 Paralipomenon", "1 Esdras", "2 Esdras", "Tobias",
		 "Judith", "Esther", "Job", "Psalms", "Proverbs", "Ecclesiastes", "Canticles", "Wisdom", "Ecclesiasticus",
		 "Isaias", "Jeremias", "Lamentations", "Baruch", "Ezechiel", "Daniel", "Osee", "Joel", "Amos", "Abdias", "Jonas",
		 "Micheas", "Nahum", "Habacuc", "Sophonias", "Aggeus", "Zacharias", "Malachias", "1 Machabees", "2 Machabees",
	     "Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians",
		 "Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians", "1 Timothy", "2 Timothy", "Titus",
		 "Philemon", "Hebrews", "James", "1 Peter", "2 Peter", "1 John", "2 John", "3 John", "Jude", "Apocalypse");

		# Now, for each of those books, register two regular expressions:
		## One for "through verses" like chapter 2 verses 19 through 24 (Mark 2:19-24)
		## One for just a single verse (Mark 2:19)
		## The order of registry is important! Make sure the through-verse is first or else the single verse will gobble everything.
		foreach ($validBibleBooksArr as $bibleBook){
			$this->Lexer->addSpecialPattern($bibleBook . ' \d+:\d+-\d+',$mode,'plugin_bible');
			$this->Lexer->addSpecialPattern($bibleBook . ' \d+:\d+',$mode,'plugin_bible');
		}
    }

	#Plugin Part 2 of 3. This is the handler - the workhorse - which takes the matched result from part 1 and modifies it.
    public function handle($match, $state, $pos, &$handler){
        $data = array(); # Blank array
		# Make sure that we're dealing with the match from our regexp in part 1, which is in the DOKU_LEXER_SPECIAL context.
		switch ($state){
			case DOKU_LEXER_SPECIAL :
				# Okay awesome, lets process that regexp match. Call my custom function called _fetchBibleVerse().
				$bibleLink = $this->_fetchBibleVerse($match);
				# Modified match obtained! Now return that to Dokuwiki for collection in Part 3.
				return array($bibleLink, $state, $pos);		
		}
        return $data; # Upon failure, return that blank array
    }

	#Plugin part 3 of 3. This takes that result from part 2 and actually renders it to the page.
    public function render($mode, &$renderer, $data) {
        if($mode != 'xhtml') return false; # If mode is not html, like if it is metadata, just return.
        $renderer->doc .= $data[0]; # Otherwise, fetch that $bibleLink (stored in $data[0]) and pass it to the dokuwiki renderer.
        return true;
    }
	
	# This custom function takes a matched input value (like Mark 2:19-24 or Amos 1:1) and returns HTML code
	## which turns that input into an <acronym> with the bible verse as the hover-text.
	function _fetchBibleVerse($inputVerse) {
		# Set up variables for later
		$thisChapterName = "";
		$thisChapterNum = "";
		$thisVerse = "";
		$throughVerse = "";
		$matchingVerses = array();
		$inputVerses = array();
  
  		# Have a look at that input and try to determine chapter name, number, verse number, and through-verse if it exists.
		## Groups are defined by parenthesis.  Group one is catching the Chapter Name, accounting for the fact that some chapters
		## begin with a number.
		## Group two is catching the chapter number
		## Group three is catching the verse number
		## Group 4 is required so that I can put a * after it, indicating that it may or may not be present
		## Group 5 catches the "through verse" if it should exist.
		$result = preg_match("/^.*?(\d*?\s*?\w+) (\d+):(\d+)(-(\d+))*$/", $inputVerse, $matches);
		if ($result){
		  $chapterName = trim($matches[1]);  
		  $thisChapterName = $chapterName;
		  $thisChapterNum = $matches[2];
		  $thisVerseNum = $matches[3];
		  $throughVerse = $matches[5];
		  
		  # Define which bible text file matches the result
		  ## Here is where you'll plug in a different version of the bible, should you desire.
		  ## Make sure that the syntax of the text files matches that of the Douay Rhiems bible
		  ## provided with this plugin.
		  ## PLEASE NOTE -- The bible cannot be all in one text file or the memory limit imposed upon 
		  ## plugins by Dokuwiki will be reached, and the plugin will fail. You MUST separate by chapter.
		  $filename = DOKU_PLUGIN . "bible/bible_douayRheims/" . $thisChapterName . ".txt";
		  $filename = str_replace(' ','_',$filename); # The user entered spaces, but filenames use underscores.
	  
		  if(!file_exists($filename)){
    		echo "ERROR, can't find matching bible file: " . $filename . "<br />\n";
    		return;
		  }
		  
		  # Check for a through verse (like, Mark 2:7-9), expand it if found
		  if ($throughVerse != ""){
			# Through Verse supplied 
			foreach (range($thisVerseNum, $throughVerse) as $verseNum){
				# Push each valid verse defined by the range provided onto the inputVerses array
				array_push($inputVerses, $thisChapterName . " " . $thisChapterNum . ":" . $verseNum);
			}
		  } else {
		    # No through verse, just take the input
			array_push($inputVerses, $inputVerse);
		  }

		  # Now for each of the verses supplied (multiples are from through-verses),
		  ## look for a match in the bible file, collect it for return.
		  foreach ($inputVerses as $thisInputVerse){
			foreach (file($filename) as $linenum => $line){
		  		# Find matching verse
				## Here is where the syntax of the bible text files is important. Every line must be:
				## "CHAPTER:VERSE. CONTENT"
				$result = preg_match("/^.*?(\d+:\d+)\.(.*?)$/", $line, $matches);
		    	if ($result){
			  		# looks like a verse
		      		$thisVerse = $matches[1];
			  		$content = $matches[2];
			  		$constructedVerse = $thisChapterName . " " . $thisVerse; # Form the verse into something similar to user input
			  		$constructedVerse = strtolower($constructedVerse); # Force it to lower case for comparison
			  		$lowInputVerse = strtolower($thisInputVerse); # Force user input to lower case for comparison
			  		if($lowInputVerse == $constructedVerse){  # Do they match?
						# This verse matches one of the verses that the user asked for! Push its contents into our matchingVerses array
						array_push($matchingVerses, $content);
			  		}
				}
				#Not a chapter or verse? Skip it.
		  	}
		  }
		  $matchingVersesOutput = implode(" ", $matchingVerses); # Flatten our matching verses array into a string joined by spaces.
		  $matchingVersesOutput = trim($matchingVersesOutput); # Make it look pretty by trimming off any leading or trailing whitespace
		  
		  # FINALLY, return all matching verses smooshed together as the "title" or hover-text to an <acronym>.
		  return "<acronym title=\"" . $matchingVersesOutput . "\">" . $inputVerse . "</acronym>";
		} else {
			# Warn the user that some match is not a valid bible verse (Mark 445:9833)
		  echo "Input does not appear to be a valid bible verse: " . $inputVerse . "<br />\n";
		}
	}
}

// vim:ts=4:sw=4:et:
