<?php
# Mantis - a php based bugtracking system

# Mantis 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.
#
# Mantis 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 Mantis.  If not, see <http://www.gnu.org/licenses/>.

/**
 * @package CoreAPI
 * @subpackage GraphAPI
 * @copyright Copyright (C) 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
 * @copyright Copyright (C) 2002 - 2009  Mantis Team   - mantisbt-dev@lists.sourceforge.net
 * @link http://www.mantisbt.org
 */

if( ON == config_get( 'use_jpgraph' ) ) {
	$t_jpgraph_path = config_get( 'jpgraph_path' );

	require_once( $t_jpgraph_path . 'jpgraph.php' );
	require_once( $t_jpgraph_path . 'jpgraph_line.php' );
	require_once( $t_jpgraph_path . 'jpgraph_bar.php' );
	require_once( $t_jpgraph_path . 'jpgraph_pie.php' );
	require_once( $t_jpgraph_path . 'jpgraph_pie3d.php' );
	require_once( $t_jpgraph_path . 'jpgraph_canvas.php' );
}

function graph_get_font() {
	$t_font_map = array(
		'arial' => FF_ARIAL,
		'verdana' => FF_VERDANA,
		'courier' => FF_COURIER,
		'comic' => FF_COMIC,
		'times' => FF_TIMES,
		'georgia' => FF_GEORGIA,
		'trebuche' => FF_TREBUCHE,
		'vera' => FF_VERA,
		'veramono' => FF_VERAMONO,
		'veraserif' => FF_VERASERIF,
	);

	$t_font = config_get( 'graph_font', '' );
	if( isset( $t_font_map[$t_font] ) ) {
		return $t_font_map[$t_font];
	} else {
		return FF_FONT1;
	}
}

# ## Graph API ###
# --------------------
# graphing routines
# --------------------
function graph_bar( $p_metrics, $p_title = '', $p_graph_width = 350, $p_graph_height = 400 ) {

	$t_graph_font = graph_get_font();

	error_check( is_array( $p_metrics ) ? array_sum( $p_metrics ) : 0, $p_title );

	$graph = new Graph( $p_graph_width, $p_graph_height );
	$graph->img->SetMargin( 40, 40, 40, 170 );
	if( ON == config_get_global( 'jpgraph_antialias' ) ) {
		$graph->img->SetAntiAliasing();
	}
	$graph->SetScale( 'textlin' );
	$graph->SetMarginColor( 'white' );
	$graph->SetFrame( false );
	$graph->title->Set( $p_title );
	$graph->title->SetFont( $t_graph_font, FS_BOLD );
	$graph->xaxis->SetTickLabels( array_keys( $p_metrics ) );
	if( FF_FONT2 <= $t_graph_font ) {
		$graph->xaxis->SetLabelAngle( 60 );
	} else {
		$graph->xaxis->SetLabelAngle( 90 );

		# can't rotate non truetype fonts
	}
	$graph->xaxis->SetFont( $t_graph_font );

	$graph->legend->SetFont( $t_graph_font );

	$graph->yaxis->scale->ticks->SetDirection( -1 );
	$graph->yaxis->SetFont( $t_graph_font );

	$p1 = new BarPlot( array_values( $p_metrics ) );
	$p1->SetFillColor( 'yellow' );
	$p1->SetWidth( 0.8 );
	$graph->Add( $p1 );
	if( helper_show_queries() ) {
		$graph->subtitle->Set( db_count_queries() . ' queries (' . db_count_unique_queries() . ' unique) (' . db_time_queries() . 'sec)' );
		$graph->subtitle->SetFont( $t_graph_font, FS_NORMAL, 8 );
	}

	$graph->Stroke();
}

