#------------------------------------------------------------
# Copyright (C) 2007 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#------------------------------------------------------------
#
#------------------------------------------------------------
# Validation Rule ImportTime Object.  Extends the Validate Rule
# class.  This object contains the method to do validation
# on piece of data.
#
# Test if a value is at colon dilimeted MM:SS time or if not
# do the minute and second times only contain digits.  This
# is used because we want to be able to read in MM:SS in the
# minute field as an undocumented feature.
#
# Minutes can contain with MM:SS or MM.  If colon delimited
# then seconds must be null.
#
# Parameter Requirements:
#
#    The parameter used in config_rule must be a:
#       String containing minute or second which indicates
#       which validate tecniques to use.
#
#       i.e. [ min => 1, max => 2 ]
#
# Base Class:
#   Validation::Rule
#
# Public methods:
#   + Validate::Rule::Code new();
#   + $fail_status         ok();
#   + $scalar              AUTOLOAD( $scalar );
#------------------------------------------------------------
package Metadata::Rule::Constraint::ImportTime;
use strict;

use lib '/app/tools/common/lib';
use Common::Log;
use Common::RSApp;
use Common::Assert;

use lib '/app/tools/metadata/lib';
use base 'Validate';
use base 'Validate::Rule';

use Validate::Util qw( is_integer );

use vars qw($AUTOLOAD);

###
# Define addition public accessors
###
use constant kClassAttributes => qw( message fail_status parameter );

###
# Default failure message if the OK method fails.
###
use constant kFailureMessage => "Invalid time";

#------------------------------------------------------------
# Public Methods
#------------------------------------------------------------

#------------------------------------------------------------
# Constructor
#------------------------------------------------------------
sub new {
    my ( $class, %args ) = @_;
    my $self = bless {}, $class;

    return $self->_init(%args);
}

#------------------------------------------------------------
# $scalar AUTOLOAD( $scalar )
#
# Accessor methods for this object.  Methods to access the
# private data in this object.  Valid members are defined in
# the kClassAttributes constant.
#
# If a scalar is passed in this method will set the
# corrisponding private data member.
#
# This method always returns the value of the corrisponding
# private data member.
#
# This method is almost identical base class AUTOLOAD method
# except the method calls the parent AUTOLOAD instead of
# dying when an unknown attribute was seen.
#
# Parameters:
#   $scalar - Set the private data member to this value.
#             ** optional **
#
# Return:
#   $scalar - private memeber value
#------------------------------------------------------------
sub AUTOLOAD {
    my $self  = shift;
    my $value = shift;
    my $field = $AUTOLOAD;
    $field =~ s/.*://;    # Strips fully-qualified portion.

    my $attr = "_$field";

    return if $field eq 'DESTROY';    # skip DESTROY and all-cap methods

    unless ( $self->{_attributes}->{$field} ) {
        my $superior = "SUPER::$field";
        $self->$superior(@_);
    }

    # All preconditions are validated.  Let's do some work.
    $self->{$attr} = $value
      if ( defined($value) );

    return $self->{$attr};
}

#------------------------------------------------------------
# $parameter parameter( $parameter )
#
# The value passed in must be an array reference which is
# converted to a hash with min and max values.
#
# Parameters:
#   $arrayref - array containing the min and max values
#
# Return:
#   $scalar - private memeber value
#------------------------------------------------------------
sub parameter {
    my $self      = shift;
    my $parameter = shift;

    if ($parameter) {
        assert( $parameter =~ /^minutes|seconds$/, "Invalid.  Must be minutes or seconds" );

        $self->{_parameter} = $parameter;
    }

    return $self->{_parameter};
}

