=head1 NAME

  SimpleGraph - easy creation of simple charts.

=head1 SYNOPSIS

 use SimpleGraph;

 $s = new SimpleGraph;
 $s->data( ['jan','feb','mar','apr','may'], [10,12,15,23,14], [7,5,6,9,4] );
 $s->write_graph('foo/bar/goofy.png');
 
 $s->print_graph();
 $s->labels( ['Acme Inc.','sales for 1999','months','sales in millions'] );
 $s->legend( ['gnutty bars','gnawrola bars'] );

=head1 DESCRIPTION

 SimpleGraph is a simple wrapper around the Chart::Lines object. Basically 
 it sets a bunch of the Chart objects defaults for you, so you can simply 
 and easily pass the SimpleGraph object just the bare data you need drawn.

=head1 METHODS

 data( [ data_set1 ], [ data_set2 ], ... )
 data can be passed just one data set or multiple data sets, as array
 references.

 If multiple data sets are passed the first data set needs to be
 the data points plotted on the x-axis against which the other data sets are
 plotted. For example in the above example the months is the first data set
 and it will plotted on the x-axis. The other data sets, ( the sales 
 figures )  will be plotted against the months.

 If only one data set is passed, it is plotted against a straight number
 ordering, from 1 till the number of elements in the data set.

 labels( ['Title', 'Sub Title', 'X label', 'Y label'] )
 labels sets the above 4 labels on the graph. Optional.

 legend( ['data_set_1_name','data_set_2_name'], ... )
 legend sets the legend for the graph. It is passed the names of the data 
 sets being graphed. Optional.

 write_graph('filename.png');
 will write out the graph as a png file. If filename not specified will 
 write to foobar.png

 print_graph()
 will send print the image with a 'Content-type: image/png' header, so it 
 can be used in a cgi enviornment to send image to the browser.

=cut

#
# SimpleGraph is a simple wrapper around the Chart::Lines module.
# It allows you to chart data points easily and quickly without having to
# bother with the extra niceties of the Chart::Lines module.
#
package SimpleGraph;
$SimpleGraph::VERSION = '0.9';

use Chart::Lines;
use strict;

$SimpleGraph::ARBITRARY_X_SCALING = 40;

sub new {
    my $type = shift;
    my $class = ref($type) || $type;
    my $self = {};

    # default graph is an 800 x 600 png.
    my ($x_res, $y_res) = @_;
    $x_res ||= 400;
    $y_res ||= 300;

    # has-a Chart::Lines object.
    $self->{'object'} = Chart::Lines->new($x_res,$y_res);

    bless $self, $class;
    $self->_initialize_defaults;
    return $self;
}

#
# sub labels sets the following four things.
#
#    title        => 'Site Accesses for Business Cobrands', # title of graph [empty]
#    sub_title    => 'June 1999',        # sub title below title [empty]
#    x_label      => 'Date',             # x axis label [empty]
#    y_label      => 'No. of Accesses',  # y axis label [empty]
#
sub labels {
    my $self   = shift;
    my $labels = shift;
    my ($title, $sub_title, $x_label, $y_label) = @{$labels};
    $self->{'config_hash'}->{'title'} = $title;
    $self->{'config_hash'}->{'sub_title'} = $sub_title;
    $self->{'config_hash'}->{'x_label'} = $x_label;
    $self->{'config_hash'}->{'y_label'} = $y_label;
}

#
# reference to an array of labels.
#
sub legend {
    my $self = shift;
    my $legend_labels = shift;
    $self->{'config_hash'}->{'legend_labels'} = $legend_labels;
}

