#!/usr/bin/perl -T

# This is a library for generating the Captrap main page.

# Copyright 2009 Corey Hickey


# This file is part of Captrap.
#
# Captrap 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 3 of the License, or
# (at your option) any later version.
#
# Captrap 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 Captrap.  If not, see <http://www.gnu.org/licenses/>.

=head1 NAME

Captrap::Main - a library of functions used by Captrap's main page generation
programs.

=head1 SYNOPSIS

use Captrap::Main qw(:mainpage :param :misc);

=head1 DESCRIPTION

As of yet, this library is not intended to be used outside of Captrap; few of
the included functions can be used in a self-contained manner. This text is
intended as a quick reference for use in Captrap development.

No functions are exported by default.

=head2 Tags

The following groups of functions are available; contents may change in future
versions.

=over

=item :param

mk_mainpage, add_now

=item :param

mk_param_info

=item :misc

new_graph_string

=back

=head2 Functions

=over

=cut


use strict;
use warnings FATAL => 'all';

package Captrap::Main;
require Exporter;
our @ISA = ('Exporter');
our @EXPORT = ();
our %EXPORT_TAGS = (
  mainpage => [ qw(
    mk_mainpage
    add_now
  ) ],
  misc => [ qw(
    new_graph_string
  ) ],
  param => [ qw(
    mk_param_info
  ) ],
);
# all the tagged functions
our @EXPORT_OK = map({ @$_ } values(%EXPORT_TAGS));

# for development using a different Captrap module
use lib "lib";
use Captrap qw(:db :misc :cgi);
use Captrap::Graph;
use Captrap::View;


# -----------------------------------------------------------------------------
# main page generation
# ----------------------------------------------------------------------------

=item mk_mainpage($common, $params, $extension)

Generate the Captrap main page and return the contents. $extension is a scalar
reference; the referent will be set to the suggested filename extension if the
output were to be written to a file.

=cut