# Function which displays the charts using the absolute values according to the status (opened/closed/resolved)
function graph_group( $p_metrics, $p_title = '', $p_graph_width = 350, $p_graph_height = 400, $p_baseline = 100 ) {

	# $p_metrics is an array of three arrays
	#   $p_metrics['open'] = array( 'enum' => value, ...)
	#   $p_metrics['resolved']
	#   $p_metrics['closed']

	$t_graph_font = graph_get_font();

	# count up array portions that are set
	$t_count = 0;
	foreach( array( 'open', 'resolved', 'closed' ) as $t_label ) {
		if( isset( $p_metrics[$t_label] ) && is_array( $p_metrics[$t_label] ) ) {
			$t_count += array_sum( $p_metrics[$t_label] );
		}
	}

	error_check( $t_count, $p_title );

	# calculate totals
	$total = graph_total_metrics( $p_metrics );

	# defines margin according to height
	$graph = new Graph( $p_graph_width, $p_graph_height );
	$graph->img->SetMargin( 45, 35, 35, $p_baseline );
	if( ON == config_get_global( 'jpgraph_antialias' ) ) {
		$graph->img->SetAntiAliasing();
	}
	$graph->SetScale( 'textlin' );
	$graph->SetMarginColor( 'white' );
	$graph->SetFrame( false );
	$graph->title->SetFont( $t_graph_font, FS_BOLD );
	$graph->title->Set( $p_title );
	$graph->xaxis->SetTickLabels( array_keys( $p_metrics['open'] ) );
	if( FF_FONT2 <= $t_graph_font ) {
		$graph->xaxis->SetLabelAngle( 60 );
	} else {
		$graph->xaxis->SetLabelAngle( 90 );

		# can't rotate non truetype fonts
	}
	$graph->xaxis->SetFont( $t_graph_font );
	$graph->legend->Pos( 0.05, 0.08 );
	$graph->legend->SetFont( $t_graph_font );

	$graph->yaxis->scale->ticks->SetDirection( -1 );
	$graph->yaxis->SetFont( $t_graph_font );
	$graph->yscale->SetGrace( 10 );

	# adds on the same graph
	$tot = new BarPlot( array_values( $total ) );
	$tot->SetFillColor( 'lightblue' );
	$tot->SetWidth( 0.7 );
	$tot->SetLegend( lang_get( 'legend_total' ) );
	$graph->Add( $tot );

	$p1 = new BarPlot( array_values( $p_metrics['open'] ) );
	$p1->SetFillColor( 'yellow' );
	$p1->SetWidth( 1 );
	$p1->SetLegend( lang_get( 'legend_opened' ) );

	$p2 = new BarPlot( array_values( $p_metrics['closed'] ) );
	$p2->SetFillColor( 'blue' );
	$p2->SetWidth( 1 );
	$p2->SetLegend( lang_get( 'legend_closed' ) );

	$p3 = new BarPlot( array_values( $p_metrics['resolved'] ) );
	$p3->SetFillColor( 'red' );
	$p3->SetWidth( 1 );
	$p3->SetLegend( lang_get( 'legend_resolved' ) );

	$gbplot = new GroupBarPlot( array( $p1, $p3, $p2 ) );
	$graph->Add( $gbplot );

	if( helper_show_queries() ) {
		$graph->subtitle->Set( db_count_queries() . ' queries (' . db_count_unique_queries() . ' unique) (' . db_time_queries() . 'sec)' );
		$graph->subtitle->SetFont( $t_graph_font, FS_NORMAL, 8 );
	}

	$graph->Stroke();
}

