#!/usr/bin/perl -wT

use strict;
use warnings;

use POSIX;
use CGI;

sub position($$)
{
    my $card = shift;
    my $deck = shift;
    my ($pile, $slot, $position);
    
    foreach my $j (0..2)
    {
        foreach my $i (0..6)
        {
            if( $deck->[$i + $j*7] == $card )
            {
                $pile = $j + 1;
                $slot = $i + 1;
                $position = 1 + $i + $j*7;

                return( ($pile, $slot, $position) );
            }
        }
    }
}

sub run_trick($$)
{
    my $query = shift;
    my $card = shift;

    my @deck = 1..21;
    my @piles = ([0..6],[7..13],[14..20]);
    my @tmp;

    my $middle = ($card >= 8 && $card <= 14) ? -1 : 0;
    my @summary = ($query->th({-align=>'right'}, ['Step', 'Pile', 'Position in Pile', 'Position in Deck'] ), );


    my $step = 0;
    my ($pile, $slot, $position) = print_deck( $query, $step, $card, \@deck );

    push @summary, $query->td( {-align=>'right'}, [$step, $pile, $slot, $position] );

    while( 1 )
    {
        #
        # Sort
        #

        foreach my $i (0..2)
        {
            if( grep {$_ == $card} @deck[@{$piles[$i]}] )
            {
                @tmp = @deck;
                @deck = (@tmp[@{$piles[($i + 2) % 3]}], @tmp[@{$piles[$i]}], @tmp[@{$piles[($i + 1) % 3]}]);

                if( $i == 1 )
                {
                    ++$middle;
                }
                else
                {
                    $middle = 0;
                }

                last;
            }
        }

        last if( $middle >= 2 );

        #
        # Deal
        #

        @tmp = @deck;

        foreach my $i (reverse(0..6))
        {
            $deck[$piles[0]->[$i]] = shift(@tmp);
            $deck[$piles[1]->[$i]] = shift(@tmp);
            $deck[$piles[2]->[$i]] = shift(@tmp);
        }

        ($pile, $slot, $position) = print_deck( $query, ++$step, $card, \@deck );
        push @summary, $query->td( {-align=>'right'}, [$step, $pile, $slot, $position] );
    }

    $query->print( "Here's a summary of where your card was at each step (1 = Top):", "\n" );
    $query->print( $query->table( {-cellspacing=>12}, $query->Tr( [@summary] ) ), "\n" );
    $query->print( $query->hr, $query->br, "\n" );

    return( ($step, position($card, \@deck)) );
}

sub print_deck($$$$)
{
    my $query = shift;
    my $step = shift;
    my $card = shift;
    my $deck = shift;
    my @piles = ([0..6],[7..13],[14..20]);
    my @ordinals = ("oops", "on", "the second card from the", "the third card from the", "the fourth card from the", "the fifth card from the", "the sixth card from the", "the seventh card from the" );

    my @entries = ( $query->th( {-align=>'center'}, ['Pile 1', 'Pile 2', 'Pile 3'] ), );

    my ($pile, $slot, $position) = position( $card, $deck );

    my @cdeck = @$deck;
    $cdeck[$position - 1] = $query->span( {-style=>'color: red; font-size: large;'}, $card );

    if( $step > 0 )
    {
        $query->print( "After step $step ...\n", $query->br );
    }
    else
    {
        $query->print( "At the start ...\n", $query->br );
    }

    $query->print( sprintf( "Your card is %s top of pile %d, and when the piles are stacked, your card is number %d, counting from the top of the deck\n<BR>", $ordinals[$slot], $pile, $position ) );
                             

    foreach my $i (0..6)
    {
        push @entries, $query->td( [$cdeck[$piles[0]->[$i]], $cdeck[$piles[1]->[$i]], $cdeck[$piles[2]->[$i]]] );
    }       

    $query->print( $query->table( {-cellspacing=>12}, $query->Tr( [@entries] ) ), "\n" );
    $query->print( $query->hr, $query->br, "\n" );

    return( ($pile, $slot, $position) );
}

#
# Main routine
#

my $query = new CGI;


$query->print( $query->header, "\n" );
$query->print( $query->start_html, "\n" );

if( $query->param( 'card' ) )
{
    my $scratch = $query->param( 'card' );
    $scratch =~ /(1[0-9]|2[0-1]|[1-9])/  || die "Improper card specified";
    my $card = $1;


    $query->print( "Now I'll show you the piles every step of the way. Your card will be shown in ", $query->span({-style=>"color: red;"}, "red" ), "\n", $query->br, $query->hr, "\n" );
    run_trick( $query, $card );

    $query->delete( 'card' );
    $query->print( $query->startform, "\n" );
    $query->print( $query->submit( -name=>'reset', -value=>'Pick another card' ), "\n" );
    $query->print( $query->endform, "\n" );
}
else
{
    $query->print( $query->span( {-style=>'font-size: large;'}, "I've put 21 cards into 3 piles of 7 cards each. The cards are numbered from 1 to 21. Pick one of the cards then push the button below to do the trick. Don't worry, I'm not looking!" ),
                   $query->br, "\n" );

    $query->print( $query->startform, "\n" );
    
    my @entries = ( $query->th({-align=>'right'},['Pile 1', 'Pile 2', 'Pile 3']),  );
    my @h = $query->radio_group( -name=>'card', -values=>[1..21], -default=>'-' );

    foreach my $k (0..6)
    {
        push @entries, $query->td( {-align=>'right'}, [@h[($k,$k+7,$k+14)]] );
    }

    $query->print( $query->table( {-cellspacing=>12}, $query->Tr( [@entries] ) ), "\n", $query->br, "\n" );
    $query->print( $query->submit( -name=>'run', -value=>'Do the trick now' ), "\n" );
    $query->print( $query->endform, "\n" );
}

$query->print( $query->end_html, "\n" );