sub mk_mainpage {
  my $common = shift;
  my $params = shift;
  my $extension = shift; # scalar ref
  my $cgi = $common->{cgi};
  my $viewer = $common->{config}->{viewer};
  my $grapher = $common->{config}->{grapher};
  my $now = $params->{now}; # likely undef
  if (defined($now)) {
    $now = floor_unit("hour", $now);
  }
  my $static = defined($common->{recur});
  $$extension = "html";
  my $text;
  $text .= cgi_start_xhtml($cgi, 'Captrap main');
  $text .= $cgi->h1('Captrap Main Page');
  $text .= $cgi->p("Captrap is a program for displaying network data " .
      "transfer information. Different sets of data (mostly graphs) are " .
      "divided into separate 'views'. Each view can be set to show " .
      "information about bytes or packets. To get started, try clicking the " .
      "link for 'Basic Information' or 'Interval Graphs', below.");
  my $times = get_times($common);
  if ($static) {
    $text .= mk_static_text($cgi, $times, $now);
  }
  my $thing;

  $text .= $cgi->h4('Select Individual Views To Display');
  $text .= $cgi->p("To select a single view, click the name of the view (to " .
      "view bytes) or the 'P' (to view packets). To select a combination, " .
      "click the corresponding checkboxes and click the submit button.");
  $text .= $cgi->start_form(-method => "get", -id => "select_view");
  $text .= $cgi->p(mk_views_list($common, $now));
  $thing = $cgi->popup_menu('item',  [ qw(bytes packets) ]);
  $text .= $cgi->p("$thing Choose item to be viewed.");
  $thing = $cgi->popup_menu('patterns',  [ qw(0 1) ]);
  $text .= $cgi->p("$thing Choose 0 or 1 to disable or enabled patterned bars " .
      "(useful for the colorblind).");
  $text .= $cgi->p(mk_time_select($cgi, "now", undef));
  $text .= $cgi->p(mk_submit_button($cgi, $static,
      { -name => "select_views", -value => "submit" }));
  $text .= $cgi->end_form();
  $text .= $cgi->hr();
  # useful views
  $text .= $cgi->h4('Some Useful Combinations of Views');
  $text .= $cgi->p("These can be manually selected above, but having a few " .
      "presets is convenient.");
  my $link;
  $link = mk_href($common, $viewer,
      { views => [ "basic,graph_inter,graph_adv" ], now => [ $now ] },
      "Basic Information and Graphs of Totals");
  $text .= $cgi->p($link);
  $link = mk_href($common, $viewer, { views =>
      [ "basic,graph_inter_updown,graph_adv_updown" ], now => [ $now ] },
      "Basic Information and Graphs of Upload/Download Data");
  $text .= $cgi->p($link);
  $link = mk_href($common, $viewer, { views => [ "all" ], now => [ $now ] },
      "All Views");
  $text .= $cgi->p("$link (slow, but useful for debug)");
  $text .= $cgi->hr();
  # custom graph
  $text .= $cgi->h4('Custom Graph Selection');
  $link = mk_href($common, $grapher, {}, "list of parameters");
  $text .= $cgi->p("Creating a custom 'mt' or 'mtpred' graph can be tricky; " .
      "you may have an easier time modifying the parameters in the URL of " .
      "an existing graph. For reference, see the $link.");
  $text .= $cgi->start_form(-method => "get", -id => "select_graph");
  $thing = $cgi->popup_menu('graph',  [ qw(per mt mtpred) ]);
  $text .= $cgi->p("$thing Choose graph type.");
  $thing = $cgi->popup_menu('item',  [ qw(bytes packets) ]);
  $text .= $cgi->p("$thing Choose item to be graphed.");
  $thing = $cgi->popup_menu('per',  [ qw(year month day hour) ], 'hour');
  $text .= $cgi->p("$thing Choose interval unit (as in \"bytes per ---\").");
  $text .= $cgi->p(mk_time_select($cgi, "start", $times->{lastday}));
  $text .= $cgi->p(mk_time_select($cgi, "end", $times->{now}));
  $thing = $cgi->textfield(-name => "w", -size => "5");
  $thing .= 'x' . $cgi->textfield(-name => "h", -size => "5");
  $text .= $cgi->p("$thing Choose image width and height (in pixels; " .
      "800x600 default).");
  $thing = $cgi->checkbox_group(-name => 'states',
      -values => $common->{config}->{states}, -linebreak => 0);
  $text .= $cgi->p("Select traffic states to view (check none to view total):" .
      $cgi->br . $thing);
  $thing = $cgi->textfield(-name => "caps", -size => "80");
  $text .= $cgi->p("Comma-separated list of caps to display (one for each " .
      "state)." . $cgi->br . $thing);
  $thing = $cgi->textfield(-name => "sum_interval", -size => "5");
  $text .= $cgi->p("$thing Choose summing interval" .
      " (only used by the 'mt' graph)");
  $thing = $cgi->popup_menu('sum_unit',  [ qw(year month day hour) ]);
  $text .= $cgi->p("$thing Choose summing unit (only used by the 'mt' graph)");
  $thing = $cgi->textfield(-name => "pred", -size => "80");
  $text .= $cgi->p("List of \"number:name\" pairs for the \'mtpred\' graph." .
      " Multiple pairs are separated by a ',' (example:" .
      " '8689718561:last_hour,10038542471:last_day')" .
      $cgi->br . $thing);
  $thing = $cgi->popup_menu('output',  [ qw(png svg gnuplot text csv csvraw) ]);
  $text .= $cgi->p("$thing Choose data output format.");
  $text .= $cgi->p(mk_submit_button($cgi, $static,
      { -name => "select_graph", -value => "submit" }));
  $text .= $cgi->end_form();
  $text .= $cgi->end_html();
  return $text;
}


# make a "Static Page" notice
sub mk_static_text {
  my $cgi = shift;
  my $times = shift;
  my $now = shift; # may be undef
  my $text = $cgi->h4({"-style" => "Color: red;"}, "Static Page");
  my $desc = "on " . print_time($times->{now});
  if (defined($now)) {
    $desc = "using data from " . print_time($now);
  }
  $text .= $cgi->p("This is a static page generated $desc. Submit buttons" .
      " are disabled.");
  return $text;
}


# make a possibly disabled submit button
sub mk_submit_button {
  my $cgi = shift;
  my $disabled = shift;
  my $params = shift; # hash ref
  my $text = "";
  if ($disabled) {
    $params->{"-disabled"} = "disabled";
    $text = "Submit button disabled in static mode.";
  }
  return $cgi->submit($params) . $text;
}


# make a checkbox list of views
sub mk_views_list {
  my $common = shift;
  my $now = shift;
  my $cgi = $common->{cgi};
  my $viewer = $common->{config}->{viewer};
  my @list;
  my $views = Captrap::View::mk_view_info();
  # We want each checkbox label to be a link, but $cgi->checkbox_group()
  # insists on escaping HTML. So, we roll our own checkbox group. CGI's
  # method required lots of secondary data structures anyway.
  while (my ($name, $view) = each %$views) {
    my $params = { views => [ $name ], now => [ $now ], item => [ "bytes" ] };
    my $link = mk_href($common, $viewer, $params, $view->{ttl});
    my $input = $cgi->input({type => "checkbox", name => "view",
        value => $name});
    # now make a link for viewing packets
    $params->{item} = [ "packets" ];
    my $linkp = mk_href($common, $viewer, $params, "P");
    push(@list, "$input $linkp $link");
  }
  return join($cgi->br(), @list);
}