# --------------------
# Function that displays pie charts
function graph_pie( $p_metrics, $p_title = '', $p_graph_width = 500, $p_graph_height = 350, $p_center = 0.4, $p_poshorizontal = 0.10, $p_posvertical = 0.09 ) {

	$t_graph_font = graph_get_font();

	error_check( is_array( $p_metrics ) ? array_sum( $p_metrics ) : 0, $p_title );

	$graph = new PieGraph( $p_graph_width, $p_graph_height );
	$graph->img->SetMargin( 40, 40, 40, 100 );
	$graph->title->Set( $p_title );
	$graph->title->SetFont( $t_graph_font, FS_BOLD );

	$graph->SetMarginColor( 'white' );
	$graph->SetFrame( false );

	$graph->legend->Pos( $p_poshorizontal, $p_posvertical );
	$graph->legend->SetFont( $t_graph_font );

	$p1 = new PiePlot3d( array_values( $p_metrics ) );

	// should be reversed?
	$p1->SetTheme( 'earth' );

	# $p1->SetTheme("sand");
	$p1->SetCenter( $p_center );
	$p1->SetAngle( 60 );
	$p1->SetLegends( array_keys( $p_metrics ) );

	# Label format
	$p1->value->SetFormat( '%2.0f' );
	$p1->value->Show();
	$p1->value->SetFont( $t_graph_font );

	$graph->Add( $p1 );
	if( helper_show_queries() ) {
		$graph->subtitle->Set( db_count_queries() . ' queries (' . db_count_unique_queries() . ' unique) (' . db_time_queries() . 'sec)' );
		$graph->subtitle->SetFont( $t_graph_font, FS_NORMAL, 8 );
	}
	$graph->Stroke();
}

# --------------------
function graph_cumulative_bydate( $p_metrics, $p_graph_width = 300, $p_graph_height = 380 ) {

	$t_graph_font = graph_get_font();
	error_check( is_array( $p_metrics ) ? count( $p_metrics ) : 0, lang_get( 'cumulative' ) . ' ' . lang_get( 'by_date' ) );

	foreach( $p_metrics as $i => $vals ) {
		if( $i > 0 ) {
			$plot_date[] = $i;
			$reported_plot[] = $p_metrics[$i][0];
			$resolved_plot[] = $p_metrics[$i][1];
			$still_open_plot[] = $p_metrics[$i][2];
		}
	}

	$graph = new Graph( $p_graph_width, $p_graph_height );
	$graph->img->SetMargin( 40, 40, 40, 170 );
	if( ON == config_get_global( 'jpgraph_antialias' ) ) {
		$graph->img->SetAntiAliasing();
	}
	$graph->SetScale( 'linlin' );
	$graph->SetMarginColor( 'white' );
	$graph->SetFrame( false );
	$graph->title->Set( lang_get( 'cumulative' ) . ' ' . lang_get( 'by_date' ) );
	$graph->title->SetFont( $t_graph_font, FS_BOLD );

	$graph->legend->Pos( 0.05, 0.9, 'right', 'bottom' );
	$graph->legend->SetShadow( false );
	$graph->legend->SetFillColor( 'white' );
	$graph->legend->SetLayout( LEGEND_HOR );
	$graph->legend->SetFont( $t_graph_font );

	$graph->yaxis->scale->ticks->SetDirection( -1 );
	$graph->yaxis->SetFont( $t_graph_font );

	if( FF_FONT2 <= $t_graph_font ) {
		$graph->xaxis->SetLabelAngle( 60 );
	} else {
		$graph->xaxis->SetLabelAngle( 90 );

		# can't rotate non truetype fonts
	}
	$graph->xaxis->SetLabelFormatCallback( 'graph_date_format' );
	$graph->xaxis->SetFont( $t_graph_font );

	$p1 = new LinePlot( $reported_plot, $plot_date );
	$p1->SetColor( 'blue' );
	$p1->SetCenter();
	$p1->SetLegend( lang_get( 'legend_reported' ) );
	$graph->Add( $p1 );

	$p3 = new LinePlot( $still_open_plot, $plot_date );
	$p3->SetColor( 'red' );
	$p3->SetCenter();
	$p3->SetLegend( lang_get( 'legend_still_open' ) );
	$graph->Add( $p3 );

	$p2 = new LinePlot( $resolved_plot, $plot_date );
	$p2->SetColor( 'black' );
	$p2->SetCenter();
	$p2->SetLegend( lang_get( 'legend_resolved' ) );
	$graph->Add( $p2 );

	if( helper_show_queries() ) {
		$graph->subtitle->Set( db_count_queries() . ' queries (' . db_count_unique_queries() . ' unique) (' . db_time_queries() . 'sec)' );
		$graph->subtitle->SetFont( $t_graph_font, FS_NORMAL, 8 );
	}
	$graph->Stroke();
}

