#!/usr/bin/perl

package Razor2::Signature::Ephemeral;
use strict;
use Data::Dumper;

BEGIN {
  eval  { require Digest::SHA;  import Digest::SHA  qw(sha1_hex); 1 }
  or do { require Digest::SHA1; import Digest::SHA1 qw(sha1_hex) }
}

sub new {

    my ( $class, %args ) = @_;

    my $self = bless {
        seed => $args{seed} || 42,
        separator => encode_separator( $args{separator} ) || encode_separator("10"),
    }, $class;
    $self;

}

sub hexdigest {

    my ( $self, $content ) = @_;

    # Initialize PRNG with $seed
    srand( $$self{seed} );

    my @content = split /$$self{separator}/, $content;

    # $content =~ s/$$self{separator}//g; -- We don't do this anyore

    # my $size = length($content);
    my $lines = scalar @content;

    debug("\nNumber of lines: $lines");

    # Randomly choose relative locations and section sizes (in percent)
    my $sections   = 6;
    my $ssize      = 100 / $sections;
    my @rel_lineno = map { rand($ssize) + ( $_ * $ssize ) } 0 .. ( $sections - 1 );
    my @lineno     = map { int( ( $_ * $lines ) / 100 ) } @rel_lineno;

    debug("Relative Line Numbers (in percent): @rel_lineno");
    debug("Absolute Line Numbers: @lineno");

    my @rel_offset1 = map { rand(50) + ( $_ * 50 ) } qw(0 1);
    my @rel_offset2 = map { rand(50) + ( $_ * 50 ) } qw(0 1);

    debug("Relative Offsets for section 1: @rel_offset1");
    debug("Relative Offsets for section 2: @rel_offset2");

    my ( $l1, $l2 ) = ( 0, 0 );
    for ( $lineno[1] .. $lineno[2] ) { $l1 += length( $content[$_] ) if $content[$_] }
    for ( $lineno[3] .. $lineno[4] ) { $l2 += length( $content[$_] ) if $content[$_] }

    debug("Length of the first section: $l1 bytes");
    debug("Length of the second section: $l2 bytes");

    my @offset1 = map { int( ( $_ * $l1 ) / 100 ) } @rel_offset1;
    my @offset2 = map { int( ( $_ * $l2 ) / 100 ) } @rel_offset2;

    debug( "Chunk start/end positions in Section 1: @offset1 (length: " . ( $offset1[1] - $offset1[0] ) . ") " );
    debug( "Chunk start/end positions in Section 2: @offset2 (length: " . ( $offset2[1] - $offset2[0] ) . ") " );

    my $x = 0;
    my ( $sc, $sl, $ec, $el ) = ( 0, 0, 0, 0 );
    my $section1 = picksection(
        \@content,
        $lineno[1],  $lineno[2],
        $offset1[0], $offset1[1]
    );
    my $section2 = picksection(
        \@content,
        $lineno[3],  $lineno[4],
        $offset2[0], $offset2[1]
    );

    debug("Section 1: $section1");
    debug("Section 2: $section2");

    my $seclength = length( $section1 . $section2 );

    debug("Total length of stuff that will be hashed: $seclength");

    if ( $section1 =~ /^\s+$/ && $section2 =~ /^\s+$/ ) {
        debug("Both sections were whitespace only!");
        $section1 = "";
        $section2 = "";
    }

    my $digest;

    if ( $seclength > 128 ) {
        $digest = sha1_hex($section1, $section2);
    }
    else {
        debug("Sections too small... reverting back to orginal content.");
        $digest = sha1_hex($content);
    }

    debug("Computed e-hash is $digest");

    return $digest;

}

sub picksection {

    my ( $content, $sline, $eline, $soffset, $eoffset ) = @_;
    my $x = 0;
    my ( $sc, $sl, $ec, $el ) = ( 0, 0, 0, 0 );

    for ( $sline .. $eline ) {
        next unless $content->[$_];
        $x = $x + length( $content->[$_] );
        if ( ( $x > $soffset ) && ( $sc == 0 ) ) {    # we come here first time
            $sc = length( $content->[$_] ) - ( $x - $soffset );    # $x is greater than start
            $sl = $_;                                              # offset
        }
        if ( $x > $eoffset ) {
            $ec = length( $content->[$_] ) - ( $x - $eoffset );
            $el = $_;
        }
        last if $ec;
    }

    $sc = 0 if $sc < 0;
    $ec = 0 if $ec < 0;                                            # FIX!  not verified to work correctly.

    debug("Absolute chunk offsets: Line $sl charachter $sc to line $el character $ec");

    my $section = "";

    if ( $sl == $el ) {
        if ( $content->[$sl] ) {
            $section = substr( $content->[$sl], $sc, $ec - $sc + 1 );
        }
        else {
            $section = "";
        }
    }
    else {
        $section .= substr( $content->[$sl], $sc );
        for ( $sl + 1 .. $el - 1 ) {
            $section .= $content->[$_];
        }
        $section .= substr( $content->[$el], 0, $ec );
    }
    return $section;
}

sub encode_separator {

    my ( $self, $separator ) = @_;
    my $rv;

    unless ( ref $self ) { $separator = $self }
    my @chars = split /-/, $separator;
    push @chars, $separator unless scalar @chars;
    for (@chars) { $rv .= chr($_) }
    return $rv;

}

sub debug {
    my $message = shift;

    # print "debug: $message\n";
    #open TMP, ">>/tmp/ehash";
    #print TMP "$message\n";
    #close TMP;
}

1;

