######################################################################
#
# $Id: MiaRoutines.pm,v 1.8 2012/01/07 07:56:13 mavrik Exp $
#
######################################################################
#
# Copyright 2007-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: MIA-related routines
#
######################################################################

package WebJob::MiaRoutines;

require Exporter;

use 5.008;
use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

@EXPORT = qw(MiaComputeMiaStatus MiaNewContext);
@EXPORT_OK = ();
@ISA = qw(Exporter);
$VERSION = do { my @r = (q$Revision: 1.8 $ =~ /(\d+)/g); sprintf("%d."."%03d" x $#r, @r); };

######################################################################
#
# MiaComputeMiaStatus
#
######################################################################

sub MiaComputeMiaStatus
{
  my ($phMiaContext, $psError) = @_;

  ####################################################################
  #
  # Make sure that required inputs are defined. If the caller didn't
  # enable strict mode, automatically initialize undefined variables
  # with their default values.
  #
  ####################################################################

  if (!defined($phMiaContext) || !defined($$phMiaContext{'Client'}))
  {
    $$psError = "Unable to proceed due to missing or undefined inputs." if (defined($psError));
    return undef;
  }

  if ($$phMiaContext{'Strict'})
  {
    my @aKeys =
    (
      'Checkpoint',
      'Limit',
      'Multiplier',
      'Period',
      'Time',
    );
    if (!defined(MiaVerifyHashKeys($phMiaContext, \@aKeys)))
    {
      $$psError = "Unable to proceed in strict mode for $$phMiaContext{'Client'} due to missing or undefined inputs." if (defined($psError));
      return undef;
    }
  }
  else
  {
    if (!defined($$phMiaContext{'Checkpoint'}))
    {
      $$phMiaContext{'Checkpoint'} = MiaGetDefaultCheckpoint();
    }
    if (!defined($$phMiaContext{'Limit'}))
    {
      $$phMiaContext{'Limit'} = MiaGetDefaultLimit();
    }
    if (!defined($$phMiaContext{'Multiplier'}))
    {
      $$phMiaContext{'Multiplier'} = MiaGetDefaultMultiplier();
    }
    if (!defined($$phMiaContext{'Period'}))
    {
      $$phMiaContext{'Period'} = MiaGetDefaultPeriod();
    }
    if (!defined($$phMiaContext{'Time'}))
    {
      $$phMiaContext{'Time'} = MiaGetDefaultTime();
    }
  }

  ####################################################################
  #
  # Validate inputs.
  #
  ####################################################################

  if ($$phMiaContext{'Checkpoint'} !~ /^\d+$/)
  {
    $$psError = "Checkpoint does not pass muster for $$phMiaContext{'Client'}." if (defined($psError));
    return undef;
  }

  if ($$phMiaContext{'Limit'} !~ /^\d+$/)
  {
    $$psError = "Limit does not pass muster for $$phMiaContext{'Client'}." if (defined($psError));
    return undef;
  }

  if ($$phMiaContext{'Multiplier'} !~ /^(?:\d+\.?|\.\d+|\d+(?:\.\d+)?)$/)
  {
    $$psError = "Multiplier does not pass muster for $$phMiaContext{'Client'}." if (defined($psError));
    return undef;
  }

  if ($$phMiaContext{'Period'} !~ /^\d+$/)
  {
    $$psError = "Period does not pass muster for $$phMiaContext{'Client'}." if (defined($psError));
    return undef;
  }

  if ($$phMiaContext{'Time'} !~ /^\d+$/)
  {
    $$psError = "Time does not pass muster for $$phMiaContext{'Client'}." if (defined($psError));
    return undef;
  }

  ####################################################################
  #
  # Compute MIA status. If the Checkpoint is zero, force the final
  # Delta to -1 to signify that something is wrong.
  #
  ####################################################################

  $$phMiaContext{'Delta'} = $$phMiaContext{'Time'} - $$phMiaContext{'Checkpoint'};

  $$phMiaContext{'Mia'} =
  (
    ($$phMiaContext{'Delta'} > $$phMiaContext{'Multiplier'} * $$phMiaContext{'Period'}) ||
    ($$phMiaContext{'Delta'} > $$phMiaContext{'Period'} + $$phMiaContext{'Limit'})
  ) ? 1 : 0;

  $$phMiaContext{'Delta'} = -1 if ($$phMiaContext{'Checkpoint'} == 0);

  1;
}


######################################################################
#
# MiaGetDefaultCheckpoint
#
######################################################################

sub MiaGetDefaultCheckpoint()
{
  return 0;
}


######################################################################
#
# MiaGetDefaultLimit
#
######################################################################

sub MiaGetDefaultLimit()
{
  return 600;
}


######################################################################
#
# MiaGetDefaultMultiplier
#
######################################################################

sub MiaGetDefaultMultiplier()
{
  return 1.5;
}


######################################################################
#
# MiaGetDefaultPeriod
#
######################################################################

sub MiaGetDefaultPeriod()
{
  return 3600;
}


######################################################################
#
# MiaGetDefaultTime
#
######################################################################

sub MiaGetDefaultTime()
{
  return time();
}


######################################################################
#
# MiaNewContext
#
######################################################################

sub MiaNewContext
{
  my ($phOptions) = @_;

  my %hMiaContext =
  (
    'Checkpoint' => MiaGetDefaultCheckpoint(),
    'Client'     => undef,
    'Delta'      => -1,
    'Limit'      => MiaGetDefaultLimit(),
    'Multiplier' => MiaGetDefaultMultiplier(),
    'Mia'        => 1,
    'Period'     => MiaGetDefaultPeriod(),
    'Strict'     => 0,
    'Time'       => MiaGetDefaultTime(),
  );

  if (defined($phOptions))
  {
    foreach my $sKey (keys(%hMiaContext))
    {
      if (exists($$phOptions{$sKey}))
      {
        $hMiaContext{$sKey} = $$phOptions{$sKey};
      }
    }
  }

  return \%hMiaContext;
}


######################################################################
#
# MiaVerifyHashKeys
#
######################################################################

sub MiaVerifyHashKeys
{
  my ($phHash, $paKeys) = @_;

  foreach my $sKey (@$paKeys)
  {
    if (!exists($$phHash{$sKey}) || !defined($$phHash{$sKey}))
    {
      return undef;
    }
  }

  1;
}

1;

__END__

=pod

=head1 NAME

WebJob::MiaRoutines - MIA-related routines

=head1 SYNOPSIS

    use WebJob::MiaRoutines;

=head1 DESCRIPTION

This module is a collection of MIA-related routines designed to
support various WebJob server-side utilities. As such, minimal effort
was put into supporting this code for general consumption. In other
words, use at your own risk and don't expect the interface to remain
the same or backwards compatible from release to release. This module
does not provide an OO interface, nor will it do so anytime soon.

=head1 SEE ALSO

webjob-mldbm-get-status(1)

=head1 AUTHOR

Klayton Monroe

=head1 LICENSE

All documentation and code are distributed under same terms and
conditions as B<WebJob>.

=cut