# --------------------
function graph_bydate( $p_metrics, $p_labels, $p_title, $p_graph_width = 300, $p_graph_height = 380 ) {

	$t_graph_font = graph_get_font();
	error_check( is_array( $p_metrics ) ? count( $p_metrics ) : 0, lang_get( 'by_date' ) );

	$graph = new Graph( $p_graph_width, $p_graph_height );
	$graph->img->SetMargin( 40, 140, 40, 100 );
	if( ON == config_get_global( 'jpgraph_antialias' ) ) {
		$graph->img->SetAntiAliasing();
	}
	$graph->SetScale( 'linlin' );
	$graph->SetMarginColor( 'white' );
	$graph->SetFrame( false );
	$graph->title->Set( $p_title . ' ' . lang_get( 'by_date' ) );
	$graph->title->SetFont( $t_graph_font, FS_BOLD );

	$graph->legend->Pos( 0.01, 0.05, 'right', 'top' );
	$graph->legend->SetShadow( false );
	$graph->legend->SetFillColor( 'white' );
	$graph->legend->SetLayout( LEGEND_VERT );
	$graph->legend->SetFont( $t_graph_font );

	$graph->yaxis->scale->ticks->SetDirection( -1 );
	$graph->yaxis->SetFont( $t_graph_font );
	$graph->yaxis->scale->SetAutoMin( 0 );

	if( FF_FONT2 <= $t_graph_font ) {
		$graph->xaxis->SetLabelAngle( 60 );
	} else {
		$graph->xaxis->SetLabelAngle( 90 );

		# can't rotate non truetype fonts
	}
	$graph->xaxis->SetLabelFormatCallback( 'graph_date_format' );
	$graph->xaxis->SetFont( $t_graph_font );

	$t_line_colours = config_get( 'graph_colors' );
	$t_count_colours = count( $t_line_colours );
	$t_lines = count( $p_metrics ) - 1;
	$t_line = array();
	for( $i = 1;$i <= $t_lines;$i++ ) {
		$t_line[$i] = new LinePlot( $p_metrics[$i], $p_metrics[0] );
		$t_line[$i]->SetColor( $t_line_colours[$i % $t_count_colours] );
		$t_line[$i]->SetCenter();
		$t_line[$i]->SetLegend( $p_labels[$i] );
		$graph->Add( $t_line[$i] );
	}

	if( helper_show_queries() ) {
		$graph->subtitle->Set( db_count_queries() . ' queries (' . db_count_unique_queries() . ' unique) (' . db_time_queries() . 'sec)' );
		$graph->subtitle->SetFont( $t_graph_font, FS_NORMAL, 8 );
	}
	$graph->Stroke();
}

# --------------------
# utilities
# --------------------
function graph_total_metrics( $p_metrics ) {
	foreach( $p_metrics['open'] as $t_enum => $t_value ) {
		$total[$t_enum] = $t_value + $p_metrics['resolved'][$t_enum] + $p_metrics['closed'][$t_enum];
	}
	return $total;
}

# --------------------
# Data Extractions
# --------------------
# --------------------
# summarize metrics by a single field in the bug table
function create_bug_enum_summary( $p_enum_string, $p_enum ) {
	$t_project_id = helper_get_current_project();
	$t_bug_table = db_get_table( 'mantis_bug_table' );
	$t_user_id = auth_get_current_user_id();
	$specific_where = " AND " . helper_project_specific_where( $t_project_id, $t_user_id );

	$t_metrics = array();
	$t_assoc_array = MantisEnum::getAssocArrayIndexedByValues( $p_enum_string );

	foreach ( $t_assoc_array as $t_value => $t_label  ) {
		$query = "SELECT COUNT(*)
					FROM $t_bug_table
					WHERE $p_enum='$t_value' $specific_where";
		$result = db_query( $query );
		$t_metrics[$t_label] = db_result( $result, 0 );
	}

	return $t_metrics;
}