=item add_now($now)

If the argument $now is defined, returns a string suitable for the "now=" CGI
parameter.

=cut

sub add_now {
  my $now = shift;
  if (defined($now)) {
    $now =~ s/ /T/;
    return "&now=$now";
  }
  return "";
}

# -----------------------------------------------------------------------------
# param info
# -----------------------------------------------------------------------------


=item mk_param_info($config)

Set up a hash of all parameters the generator accepts, along with their defaults.

=cut

# each parameter MUST have a key here
sub mk_param_info {
  my $config = shift;
  # load graph parameters and then override them
  my $graph_params = Captrap::Graph::mk_param_info($config);
  # make all defaults undef to avoid URL clutter;
  # grapher can use its own defaults
  foreach my $param (keys %$graph_params) {
    $graph_params->{$param}->{def} = undef;
  }
  $graph_params->{start}->{reg} = $graph_params->{end}->{reg} =
      time_re("second", " ");
  $graph_params->{states}->{var} = 'a';
  my $view_info = Captrap::View::mk_view_info();
  my $views = '^(' . join('|', keys(%$view_info)) . ')$';
  my $info = mk_ixhash();
  # don't forget to start regexes with a ^ and end with a $
  %$info = (
    select_views => {
      def => undef,
      reg => qr/^submit$/,
      txt => "submit button for selecting views",
      var => 's',
    },
    view => {
      def => [],
      reg => qr/$views/,
      txt => "a selected view",
      var => 'a',
    },
    item  => {
      def => "bytes",
      reg => qr/^(bytes|packets)$/,
      txt => "Item to be viewed ('bytes' or 'packets').",
      var => 's',
    },
    patterns => {
      def => undef,
      reg => qr/^[01]$/,
      txt => "Set to 1 to draw graphs with patterned bars, or 0 to\n" .
          "disable. See the \"patterns_default\" configuration parameter.",
      var => 's',
    },
    now => {
      def => undef,
      reg => time_re("second"),
      txt => "Current time. This will be rounded down to the nearest hour " .
          "and used as a maximum time for data retrieval.",
      var => 's',
    },
    select_graph => {
      def => undef,
      reg => qr/^submit$/,
      txt => "submit button for selecting graph parameters",
      var => 's',
    },
    %$graph_params,
  );
  return $info;
}

# -----------------------------------------------------------------------------
# misc.
# -----------------------------------------------------------------------------

# make a cgi textfield for entering a time value
sub mk_time_field {
  my $cgi = shift;
  my $name = shift;
  my $time = shift;
  $time =~ s/T/ / if defined($time);
  my $length = length("YYYY-MM-DD hh:mm:ss");
  return $cgi->textfield(-name => $name, -value => $time, -size => $length,
      -maxlength => $length);
}


# make a cgi textfield and explanation for entering a time value
sub mk_time_select {
  my $cgi = shift;
  my $name = shift;
  my $time = shift;
  my $field = mk_time_field($cgi, $name, $time);
  if ($name eq "now") {
    return "$field Specify current time (24-hour time, as " .
        "\"YYYY-MM-DD hh:mm:ss\").";
  }
  return "$field Choose $name time (24-hour time, as \"YYYY-MM-DD hh:mm:ss\").";
}


=item mk_graph_string($config, $params)

Make a string of CGI options for calling the grapher; $params is reference to a
hash of 'option_name => value' pairs.

=cut

sub new_graph_string {
  my $config = shift;
  my $params = shift;
  my @new;
  my $param_info = Captrap::Graph::mk_param_info($config);
  foreach my $param (keys %$param_info) {
    if (defined($params->{$param})) {
      push(@new, "$param=$params->{$param}");
    }
  }
  return join('&', @new);
}


1;

=back

=head1 AUTHOR

Corey Hickey <bugfood-c@fatooh.org>

This library is free software; you may redistribute and/or modify it under the
terms of the GNU General Public License, version 3. See the source file for the
usual GPL preamble and the COPYING file for a copy of the GPL.

=head1 SEE ALSO

Captrap, Captrap::Graph, Captrap::View, captrap_main, main.pl

=cut
