<?php if (!defined('PmWiki')) exit();
/*******************************************************************************
 * Script: GoogleMaps.php
 * Version: Alpha 1.0.2
 * Conception: November 20, 2006
 * Contact: craige@internetadvisor.ca
 *
 * Copyright (C) 2006  Craige Leeder
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ******************************************************************************/

/*
 * Set Defaults
 */
$GMIversion = 'alpha 1.0.2';

# Config Defaults
SDV( $GoogleMapKey, '' );
SDVA( &$GoogleMapDefaults, array( 'width' => '500',
                                'height' => '300',
                                'id' => 'map',
                                'view' => 'normal',
                                'zoom' => '13',
                                'coordinates' => array('lon' => '42.72500', 'lat' => '-81.200935'),
                                'address' => '',
                                'user-address' => false
                                ) );
                                

# HTMLHeader Defaults
$HTMLHeaderFmt[] = '<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=' . $GoogleMapKey . '"
            type="text/javascript"></script>';

            
# Other Defaults
$Parser = new Parse( &$GoogleMapDefaults );
$ParsedParams = null; // used by the parser
    
$HTMLHandle = new HtmlInterface();
$HTMLHandle->AddHtml( 'var map = null;
    var geocoder = null;

    function load()  {
      var map = new GMap2(document.getElementById("' . $GoogleMapDefaults['id'] . '"));
      var geocoder = new GClientGeocoder();
      
      if (GBrowserIsCompatible()) {
          map.addControl(new GLargeMapControl());
          map.addControl(new GMapTypeControl());' );


# Markup() Call
Markup( 'map', '>[=', '/\(:map(-location|end)?(\s.*?)?:\)/ie',
  "CvtMarkup(PSS('$1'), PSS('$2'))" );


/********************************
 * Function Definitions.
 * AKA: The Fun Stuff
 *******************************/
 
 
/* (string) furncion cvtMarkup($call, $params);
 * Discription: Main() function of script. CvtMarkup() checkes for the markup call
 *             used, and makes a call to one of two fucntions  depending on call
 *             type. CvtMarkup() returns the result of this function call to the
 *             wiki, which is the html needed to preform the action requested.
 */
function CvtMarkup( $call, $params )
{
    global $Parser;

    # Deturmine the call type.
    if ( strpos( $call, '-location' ) === 0 )
    {
        $type = 'location';
    }
    elseif ( strpos( $call, 'end' ) === 0 )
    {
       $type = 'end';
   }
    else
    {
       $type = 'generate';
   }

    # Get the paramaters passed to the scirpt; pass through PmWiki's ParseArgs to
    # make things easy
    $args = ParseArgs( $params );
    
    # Add paramaters to the Parser
    $Parser->AddCallParams ( $args );
    
    # Pass controll to another part of the script, depending on the call type.
    if ( $type == 'generate' )
    {
        $replace = GenerateMap();
    }
    elseif ( $type == 'location' )
    {
        $replace = AddLocation();
    }
    elseif ( $type == 'end' )
    {
        GoogleMapsDestruct();
    }

    return $replace;
}


/* (string) GenerateMap();
 * Discription: GenerateMap() does just as it's name implies: it generates a map.
 *              More specifically though, it adds the javascript from the Google
 *              Maps API, which generates the map for it.
 */
function GenerateMap( )
{
    global $GoogleMapDefaults, $ParsedParams, $Parser, $HTMLHandle;

    /*
     * Time to parse the arguments
     */

     $loctype = $Parser->ParseAddr(); // address/location
     $Parser->ParseZoom(); // zoom level

     // generate javascript for setting center.
     if ( $loctype == 'll' )
     {
         $HTMLHandle->addHtml( " \n" . 'map.setCenter(new GLatLng(' . $ParsedParams['coordinates']['lon'] . ',' . $ParsedParams['coordinates']['lat'] . '),' . $GoogleMapDefaults['zoom'] . ');' );
     }
     elseif ( $loctype == 'addr' )
     {
         $HTMLHandle->addHtml( "\n" . 'geocoder.getLatLng(
    \''. $ParsedParams['address'] . '\',
             function(point) {
            if (!point) {
              alert(" not found");
            } else {
              map.setCenter(point, ' . $ParsedParams['zoom'] . ');
            }
          });' );
     }
     
     
     /*
      * Check for user-address'
      */
      
     $Parser->ParseUserAddrBool();
     $form = null;
     
     if ( $ParsedParams['user-address'] )
     {
         # Figure out method of getting group and page name later to use here
         $form = '<br /><form action="'.$_SERVER['PHP_SELF'] . '/Google/Maps" method="post">
                    <input type="text" name="Address" style="width:' . $GoogleMapDefaults['width'] . 'px;" />
                  </form>';
     }
     
     if ( $_POST['Address'] && $GoogleMapDefaults['user-address'] && !$checked)
     {
         $checked = true; # avoid a recrusive function loop
         
         # Fourge Wiki-Markup call to add user address to map.
         CvtMarkup('-locaiton', 'address="' . $_GET['GoogleMap_usrAddr'] . '"');
      }


    return '<div id="' . $GoogleMapDefaults['id'] . '" title="Map: Turn on Javascript to display" style="width:' . $GoogleMapDefaults['width'] . 'px;height:' .$GoogleMapDefaults['height'] . 'px;"></div>' . $form;
}



/* (null) AddLocation();
 * Discription: AddLocation() is a function to add location points to the map.
 *              It uses much the same code for checking paramaters as GenerateMap()
 *              does, but it returns different javascript to PmWiki.
 */
function AddLocation()
{
    global $ParsedParams, $Parser, $HTMLHandle;
    
    $loctype = $Parser->ParseAddr();
    
    // generate javascript for adding location point.
     if ( $loctype == 'll' )
     {
         $HTMLHandle->addHtml( " \n" . 'var marker = new GMarker('. $ParsedParams['coordinates']['lon'] . ',' . $ParsedParams['coordinates']['lat'] . ');
         map.addOverlay(marker);' );
     }
     elseif ( $loctype == 'addr' )
     {
         $HTMLHandle->addHtml( "\n" . 'geocoder.getLatLng(
    \''. $ParsedParams['address'] . '\',
             function(point) {
            if (!point) {
              alert(" not found");
            } else {
              var marker = new GMarker(point);
              map.addOverlay(marker);
            }
          });' );
     }
     return null;
}


/* class Parse
 * Discription: The Parse class was created to handle the parsing of all paramaters
 *              to the wiki markup. If any paramater is not included with the wiki call,
 *              the respective parser function will use the defaults passed to in in its
 *              construct.
 */
 
class Parse
{
    var $defaults = null;
    var $wikiparams = null;
    
    
    /* (void) Parse( array $def )
     * Description: Parse() is the construct for the Parse class. It takes one
     *              paramater, $def, which should contain an array of the default
     *              values for the application. Generally, this will be the
     *              $GoogleMapDefaults array. The only case when it may not be, is...
     *              well, I can't think of one. So, just pass it the $GoogleMapDefaults
     *              array.
     */
     
    function Parse( $def )
    {
        $this->defaults = $def;
    }


    /* (void) AddWikiMarkup( array $newparams )
     * Description: AddWikiMarkp takes the parsed paramaters of a markup call as an
     *              array paramater, and adds it to an object member for use later.
     *              This method needs to be called before any parsing can take place,
     *              as all of the methods will return false (0) if they do not have,
     *              their default fallbacks set. This is a security measure.
     */

    function AddCallParams( $newparams )
    {
        $this->wikiparams = $newparams;
    }

    
    /* (string) ParseAddr()
     * Description: Although its name leads you to beleive this method would only
     *              parse an address, it is also used to parse latitudes and longitudes.
     *              In hines sight, it probably should have been called ParseLocation();
     *              however, it isn't.
     *
     *              This method checks and validates all location types to make sure
     *              they are valid, or atleast as far as it can see. If the location is
     *              valid, ParseAddr will store the location in the $ParsedParams array,
     *              and return a short string represinting the type ('addr' for address, and
     *              'll' for longitude and latitued.).
     */
     
    function ParseAddr()
    {
        global $ParsedParams;
        
        // Is it a longitude and a latitude?
        if ( preg_match( '/([0-9]{1,3}\..[0-9]{1,9}\,.[0-9]{1,3}\..[0-9]{1,9})/', $this->wikiparams['coordinates'] ) )
        {
            $loctype = 'll'; // It's a longitude and latitude
            $ll = explode( ',', $this->wikiparams['coordinates'] );

            $ParsedParams['coordinates'] = array( 'lon' => $ll[0],
                                                  'lat' => $ll[1] );
        }
        // Is it an address?
        elseif ( preg_match( '/(.*\s*)?/', $this->wikiparams['address'] ) )
        {
            $loctype = 'addr'; // It's an address
            $ParsedParams['address'] = $this->wikiparams['address'];
        }
        // Use Defaults
        else  {
            $loctype = 'll';
            $ParsedParams['location']['lon'] = $this->defaults['location']['lon'];
            $ParsedParams['location']['lat'] = $this->defaults['location']['lat'];
        }
        
        return $loctype;
    }
    
    /* (void) ParseUserAddrBool()
     * Description: ParseUserAddrBool is the method that dedides wether or not to show a
     *              user-address bar, a feature add in the alpha version to allow the user
     *              to enter their address to add to the map (usefull for getting directions).
     *              This setting is off by default.
     *
     *              Currently used as placeholder untill I get arround to actually coding it.
     */
    function ParseUserAddrBool()
    {
        global $GoogleMapDefaults, $ParsedParams;
        
        $ParsedParams['user-address'] = $this->defaults['user-address'];
    }
    
    
    function ParseZoom()
    {
    	global $ParsedParams;
    	
    	if ( !isset($this->wikiparams['zoom']) || !preg_match('/[0-9]{1,2}/', $this->wikiparams['zoom']) )
    	{
    		$ParsedParams['zoom'] = $this->defaults['zoom'];
    	}
    	else
    	{
    		$ParsedParams['zoom'] = $this->wikiparams['zoom'];
    	}

    }
    
    
}


class HtmlInterface
{
    var $Head;
    
    function HtmlInteface()
    {
        $this->Head = "<script type=\"text/javascript\">
                            //<![CDATA[\n";
    }
    
    function AddHtml( $new )
    {
        $this->Head .= $new;
    }
    
    function __destruct()
    {
        global $HTMLHeaderFmt;
        
        $this->Head .= "\n}    }
            //]]>
        </script>";
        
        $HTMLHeaderFmt[] = "<script type=\"text/javascript\">
                            //<![CDATA[\n" . $this->Head;
    }
}


/* (void) GoogleMapsDestruct()
 * Description: GoogleMapsDeastruct() preforms creanup and finalization for the script.
 *              It does several things, including calling of other class' destructs,
 *              memory clean-up, and [...].
 *
 *              The main reason this was added, was to allow output of <head>er data on
 *              script completion. It is affiliated with the (:map-end:) markup tag, and
 *              is required to run inorder to display the map.
 */
 
function GoogleMapsDestruct()
{
    global $HTMLHandle;
    
    $HTMLHandle->__destruct();
}