# Function which gives the absolute values according to the status (opened/closed/resolved)
function enum_bug_group( $p_enum_string, $p_enum ) {
	$t_bug_table = db_get_table( 'mantis_bug_table' );

	$t_project_id = helper_get_current_project();
	$t_bug_table = db_get_table( 'mantis_bug_table' );
	$t_user_id = auth_get_current_user_id();
	$t_res_val = config_get( 'bug_resolved_status_threshold' );
	$t_clo_val = CLOSED;
	$specific_where = " AND " . helper_project_specific_where( $t_project_id, $t_user_id );

	$t_array_indexed_by_enum_values = MantisEnum::getAssocArrayIndexedByValues( $p_enum_string );
	$enum_count = count( $t_array_indexed_by_enum_values );
	foreach ( $t_array_indexed_by_enum_values as $t_value => $t_label ) {
		# Calculates the number of bugs opened and puts the results in a table
		$query = "SELECT COUNT(*)
					FROM $t_bug_table
					WHERE $p_enum='$t_value' AND
						status<'$t_res_val' $specific_where";
		$result2 = db_query( $query );
		$t_metrics['open'][$t_label] = db_result( $result2, 0, 0 );

		# Calculates the number of bugs closed and puts the results in a table
		$query = "SELECT COUNT(*)
					FROM $t_bug_table
					WHERE $p_enum='$t_value' AND
						status='$t_clo_val' $specific_where";
		$result2 = db_query( $query );
		$t_metrics['closed'][$t_label] = db_result( $result2, 0, 0 );

		# Calculates the number of bugs resolved and puts the results in a table
		$query = "SELECT COUNT(*)
					FROM $t_bug_table
					WHERE $p_enum='$t_value' AND
						status>='$t_res_val'  AND
						status<'$t_clo_val' $specific_where";
		$result2 = db_query( $query );
		$t_metrics['resolved'][$t_label] = db_result( $result2, 0, 0 );
	}

	# ## end for

	return $t_metrics;
}

# --------------------
function create_developer_summary() {

	$t_project_id = helper_get_current_project();
	$t_user_table = db_get_table( 'mantis_user_table' );
	$t_bug_table = db_get_table( 'mantis_bug_table' );
	$t_user_id = auth_get_current_user_id();
	$specific_where = " AND " . helper_project_specific_where( $t_project_id, $t_user_id );

	$t_res_val = config_get( 'bug_resolved_status_threshold' );
	$t_clo_val = CLOSED;

	$query = "SELECT handler_id, status
				 FROM $t_bug_table
				 WHERE handler_id != '' $specific_where";
	$result = db_query( $query );
	$t_total_handled = db_num_rows( $result );

	$t_handler_arr = array();
	for( $i = 0;$i < $t_total_handled;$i++ ) {
		$row = db_fetch_array( $result );
		if( !isset( $t_handler_arr[$row['handler_id']] ) ) {
			$t_handler_arr[$row['handler_id']]['res'] = 0;
			$t_handler_arr[$row['handler_id']]['open'] = 0;
			$t_handler_arr[$row['handler_id']]['close'] = 0;
		}
		if( $row['status'] >= $t_res_val ) {
			if( $row['status'] >= $t_clo_val ) {
				$t_handler_arr[$row['handler_id']]['close']++;
			} else {
				$t_handler_arr[$row['handler_id']]['res']++;
			}
		} else {
			$t_handler_arr[$row['handler_id']]['open']++;
		}
	}

	if( count( $t_handler_arr ) == 0 ) {
		return array( 'open' => array() );
	}

	$t_imploded_handlers = implode( ',', array_keys( $t_handler_arr ) );
	$query = "SELECT id, username
				FROM $t_user_table
				WHERE id IN ($t_imploded_handlers)
				ORDER BY username";
	$result = db_query_bound( $query );
	$user_count = db_num_rows( $result );

	for( $i = 0;$i < $user_count;$i++ ) {
		$t_row = db_fetch_array( $result );

		$v_id = $t_row['id'];
		$v_username = $t_row['username'];

		$t_metrics['open'][$v_username] = $t_handler_arr[$v_id]['open'];
		$t_metrics['resolved'][$v_username] = $t_handler_arr[$v_id]['res'];
		$t_metrics['closed'][$v_username] = $t_handler_arr[$v_id]['close'];
	}

	# end for
	return $t_metrics;
}