#------------------------------------------------------------
# $fail_status ok( $target_data )
#
# Check if target data is a valid time.  If testing minutes
# we will be successful if:
#    1. parameter is only positive integer between 0 - 999
#    2. parameter is two positive integers, separated by
#       a colon.  Minutes must be 0 - 999 and seconds must
#       be 0 - 59.  Also the seconds parameter must be null
#       or equal the seconds followed by the colon.
#
# If testing seconds:
#    1. Minutes is positive integer between 0 - 999 and
#       seconds is positive integer between 0 - 999.
#    2. Minutes contains seconds and both seconds fields
#       match.
#
# Parameters:
#   $target_data - Data we will be testing.
#
# Return:
#   $fail_status - kStatusOK (0) if the test passes, any
#                  non-zero should be considered a failure.
#------------------------------------------------------------
sub ok {
    my ( $self, $target, %args ) = @_;
    my $fail =
      defined( $self->{_fail_status} )
      ? $self->{_fail_status}
      : Validate::kStatusDefault;
    my $success = Validate::kStatusOK;

    return undef unless ( $self->is_configured() );

    # No test if the value is not defined.
    return $success unless ( defined( $args{track_minutes} ) || defined( $args{track_seconds} ) );

    #    return $success if( ! defined($target) && $self->parameter() eq 'minutes' );
    #    return $success if( ! defined($target) && $self->parameter() eq 'seconds' &&
    #                        ! defined($args{track_minutes}) );

    # If the minute parameter doesn't contain a : then we just do an integer
    # check on the parameter
    if (   ( $self->parameter eq 'minutes' && ( !defined($target) || defined($target) && $target !~ /:/ ) )
        || ( $self->parameter eq 'seconds' ) ) {
        return $target && $target =~ /^\d+$/ && $target >= 0 && $target <= 999 ? $success : $fail;
    }

    # Now we know that the minutes column contains a colon so we need to do
    # extended validation.

    my ( $minutes, $seconds );
    $minutes = $self->parameter() eq 'minutes' ? $target : $args{track_minutes};
    $seconds = $self->parameter() eq 'seconds' ? $target : $args{track_seconds};

    return $self->_is_valid_time( minutes => $minutes, seconds => $seconds ) ? $success : $fail;
}

#------------------------------------------------------------
# Private Methods
#------------------------------------------------------------

#------------------------------------------------------------
# Validate::Rule _init()
# Initialize the object.
#
# - Build accessor method hash
# - set internal private data ( message, fail_status, and
#   parameter ). See the base class for parameter defintions.
#------------------------------------------------------------
sub _init {
    my ( $self, %args ) = @_;
    my @paramlist;

    # Build a hash with our valid class attributes
    foreach ( (kClassAttributes) ) {
        $self->{_attributes}->{$_}++;
    }

    $self->message( $args{message}                    ? $args{message}     : kFailureMessage );
    $self->fail_status( defined( $args{fail_status} ) ? $args{fail_status} : Validate::kStatusDefault );

    $self->parameter( $args{parameter} );

    $self->SUPER::_init(%args);

    return $self;
}

#------------------------------------------------------------
# Validate::Rule _is_valid_time()
# Compare a colon seperated minute:second string and a
# seperate seconds value.  Return true if all values are
# valid and seconds match.
#
# Parameters:
#   minutes - A colon seperated minutes/seconds string
#   seconds - seconds specified
#
# Return:
#   bool - true if times are valid, otherwise return undef.
#------------------------------------------------------------
sub _is_valid_time {
    my $self = shift;
    my %args = @_;

    use Data::Dumper;
    Common::Log::Debug( Dumper( \%args ) );

    return 1 unless ( defined( $args{minutes} ) );

    # We're done unless time came in MM:SS format
    return undef unless ( $args{minutes} =~ /^\d+:\d+$/ );

    my ( $mMinutes, $mSeconds ) = split( /:/, $args{minutes} );

    # Don't really need this test, but what the heck
    return undef unless ( defined($mMinutes) && defined($mSeconds) );

    # If minutes and seconds aren't numeric then fail
    return undef unless ( $mMinutes =~ /^\d+/ && $mSeconds =~ /^\d+$/ );

    # Seconds must be between 0 and 59 seconds
    return undef unless ( $mSeconds >= 0 && $mSeconds <= 59 );

    # Minutes must be between 0 and 999
    return undef unless ( $mMinutes >= 0 && $mMinutes <= 999 );

    # Ensure the passed in seconds is undefined or is the same as the time string
    return undef if ( defined( $args{seconds} )
        && $args{seconds} =~ /^\d+$/
        && $args{seconds} != $mSeconds );

    # All test are passed.  We have a valid import time.
    return 1;
}

1;