sub _initialize_defaults {
    my $self = shift;
    $self->{'config_hash'} =
      {
       # relatively constant configurations that you really dont want to change.
       gif_border   => '20',               # between graph and edges of gif. [10]
       graph_border => '10',               # between title/labels and graph [10]
       text_space   => '2',                # between sides of text [2]
       tick_len     => '4',                # length of ticks sticking out of the axis [4]
       stagger_x_labels => 'true',         # staggers x labels for readability [true]
       y_ticks      => '10',               # no. of y-ticks to draw [5]
       max_val      => undef,              # cancels autoscale and sets max val [undef]
       pt_size      => '4',
       dashed_lines => undef,              # [undef] any other value draws dashed lines
       custom_x_ticks => undef,            # exactly which x ticks to print \@ticks
       # colors       => [[255,0,0],[229,125,237]]  # [undef] selects colors for you.
       transparent  => 'false',            # [false]
       grid_lines   => 'true',             # [undef]
       legend       => 'bottom',            # [right]
      };

    # If no data is specified print this bogus information.
    my @data = ( ['foo','bar'],[1,2] );
    $self->{'data'} = \@data;

    # set labels
    my @labels = ['default label and data'];
    $self->{'labels'} = \@labels;
}



#
# data is passed a list of array refences. Where the first array ref conatins x labels
# data.
#
# my @data = ( ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'],
#  [30,40,50,60,70,60,80,30,90,100,],
#  [20,30,10,12,13,49,30]);
#
#
sub data {
    my $self = shift;
    my @data = @_;

    if ($self->{'object'}->{'data'}) { # re-initialize the Chart object.
        $self->{'object'} = Chart::Lines->new(800,600);
    }

    my $data_sets = scalar @data;
    if( $data_sets == 1 ) {
        my $num_items_in_data_set = scalar @{ $data[0] };
        my @num_items;
        if ($num_items_in_data_set > $SimpleGraph::ARBITRARY_X_SCALING ) {
            my ($x_scale,$custom_x_ticks) = &_autoscale_x($num_items_in_data_set);
            @num_items = @{ $x_scale };
            $self->{'config_hash'}->{'custom_x_ticks'} = $custom_x_ticks;
        } else {
            @num_items = ( 1 .. $num_items_in_data_set );
        }

        @data = (\@num_items, $data[0]);
    }
    $self->{'data'} = \@data;
}

sub _autoscale_x {
    my ($num_items_in_data_set) = @_;
    my @num_items;
    my @custom_ticks;

    my $highest_value = substr($num_items_in_data_set,0,1);
    my $decimal_positions = length (substr($num_items_in_data_set,1));
    my $range = ($highest_value + 1) . ('0' x $decimal_positions);

    my $factor = $range / 10;

    for( my $i=0; $i<=$num_items_in_data_set; $i++) {
        if ($i%$factor == 0) {
            push @num_items,$i;
            push @custom_ticks,($i);
        } else {
            push @num_items, '';
        }
    }
    return (\@num_items,\@custom_ticks);
}

#
# write to a file, if filename not specified write to filename foobar.png
#
sub write_graph {
    my $self     = shift;
    my $filename = shift;
    ($filename) ? $filename : ($filename = 'foobar.png');
    if (-e "$filename") { open(FH,">$filename");close(FH) };

    # Get the parameters for the Chart object.
    my $obj  =    $self->{'object'};
    my %hash = %{ $self->{'config_hash'} };
    my $data =    $self->{'data'};

    # and use Charts methods.
    $obj->set(%hash);
    $obj->png("$filename", $data);
}

#
# this method incase you want to send the output to a webpage.
#
sub print_graph {
    my $self = shift;

    my $obj  =    $self->{'object'};
    my %hash = %{ $self->{'config_hash'} };
    my $data =    $self->{'data'};

    $obj->set(%hash);
    print $obj->cgi_png($data);
}

=head1 PREREQUISITES

Chart::Lines.pm Version 0.99c or greater.

=head1 SEE ALSO

Chart::Lines.pm

=head1 AUTHOR

Mehryar Mansoor E<lt>mehryar@mehryar.comE<gt>

=head1 VERSION

$Revision: 0.9 $

=head1 COPYRIGHT

Copyright (c) 1999 by Mehryar Mansoor.
All rights reserved.  This program is free software;
you can redistribute it and/or modify it under the same
terms as Perl itself.       

=head1 KEYWORDS

chart, graph, plot

=cut

1;