# --------------------
function create_reporter_summary() {
	global $reporter_name, $reporter_count;


	$t_project_id = helper_get_current_project();
	$t_user_table = db_get_table( 'mantis_user_table' );
	$t_bug_table = db_get_table( 'mantis_bug_table' );
	$t_user_id = auth_get_current_user_id();
	$specific_where = " AND " . helper_project_specific_where( $t_project_id, $t_user_id );

	$query = "SELECT reporter_id
				 FROM $t_bug_table
				 WHERE id != '' $specific_where";
	$result = db_query( $query );
	$t_total_reported = db_num_rows( $result );

	$t_reporter_arr = array();
	for( $i = 0;$i < $t_total_reported;$i++ ) {
		$row = db_fetch_array( $result );

		if( isset( $t_reporter_arr[$row['reporter_id']] ) ) {
			$t_reporter_arr[$row['reporter_id']]++;
		} else {
			$t_reporter_arr[$row['reporter_id']] = 1;
		}
	}

	if( count( $t_reporter_arr ) == 0 ) {
		return array();
	}

	$t_imploded_reporters = implode( ',', array_keys( $t_reporter_arr ) );
	$query = "SELECT id, username
				FROM $t_user_table
				WHERE id IN ($t_imploded_reporters)
				ORDER BY username";
	$result = db_query_bound( $query );
	$user_count = db_num_rows( $result );

	for( $i = 0;$i < $user_count;$i++ ) {
		$t_row = db_fetch_array( $result );
		
		$v_username = $t_row['username'];
		$v_id = $t_row['id'];

		$t_metrics[$v_username] = $t_reporter_arr[$v_id];
	}

	# end for
	return $t_metrics;
}

# --------------------
function create_category_summary() {
	global $category_name, $category_bug_count;

	$t_project_id = helper_get_current_project();
	$t_cat_table = db_get_table( 'mantis_category_table' );
	$t_bug_table = db_get_table( 'mantis_bug_table' );
	$t_user_id = auth_get_current_user_id();
	$specific_where = helper_project_specific_where( $t_project_id, $t_user_id );

	$query = "SELECT id, name
				FROM $t_cat_table
				WHERE $specific_where
				ORDER BY name";
	$result = db_query( $query );
	$category_count = db_num_rows( $result );

	$t_metrics = array();
	for( $i = 0;$i < $category_count;$i++ ) {
		$row = db_fetch_array( $result );
		$t_cat_name = $row['name'];
		$t_cat_id = $row['id'];
		$query = "SELECT COUNT(*)
					FROM $t_bug_table
					WHERE category_id='$t_cat_id' AND $specific_where";
		$result2 = db_query( $query );
		$t_metrics[$t_cat_name] = $t_metrics[$t_cat_name] + db_result( $result2, 0, 0 );
	}

	# end for
	return $t_metrics;
}

# --------------------
function cmp_dates( $a, $b ) {
	if( $a[0] == $b[0] ) {
		return 0;
	}
	return( $a[0] < $b[0] ) ? -1 : 1;
}

# --------------------
function find_date_in_metrics( $aDate ) {
	global $metrics;
	$index = -1;
	for( $i = 0;$i < count( $metrics );$i++ ) {
		if( $aDate == $metrics[$i][0] ) {
			$index = $i;
			break;
		}
	}
	return $index;
}

