<?php

/*
	Googlyzer
	Contact: Bill Edgar (bill.edgar@oaktondata.com)
	http://www.oaktondata.com/googlyzer
	
	Copyright (c) 2011-2012, Oakton Data LLC
	All rights reserved.

	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
	    
	    * Redistributions of source code must retain the above copyright
	      notice, this list of conditions and the following disclaimer.
	    * Redistributions in binary form must reproduce the above copyright
	      notice, this list of conditions and the following disclaimer in the
	      documentation and/or other materials provided with the distribution.
	    * Neither the name of the Oakton Data LLC nor the
	      names of its contributors may be used to endorse or promote products
	      derived from this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL Oakton Data LLC BE LIABLE FOR ANY
	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	
	This is the googlyzer_dashboard PHP class file.
	
 */

if ( !class_exists( 'googlyzer_Dashboard' ) ) {
	
	// googlyzer_dashboard class
	class googlyzer_dashboard {
		
		/*
		 * public variables
		 */
		
		/*
		 * private variables
		 */
		private $genChartOpts = 		array(
			'googlyzer-composites' =>	'composite_general',
			'googlyzer-linecharts' =>	'linechart_general',
			'googlyzer-piecharts' =>	'piechart_general',
			'googlyzer-tables' =>		'table_general'
			);
		private $chartOpts = 			array( 
			'googlyzer-composites' => 	array( 'composite1' ),
			'googlyzer-linecharts' => 	array( 'linechart1', 'linechart2', 'linechart3', 'linechart4' ),
			'googlyzer-piecharts' => 	array( 'piechart1', 'piechart2' ),
			'googlyzer-tables' =>		array( 'table1', 'table2' )
			);
		private $composite_metrics = 	array( 
			'metric_1', 
			'metric_2', 
			'metric_3', 
			'metric_4', 
			'metric_5' 
			);
		private $parentInstance = 		null;
		
		// old style constructor method for backward PHP compatibility
		public function googlyzer_Dashboard( $parentInstance ) {
			$this->__construct( $parentInstance );
		}
		
		// public constructor
		public function __construct( $parentInstance ) {
			// record reference to parent object
			$this->parentInstance = $parentInstance;
			// load Googlyzer dashboard
			$this->loadDashboard();
		}

		/*
		 * private functions
		 */
		
		/**
		 * 
		 * private method for output of composite chart (chart, labels, etc.)
		 * 
		 * @param string $chartName
		 * @param array $genChartOpts
		 * @param array $chartOpts
		 * @param object $dataObj
		 * @param string $dashHtml (HTML output for dashboard page)
		 * @return string $dashHtml (updated HTML)
		 */
		private function composite( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml ) {
			$metaVals = array();
			// retrieve line colors and metrics
			$lineColors = $this->getCompositeColors( $chartOpts );
			$metrics = $this->getCompositeMetrics( $chartOpts );
			// before doing any sparklines, must output javascript to make page elements visible
			$this->displayChartElements( $chartName, $chartOpts );
			// then get chart label
			$title = $this->getChartTitle( $type = 'composite', $chartName, $chartOpts );
			// insert title into html for output
			$dashHtml = str_replace( '[' . $chartName . '_title]', $title, $dashHtml );
			// initialize composite chart obj
			$compositeChart = new googlyzer_ChartObj( array(
				'composite' => 		true,
				'elementId' => 		$chartName,
				'height' =>			$genChartOpts['height'],
				'type' => 			'line',
				'width' => 			$genChartOpts['width']
				) );
			// loop through each data set for composite chart
			$script = "<script type='text/javascript'>\njQuery(document).ready(function() {\n";
			foreach ( $metrics as $key => $metricName ) {
				if ( $metricName != 'none' ) {
					// set chart specific properties first
					$compositeChart->setProperties( array(
						'values' => join( ",", $dataObj->getMetricValuesList( $metric = $metricName ) ),
						'lineColor' => array_shift( $lineColors )
						) );
					// rebuild sparkline script tags
					$compositeChart->buildTags();
					// then output sparkline script
					$script .= "\n" . $compositeChart->getSparklineScript();
					// capture metric total for metadata output
					$metaVals[$metricName] =  $dataObj->getMetaDataVal( 'total', $metricName, 'current' );
				}
			}
			if ( $chartOpts['metadata_enable'] === 'enabled' ) {
				$html = $this->getCompositeDataLabels( $metaVals, $this->getCompositeColors( $chartOpts ) );
				$metaData = $this->metaDataStart() . $html . $this->metaDataEnd();
				// insert metadata into html for output
				$dashHtml = str_replace( '[' . $chartName . '_metadata]', $metaData, $dashHtml );
			}
			// insert scripts into html for output
			$dashHtml = str_replace( '[' . $chartName . '_script]', $script . "});\n</script>", $dashHtml );
			// return updated HTML
			return $dashHtml;
		}
		
		/**
		*
		* method to output javascript for displaying chart elements in HTML doc
		*
		* @param string $chartName
		*/
		private function displayChartElements( $chartName, $chartOpts ) {
			$elementList = "'" . $chartName . "'";
			if ( array_key_exists( 'metadata_enable', $chartOpts ) && $chartOpts['metadata_enable'] === 'enabled' ) {
				$elementList .= ", '" . $chartName . "_metadata'";
			}
			// before doing any sparklines, must output javascript to make page elements visible
			?>
				<script type="text/javascript">
					jQuery(document).ready(function() { 
						googlyzer_ShowHideElements( true, new Array( <?php echo $elementList; ?> ) );
						});
				</script>
			<?php
		}
		
		/**
		 * 
		 * private method for chart output
		 * 
		 * @param string $chartName
		 * @param string $type (composite, line, pie, or table)
		 * @param array $genChartOpts (general chart options array)
		 * @param array $chartOpts (chart options array)
		 * @param object $dataObj (googlyzer_AnalyticsDataObj)
		 * @param string $dashHtml (HTML output for dashboard page)
		 * @return string $dashHtml (updated HTML)
		 */
		private function drawChart( $chartName, $type, $genChartOpts, $chartOpts, $dataObj, $dashHtml ) {
			switch ( $type ) {
				case 'composite':
					$dashHtml = $this->composite( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml );
					break;
				case 'line':
					$dashHtml = $this->line( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml );
					break;
				case 'pie':
					$dashHtml = $this->pie( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml );
					break;
				case 'table':
					$dashHtml = $this->table( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml );
					break;
			}
			return $dashHtml;
		}
		
		private function getCompositeColors( $chartOpts ) {
			$colors = array();
			for ( $i=1; $i<6; $i++ ) {
				array_push( $colors, $chartOpts['line_color_' . $i] );
			}
			return $colors;
		}
		
		private function getCompositeDataLabels( $metaVals, $colors ) {
			$html = "<tr>";
			foreach ( $metaVals as $metric => $total ) {
				$html .= "<td>" . $this->parentInstance->getMetricLabel( $metric ) . "&nbsp;<font color='" . array_shift( $colors ) . "'>" .
				$total . "</font></td>";
			}
			return $html . "</tr>";
		}
		
		private function getCompositeMetrics( $chartOpts ) {
			$metrics = array();
			for ( $i=1; $i<6; $i++ ) {
				if ( $chartOpts['metric_' . $i] !== 'none' ) {
					array_push( $metrics, $chartOpts['metric_' . $i] );
				}
			}
			return $metrics;
		}
		
		/**
		 * 
		 * method to output chart title
		 * 
		 * @param string $type (composite, line, pie, or table)
		 * @param string $chartName
		 * @param array $chartOpts (chart options array)
		 * @return string $chartTitleTags (HTML output string)
		 */
		private function getChartTitle( $type, $chartName, $chartOpts ) {
			$chartTitleStart = "<p class='chart_title'>" . "<span id='" . $chartName . "_title'>";
			$chartTitleEnd = "</span></p>";
			switch ( $type ) {
				case 'composite':
					$chartTitle = $chartTitleStart . "Totals" . $chartTitleEnd;
					break;
				case 'line':
					$metricLabel = $this->parentInstance->getMetricLabel( $chartOpts['metrics'] );
					$dimensionLabel = $this->parentInstance->getDimensionLabel( $chartOpts['dimensions'] );
					$chartTitle = $chartTitleStart . $metricLabel . " by " . $dimensionLabel . $chartTitleEnd;
					break;
				case 'pie':
					$pieTitle = $this->parentInstance->getPieChartLabel( $chartOpts['chart_selection'] );
					$chartTitle = $chartTitleStart . $pieTitle . $chartTitleEnd;
					break;	
			}
			return $chartTitle;
		}
		
		/**
		*
		* method to determine chart type based on name pattern recognition
		*
		* @param string $chart
		* @return string $type
		*/
		private function getChartType( $chart ) {
			if ( preg_match( "/comp/", $chart ) ) {
				$type = 'composite';
			} elseif ( preg_match( "/line/", $chart ) ) {
				$type = 'line';
			} elseif ( preg_match( "/pie/", $chart ) ) {
				$type = 'pie';
			} elseif ( preg_match( "/table/", $chart ) ) {
				$type = 'table';
			}
			return $type;
		}
		
		/**
		 *
		 * method to retrieve basic metadata for linechart html output
		 *
		 * @param string $fontColor (hex color code)
		 * @param string $metric (ga:metric_name)
		 * @param string $period (current or history)
		 * @param object $dataObj (googlyzer_AnalyticsDataObj)
		 * @return string $metaDataTags (HTML output string)
		 */
		private function getLineMetaData( $fontColor, $metric, $period, $dataObj ) {
			// set up array of metadata items
			$metaLabels = array( 'max' => 'Max', 'min' => 'Min', 'avg' => 'Avg', 'last' => 'Last' );
			// loop through each metadata item
			$metaDataTags = "<tr>";
			foreach ( $metaLabels as $key => $label ) {
				// build html output for metadata item
				$metaDataTags .= "<td>" . $label .
					"&nbsp;<font color='" . $fontColor . 
					"'>" . $dataObj->getMetaDataVal( $key, $metric, $period ) . 
					"</font></td>";
			}
			return $metaDataTags . "</tr>";
		}
		
		private function getPieDataLabels( $chartOpts, $dataObj ) {
			$data = array();
			$data['values'] = array();
			// get pie data values
			if (  $chartOpts['chart_selection'] !== 'referral_source' ) {
				$pieData = $dataObj->getPieData();
			} else {
				$pieData = $this->pieReferralSources( $chartOpts, $dataObj );
			}
			// get last element in pieData array
			$end = end( $pieData );
			reset( $pieData ); // reset array pointer
			// get slice color array
			$sliceColors = $this->getSliceColors( $chartOpts );
			// start html output string 
			$html = "<p>";
			// loop through piedata values
			foreach ( $pieData as $sliceName => $sliceValue ) {
				array_push( $data['values'], $sliceValue );
				// append to html output string
				$html .= $sliceName . "&nbsp;<font color='" .
				array_shift( $sliceColors ) . "'>" .
				ltrim( $sliceValue, '0' ) . "&#37;</font>";
				// if not the last element, add a non-breaking space to html output
				if ( $sliceName !== $end ) {
					$html .= "&nbsp; ";
				}
			}
			$data['labels'] = $html . "</p>";
			return $data;
		}
		
		/**
		 *
		 * method to push selected slice colors into single array
		 *
		 * @param array $chartOpts (options array for chart)
		 * @return array $sliceColors (array of hex colors)
		 */
		private function getSliceColors ( $chartOpts ) {
			$sliceColors = array();
			for ( $i=1; $i<6; $i++ ) {
				array_push( $sliceColors, $chartOpts['slice_color_' . $i] );
			}
			return $sliceColors;
		}
		
		/**
		 *
		 * method to retrieve trend metadata for linechart html output
		 *
		 * @param string $metric (ga:metric_name)
		 * @param object $dataObj (googlyzer_AnalyticsDataObj)
		 * @return string $trendTags (HTML output string)
		 */
		private function getTrendData( $metric, $dataObj ) {
			$positiveColor = "#003300";
			$negativeColor = "#990000";
			$dailyTrend = $dataObj->getTrend( 'dailyTrend', $metric );
			$dateRangeTrend = $dataObj->getTrend( 'dateRangeTrend', $metric );
			if ( $dailyTrend > 0 ) {
				$dailyFontColor = $positiveColor;
				$dailySign = "+";
			} else {
				$dailyFontColor = $negativeColor;
				$dailySign = "";
			}
			if ( $dateRangeTrend > 0 ) {
				$dateRangeFontColor = $positiveColor;
				$dateRangeSign = "+"; 	
			} else {
				$dateRangeFontColor = $negativeColor;
				$dateRangeSign = "";
			}
			$trendTags = "<tr><td colspan='4'><center>Daily&nbsp;" . 
				"<font color='" . $dailyFontColor . "'>" . $dailySign . $dailyTrend . "&#37;</font>&nbsp;&nbsp;";
			$trendTags .= "Date Range&nbsp;<font color='" . 
				$dateRangeFontColor . "'>" . $dateRangeSign . $dateRangeTrend . "&#37;</font></center></td></tr>";
			return $trendTags;
		}
		
		/**
		 * 
		 * method to output linechart(s)
		 * 
		 * @param string $chartName
		 * @param array $genChartOpts
		 * @param array $chartOpts
		 * @param object $dataObj
		 * @param string $dashHtml (HTML output for dashboard page)
		 * @return string $dashHtml (updated HTML)
		 */
		private function line( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml ) {
			$script = "<script type='text/javascript'>\njQuery(document).ready(function() {\n";
			// before doing any sparklines, must output javascript to make page elements visible
			$this->displayChartElements( $chartName, $chartOpts );
			// then output chart label 
			$title = $this->getChartTitle( $type = 'line', $chartName, $chartOpts );
			// insert title into html for output
			$dashHtml = str_replace( '[' . $chartName . '_title]', $title, $dashHtml );
			// check for history comparison
			$composite = ( $chartOpts['history_enable'] ) ? true : false;
			// set up linechart object
			$lineChart = new googlyzer_ChartObj( array(
				'composite' => 		false,
				'elementId' => 		$chartName,
				'fillColor' => 		false,
				'height' => 		$genChartOpts['height'],
				'lineColor' => 		$chartOpts['color'],
				'type' => 			'line',
				'values' =>			$dataObj->getMetricValuesString( $metric = $chartOpts['metrics'], $period = 'current', $historyFlag = false ),
				'width' => 			$genChartOpts['width']
				) );
			// set up sparkline tags
			$lineChart->buildTags();
			$metaData = $this->metaDataStart();
			// do history sparkline first, if option selected
			if ( $chartOpts['history_enable'] === 'enabled' ) {
				$lineChart->setProperties( array(
					'lineColor' =>	$chartOpts['history_color'],
					'values' => 	$dataObj->getMetricValuesString( $metric = $chartOpts['metrics'], $period = 'history' )
					) );
				// rebuild sparkline chart tags
				$lineChart->buildTags();
				// output sparkline script
				$script .= $lineChart->getSparklineScript();
				// get metadata for history, if selected
				if ( $chartOpts['metadata_enable'] === 'enabled' ) {
					$metaData .= $this->getLineMetaData( $chartOpts['history_color'], $chartOpts['metrics'], 'history', $dataObj );
				}
				// reset linechart object properties for current data
				$lineChart->setProperties( array(
					'composite' => 	true,
					'lineColor' =>	$chartOpts['color'],
					'values' => 	$dataObj->getMetricValuesString( $metric = $chartOpts['metrics'] )
					) );
				// rebuild sparkline chart tags
				$lineChart->buildTags();
			}
			// then output sparkline script for current data
			$script .= "\n" . $lineChart->getSparklineScript() . "});\n</script>";
			// finally, output chart metadata if selected
			if ( $chartOpts['metadata_enable'] === 'enabled' ) {
				$metaData .= $this->getLineMetaData( $chartOpts['color'], $chartOpts['metrics'], 'current', $dataObj );
			} else {
				$metaData = "";
			}
			// get trend data, if selected
			if ( $chartOpts['trend_enable'] === 'enabled' ) {
				$metaData .= $this->getTrendData( $chartOpts['metrics'], $dataObj );	
			}
			// finish metadata tag output
			$metaData .= $this->metaDataEnd();
			// insert metadata into html for output
			$dashHtml = str_replace( '[' . $chartName . '_metadata]', $metaData, $dashHtml );
			// insert scripts into html for output
			$dashHtml = str_replace( '[' . $chartName . '_script]', $script, $dashHtml );
			return $dashHtml;
		}
		
		private function getDateLabel( $genOpts ) {
			// get date range selection
			$selection = $genOpts['date_range'];
			// check for custom date range
			if ( $selection === 'Custom Range' ) {	
				// reset start date for GA data feed, if necessary
				$start = googlyzer_Utilities::validStart( $genOpts['dataStart'], $genOpts['start'] );
				if ( !googlyzer_Utilities::validDate( $start, 'm-d-Y' ) ) {
					$start = googlyzer_Utilities::convertDateString( $start, 'googlyzer' );
				}
				$label = 'Displaying Custom Range --- Start Date: ' . $start . '&nbsp;&nbsp;End Date: ' . $genOpts['end'];
			} else {
				$label = 'Displaying data for the past ' . $selection;
				if ( $genOpts['today_enable'] === 'enabled' ) {
					$label .= ' (including today).';
				} else {
					$label .= ' (not including today).';
				}
			}
			return $label;
		}
		
		// method to load dashboard page
		private function loadDashboard() {
			$dashHtml = false;
			// retrieve general option group from parent
			$genOpts = googlyzer_Utilities::getOptions( 'googlyzer-options', 'standard' );
			// make sure that Google authorization has been completed
			if ( googlyzer_Utilities::validateSetup() ) {
				try {
					// determine first occurrence of GA data for domain profile
					$genOpts['dataStart'] = googlyzer_Utilities::determineGADataStart( $genOpts );
					$this->parentInstance->updateGATracking();
					$ga_tracking = googlyzer_Utilities::getOptions( 'googlyzer-ga_tracking', 'ga_tracking_general' );
					$domain = $this->parentInstance->getDomainName();
					$ga_tableid = $this->parentInstance->getGATableId();
					$date_label = $this->getDateLabel( $genOpts );
					// output html for dashboard
					$dashHtml = googlyzer_Utilities::getFilteredHtml( GOOGLYZER_DASHBOARD_HTML, array( 
						'<!-- GOOGLYZER_VERSION -->' => 'Googlyzer v' . GOOGLYZER_VERSION,
						'<!-- DOMAIN -->' => $domain,
						'<!-- GA_TABLEID -->' => $ga_tableid,
						'<!-- GA_BEGINNING -->' => $ga_tracking['ga_beginning'],
						'<!-- DATE_RANGE -->' => $date_label,
						'<!-- ACCOUNT_ID -->' => $ga_tracking['ga_accountid'],
						'<!-- ACCOUNT_NAME -->' => $ga_tracking['ga_accountname'],
						'<!-- PROFILE_ID -->' => $ga_tracking['ga_profileid'],
						'<!-- WEB_PROPERTY_ID -->' => $ga_tracking['ga_webpropertyid'],
						) );
				} catch ( googlyzer_Exception $e ) {
					echo $e->getError();
				}
				if ( $dashHtml ) {
					// output javascript for header info
					?>
						<script type="text/javascript">
							// jquery for collapsing options areas
							jQuery(document).ready(function() {
								//toggle the component with class msg_body
								jQuery(".head_panel").click(function() {
							    	jQuery(this).next(".content_panel").slideToggle(300);
									});
								});				
					    </script>
				    <?php
			    	
			    	// outer loop for chart groups
			    	foreach ( $this->chartOpts as $optionGroup => $chartGroup ) {
			    		// inner loop for individual charts
			    		foreach ( $chartGroup as $key => $chartName ) {
			    			// set chart type
			    			$type = $this->getChartType( $chartName );
			    			// retrieve chart option groups from parent
			    			$chartOpts = googlyzer_Utilities::getOptions( $optionGroup, $chartName );
			    			$genChartOpts = googlyzer_Utilities::getOptions( $optionGroup, $this->genChartOpts[$optionGroup] );
			    			// check to see if chart will be displayed
			    			if ( $chartOpts['enable'] === 'enabled' ) {
			    				$retry = true;
			    				$retry_count = 0;
			    				$max_retries = 1;
			    				while ( $retry && $retry_count <= $max_retries ) {
				    				try {
				    					// map chart options into args for data request
				    					$args = $this->setFeedArgs( $type, $genOpts, $chartOpts, $genChartOpts );
					    				// create data feed object
					    				$feedObj = new googlyzer_DataFeed( 
			    							$feedType =				'data_request',
			    							$url = 					'https://www.google.com/analytics/feeds/data?',
			    							$method = 				'GET',
					    					$feedSpecificSettings = array(
			    								'GA_AUTH_TOKEN' =>	$genOpts['ga_auth_token'],
			    								'feedArgs' =>		$args
					    						)
					    					);
					    				// request data from Google
					    				$feedObj->getFeed();
					    				// load XML data into data object
					    				$dataObj = $feedObj->handleFeed();
					    				// complete set up of data object (metadata capture)
					    				$dataObj->analyze( $type, $chartOpts );
					    				$dashHtml = $this->drawChart( $chartName, $type, $genChartOpts, $chartOpts, $dataObj, $dashHtml );
					    				$retry = false;
				    				} catch ( googlyzer_Exception $e ) {
				    					try {
											if ( !$genOpts['ga_auth_token'] = googlyzer_Utilities::checkException( $e, $genOpts ) ) {
												$retry = false;
											}
				    					} catch ( googlyzer_Exception $error ) {
				    						echo $error->getError();	
				    					}
									}
									$retry_count++;
								}
			    			}
			    		}
			    	}
			    	// output full dashboard page
			    	echo $dashHtml;
				} else {
					// failed to load dashboard html
				}
		    } else {
		    	// otherwise Google authorization has not been completed
		    }
			
		} // end method loadDashboard
		
		/**
		 * 
		 * method to output metadata start tags (HTML)
		 */
		private function metaDataStart() {
			return "<table class='metadata'><tbody>";	
		}
		
		/**
		*
		* method to output metadata end tags (HTML)
		*/
		private function metaDataEnd() {
			return 	"</tbody></table>";
		}
		
		/**
		*
		* private method for output of piechart (chart, labels, etc.)
		*
		* @param string $chartName
		* @param array $genChartOpts
		* @param array $chartOpts
		* @param object $dataObj
		* @param string $dashHtml (HTML output for dashboard page)
		* @return string $dashHtml (updated HTML)
		*/
		private function pie( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml ) {
			// before doing any sparklines, must output javascript to make page elements visible
			$this->displayChartElements( $chartName, $chartOpts );
			// then output chart label
			$title = $this->getChartTitle( $type = 'pie', $chartName, $chartOpts );
			// insert title into html for output
			$dashHtml = str_replace( '[' . $chartName . '_title]', $title, $dashHtml );
			// get values and data labels
			// $pieValues = join( ",", $dataObj->getMetricValuesList( $metric = 'ga:visits' ) );
			$pieData = $this->getPieDataLabels( $chartOpts, $dataObj );
			// set up piechart object
			$pieChart = new googlyzer_ChartObj( array(
				'elementId' => 		$chartName,
				'height' =>			$genChartOpts['height'],
				'sliceColors' =>	$this->getSliceColors( $chartOpts ),
				'type' => 			'pie',
				'values' =>			join( ",", $pieData['values'] )
				) );
			// output sparkline script
			$script = "<script type='text/javascript'>" . $pieChart->getSparklineScript() . "</script>";
			// insert labels into html for output
			$dashHtml = str_replace( '[' . $chartName . '_labels]', $pieData['labels'], $dashHtml );
			// insert scripts into html for output
			$dashHtml = str_replace( '[' . $chartName . '_script]', $script, $dashHtml );
			return $dashHtml;
		}
		
		/**
		 * 
		 * private method for processing referral source data for piechart
		 * 
		 * @param array $chartOpts
		 * @param object $dataObj
		 * @return array $pieData
		 */
		private function pieReferralSources( $chartOpts, $dataObj ) {
			$metricName = 'ga:visits'; // metric value to be retrieved based on dimension value matches
			$pieTotal = 0; // for tracking piechart total to calculate % for each slice
			// set up categories for referral sources
			$source = array( 'Direct'=> array(), 'Referral'=> array(), 'Search'=> array() );
			// get direct requests
			$source['Direct'] = 
				$dataObj->entrySearch( 
					$patterns = 				array( 'ga:source' => array( 
						'pattern' => 			"(direct)",  
						'type' =>				'dimensions',
						'operation' =>			'equal'
						) ),
					$retrieve = 				'metrics', 
					$name = 					$metricName 
					);
			// get referrals
			$source['Referral'] = 
				$dataObj->entrySearch( 
					$patterns = 				array( 
						'ga:source' => 			array(
						'pattern' =>			"(direct)", 
						'type' =>				'dimensions',
						'operation' => 			'not_equal'
						),
						'ga:referralPath' => 	array(
						'pattern' => 			"(not set)",
						'type' =>				'dimensions',
						'operation' => 			'not_equal'
						) ),
					$retrieve = 				'metrics',
					$name = 					$metricName
					);
			// get searches
			$source['Search'] =
				$dataObj->entrySearch(
					$patterns = 				array( 'ga:referralPath' => array(
						'pattern' => 			"(not set)",
						'type' =>				'dimensions',
						'operation' => 			'equal'
						) ),
					$retrieve = 				'metrics',
					$name = 					$metricName
					);
			// total up 
			foreach ( $source as $key => $typeArray ) {
				$type = key( $typeArray );
				$pieData[$key] = array_sum( $typeArray[$type] );
				$pieTotal += $pieData[$key];
			}
			// sort $pieData array by value
			$pieData = googlyzer_Utilities::objSort( $myObj = $pieData, $sortOrder = 'descending' );
			// calc slice metadata
			foreach ( $pieData as $sliceName => $value ) {
				$pieData[$sliceName] = round( $value / $pieTotal * 100, 0 );
			}			
			return $pieData;
		}
		
		/**
		 * 
		 * method to set args array for proper GA data feed request
		 * 
		 * @param string $type
		 * @param array $genOpts
		 * @param array $chartOpts
		 * @param array $genChartOpts
		 * @return array $args
		 */
		private function setFeedArgs( $type, $genOpts, $chartOpts, $genChartOpts ) {
			$args = array();
			// load ga table id
			$args['id'] = $genOpts['ga_tableid'];
			// set dates for request
			$args = googlyzer_Utilities::setDates( $args, $type, $genOpts, $chartOpts );
			// reset start date for GA data feed, if necessary
			$args['start'] = googlyzer_Utilities::validStart( $genOpts['dataStart'], $args['start'] );
			// load dimensions into an array
			if ( !preg_match( "/pie/", $type ) ) {
				$args['dimensions'] = ( is_array( $chartOpts['dimensions'] ) ) ? $chartOpts['dimensions'] : array( $chartOpts['dimensions'] );
				$args['sort'] = $chartOpts['sort_order'];
				$args['sort_column'] = $chartOpts['sort_column'];
			} else {
				switch ( $chartOpts['chart_selection'] ) {
					case 'browser':
						$args['dimensions'] = array( 'ga:browser' );
						$args['metrics'] = array( 'ga:visits' );
						$args['sort'] = 'descending';
						$args['sort_column'] = 'ga:visits';
						break;
					case 'referral_source':
						$args['dimensions'] = array( 'ga:source', 'ga:referralPath' );
						$args['metrics'] = array( 'ga:visits' );
						$args['sort'] = 'descending';
						$args['sort_column'] = 'ga:visits';
						break;
					case 'visitor_types':
						$args['dimensions'] = array( 'ga:visitorType' );
						$args['metrics'] = array( 'ga:visits' );
						$args['sort'] = 'descending';
						$args['sort_column'] = 'ga:visits';
						break;
				}
			}
			// if type is table or piechart, max_results are specific to the chart/table
			if ( preg_match( "/table/", $type ) ) {
				$args['max_results'] = $chartOpts['num_rows'];
				if ( $chartOpts['dimension_filter_enable'] == 'enabled' ) {
					$args = googlyzer_Utilities::setGoogleAnalyticsRequestFilter(
						$args,
						$chartOpts['dimensions'],
						$chartOpts['dimension_filter_string'],
						$chartOpts['dimension_filter_op'],
						'dimensions'
						);
				}
				if ( $chartOpts['metric_filter_enable'] == 'enabled' ) {
					$args = googlyzer_Utilities::setGoogleAnalyticsRequestFilter(
						$args,
						$chartOpts['metrics'],
						$chartOpts['metric_filter_value'],
						$chartOpts['metric_filter_op'],
						'metrics'
						);
				}
			} elseif ( preg_match( "/pie/", $type ) ) {
				$args['max_results'] = $chartOpts['max_results'];
			} else {
				$args['max_results'] = $genChartOpts['max_results'];
			}
			// if type is composite, metrics must be pushed into a single array
			if ( preg_match( "/composite/", $type ) ) {
				/*
				 * specific to composites
				 */
				// load metrics into an array
				$args['metrics'] = array();
				foreach ( $this->composite_metrics as $key => $metric_num ) {
					if ( $chartOpts[$metric_num] !== 'none' ) {
						array_push( $args['metrics'], $chartOpts[$metric_num] );
					}
				}
			} elseif ( !preg_match( "/pie/", $type ) ) {
				// load metrics into an array
				$args['metrics'] = ( is_array( $chartOpts['metrics'] ) ) ? $chartOpts['metrics'] : array( $chartOpts['metrics'] );
			}
			return $args;
		}
		
		/**
		*
		* private method for output of table (html table, labels, etc.)
		*
		* @param string $chartName
		* @param array $genChartOpts
		* @param array $chartOpts
		* @param object $dataObj
		* @param string $dashHtml (HTML output for dashboard page)
		* @return string $dashHtml (updated HTML)
		*/
		private function table( $chartName, $genChartOpts, $chartOpts, $dataObj, $dashHtml ) {
			$html = "";
			// before creating table, must output javascript to make page elements visible
			$this->displayChartElements( $chartName, $chartOpts );
			// output table header
			$html .= $this->tableHeader( $chartName, $chartOpts );
			// then get the data rows
			$rows = $dataObj->getTableRows();
			foreach ( $rows as $columnA => $columnB ) {
				$dataRow = "<tr><td class='list-lft'>";
				$dataRow .= $columnA . "</td><td class='list-ctr'>" . $columnB;
				$dataRow .= "</td></tr>";
				$html .= $dataRow;
			}
			// finish html table
			$html .= "</tbody></table><br>";
			// insert table into html for output
			$dashHtml = str_replace( '[' . $chartName . ']', $html, $dashHtml );
			return $dashHtml;
		}
		
		private function tableHeader( $chartName, $chartOpts ) {
			// first create html table and output header info
			$html = "<table class='center'><tbody>";
			$metric = $this->parentInstance->getMetricLabel( $chartOpts['metrics'] );
			$dimension = $this->parentInstance->getDimensionLabel( $chartOpts['dimensions'] );
			$title = $metric . " / " . $dimension;
			$tableHeaders = "<th>" . $dimension . "</th><th>" . $metric . "</th>";
			$tableTags = $html . "<tr><td colspan='2'><p class='chart_title'><span id='" .
				$chartName . "_title'>" . $title . "</span></p><td></tr>" . 
				"<tr>" . $tableHeaders . "</tr>";
			return $tableTags;			
		}
			
	} // end class googlyzer_Dashboard
		
}
?>