#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2010 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------
package Common::Flattery::FlatteryBot;
use strict;
use warnings;

use lib '/app/tools/common/lib';
use Common::DB::Item::Flattery;
use Common::RSApp;
use Common::Util;
use Common::Assert;

use Data::Dumper;

sub acquireFlattery {
    my ( $self, %args ) = @_;

    assert( $args{flatteryAmount} );

    my $collection = Common::DB::Item::Flattery->GetAll();

    my %seen;
    my %used;
    my $requested = $args{flatteryAmount};
    my $found     = 0;
    my $flattery;

    # We will eventually need to reset the position_mask for all flattery entries,
    # once we've used them in all possible positions.
    my $attempts    = 0;
    my $maxAttempts = $collection->size();

    my $upperLimit = Common::DB::Item::Flattery->GetMaxFlatteryID();

    while ( $found < $requested ) {
        $attempts++;

        # We seem to be having trouble getting flattery.
        # Let's reset the position_mask for all entries and start over.
        if ( $attempts >= $maxAttempts ) {
            Common::DB::Item::Flattery->ResetFlattery();

            $attempts = 0;
            $found    = 0;
            $flattery = '';
            undef %seen;

            redo;
        }

        my $candidate = int rand($upperLimit);

        my $position = $found + 1;

        # If we have already tried this one, skip it and try a different one.
        if ( $seen{$candidate} && $seen{$candidate} == 1 ) {
            redo;
        }

        # If we are using this one already in a different position, skip it as well.
        # %seen is reset for each position, which is why we store the ID for the one we're using in %used.
        if ( $used{$candidate} && $used{$candidate} == 1 ) {
            $seen{$candidate} = 1;
            redo;
        }

        my $flatteryDBI = Common::DB::Item::Flattery->Lookup( flattery_id => $candidate );
        if ( !$flatteryDBI ) {
            $seen{$candidate} = 1;
            redo;
        }

        # This is a decimal that we are using to store a string of binary values
        # to keep track of which position this particular piece of flattery has appeared in.
        my $positionMask   = $flatteryDBI->position_mask;
        my $binaryPosition = dec2bin($positionMask);

        # The number of position may change, so let's make sure the binary string is long enough.
        $binaryPosition .= '0' while length($binaryPosition) < ( $position + 1 );

        my $positionUsed = substr( $binaryPosition, $position, 1 );

        if ( $positionUsed == 1 ) {
            $seen{$candidate} = 1;
            redo;
        }

        # If we made it this far, then great news!  We found one we can use.

        # Now to update the position mask.
        # We also want to set the very first character  to 1.
        # Otherwise, the leading zeros will be dropped and we need those.
        substr( $binaryPosition, 0,         1 ) = 1;
        substr( $binaryPosition, $position, 1 ) = 1;

        $positionMask = bin2dec($binaryPosition);

        $flatteryDBI->position_mask($positionMask);
        $flatteryDBI->save();

        my $flatteryText = $flatteryDBI->flattery_text;
        $flatteryText =~ s/\s+$//;

        $flattery .= " " . $flatteryText;

        # We need to store the id in the used hash so that we don't use it in another position in the same email.
        # We're going to blow away the values in the seen hash now too, because some of them might be acceptable for
        # a different position.
        $used{$candidate} = 1;
        undef %seen;
        $attempts = 0;
        $found++;
    }

    if ( !$flattery ) {
        $flattery = "Help!  I'm out of flattery.  I just feel terrible about this.";
    }

    return $flattery;
}

sub dec2bin {
    my $str = unpack( "B32", pack( "N", shift ) );
    $str =~ s/^0+(?=\d)//;    # otherwise you'll get leading zeros
    return $str;
}

sub bin2dec {
    return unpack( "N", pack( "B32", substr( "0" x 32 . shift, -32 ) ) );
}

###
1;                            #
###