# --------------------
function create_cumulative_bydate() {

	$t_clo_val = CLOSED;
	$t_res_val = config_get( 'bug_resolved_status_threshold' );
	$t_bug_table = db_get_table( 'mantis_bug_table' );
	$t_history_table = db_get_table( 'mantis_bug_history_table' );

	$t_project_id = helper_get_current_project();
	$t_user_id = auth_get_current_user_id();
	$specific_where = helper_project_specific_where( $t_project_id, $t_user_id );

	# Get all the submitted dates
	$query = "SELECT date_submitted
				FROM $t_bug_table
				WHERE $specific_where
				ORDER BY date_submitted";
	$result = db_query( $query );
	$bug_count = db_num_rows( $result );

	for( $i = 0;$i < $bug_count;$i++ ) {
		$row = db_fetch_array( $result );

		# rationalise the timestamp to a day to reduce the amount of data
		$t_date = db_unixtimestamp( $row['date_submitted'] );
		$t_date = (int)( $t_date / 86400 );

		if( isset( $metrics[$t_date] ) ) {
			$metrics[$t_date][0]++;
		} else {
			$metrics[$t_date] = array( 1, 0, 0, );
		}
	}

	# ## Get all the dates where a transition from not resolved to resolved may have happened
	#    also, get the last updated date for the bug as this may be all the information we have
	$query = "SELECT $t_bug_table.id, last_updated, date_modified, new_value, old_value
			FROM $t_bug_table LEFT JOIN $t_history_table
			ON $t_bug_table.id = $t_history_table.bug_id
			WHERE $specific_where
						AND $t_bug_table.status >= '$t_res_val'
						AND ( ( $t_history_table.new_value >= '$t_res_val'
								AND $t_history_table.field_name = 'status' )
						OR $t_history_table.id is NULL )
			ORDER BY $t_bug_table.id, date_modified ASC";
	$result = db_query( $query );
	$bug_count = db_num_rows( $result );

	$t_last_id = 0;
	$t_last_date = 0;

	for( $i = 0;$i < $bug_count;$i++ ) {
		$row = db_fetch_array( $result );
		$t_id = $row['id'];

		# if h_last_updated is NULL, there were no appropriate history records
		#  (i.e. pre 0.18 data), use last_updated from bug table instead
		if( NULL == $row['date_modified'] ) {
			$t_date = db_unixtimestamp( $row['last_updated'] );
		} else {
			if( $t_res_val > $row['old_value'] ) {
				$t_date = db_unixtimestamp( $row['date_modified'] );
			}
		}
		if( $t_id <> $t_last_id ) {
			if( 0 <> $t_last_id ) {

				# rationalise the timestamp to a day to reduce the amount of data
				$t_date_index = (int)( $t_last_date / 86400 );

				if( isset( $metrics[$t_date_index] ) ) {
					$metrics[$t_date_index][1]++;
				} else {
					$metrics[$t_date_index] = array(
						0,
						1,
						0,
					);
				}
			}
			$t_last_id = $t_id;
		}
		$t_last_date = $t_date;
	}

	ksort( $metrics );

	$metrics_count = count( $metrics );
	$t_last_opened = 0;
	$t_last_resolved = 0;
	foreach( $metrics as $i => $vals ) {
		$t_date = $i * 86400;
		$t_metrics[$t_date][0] = $t_last_opened = $metrics[$i][0] + $t_last_opened;
		$t_metrics[$t_date][1] = $t_last_resolved = $metrics[$i][1] + $t_last_resolved;
		$t_metrics[$t_date][2] = $t_metrics[$t_date][0] - $t_metrics[$t_date][1];
	}
	return $t_metrics;
}

function graph_date_format( $p_date ) {
	return date( config_get( 'short_date_format' ), $p_date );
}

# ----------------------------------------------------
#
# Check that there is enough data to create graph
#
# ----------------------------------------------------
function error_check( $bug_count, $title ) {

	if( 0 == $bug_count ) {
		$t_graph_font = graph_get_font();

		$graph = new CanvasGraph( 300, 380 );

		$txt = new Text( lang_get( 'not_enough_data' ), 150, 100 );
		$txt->Align( "center", "center", "center" );
		$txt->SetFont( $t_graph_font, FS_BOLD );
		$graph->title->Set( $title );
		$graph->title->SetFont( $t_graph_font, FS_BOLD );
		$graph->AddText( $txt );
		$graph->Stroke();
		die();
	}
}
