package Mouse;

use strict;
use warnings;
use Qt4;
# [0]
use Qt4::isa qw( Qt4::Object );
use Math::Trig;

sub angle() {
    return this->{angle};
}

sub setAngle($) {
    return this->{angle} = shift;
}

sub speed() {
    return this->{speed};
}

sub setSpeed($) {
    return this->{speed} = shift;
}

sub mouseEyeDirection() {
    return this->{mouseEyeDirection};
}

sub setMouseEyeDirection($) {
    return this->{mouseEyeDirection} = shift;
}

sub color() {
    return this->{color};
}

sub setColor($) {
    return this->{color} = shift;
}

sub graphicsItem {
    return this->{graphicsItem};
}

# [0]

my $Pi = 3.14159265358979323846264338327950288419717;
my $TwoPi = 2.0 * $Pi;
use constant { RAND_MAX => 2147483647 };

sub normalizeAngle
{
    my ($angle) = @_;
    while ($angle < 0) {
        $angle += $TwoPi;
    }
    while ($angle > $TwoPi) {
        $angle -= $TwoPi;
    }
    return $angle;
}

# [0]
sub NEW
{
    my ( $class ) = @_;
    $class->SUPER::NEW();
    this->{graphicsItem} = Qt4::GraphicsItem();
    this->setAngle(0);
    this->setSpeed(0);
    this->setMouseEyeDirection(0);
    this->setColor(Qt4::Color(rand(RAND_MAX) % 256, rand(RAND_MAX) % 256, rand(RAND_MAX) % 256));
    this->rotate(rand(RAND_MAX) % (360 * 16));
    this->startTimer(1000 / 33);
}
# [0]

# [1]
sub boundingRect
{
    my $adjust = 0.5;
    return Qt4::RectF(-18 - $adjust, -22 - $adjust,
                  36 + $adjust, 60 + $adjust);
}
# [1]

# [2]
sub shape
{
    my $path = Qt4::PainterPath();
    $path->addRect(-10, -20, 20, 40);
    return $path;
}
# [2]

# [3]
sub paint
{
    my ($painter) = @_;
    # Body
    $painter->setBrush(this->color);
    $painter->drawEllipse(-10, -20, 20, 40);

    # Eyes
    $painter->setBrush(Qt4::white());
    $painter->drawEllipse(-10, -17, 8, 8);
    $painter->drawEllipse(2, -17, 8, 8);

    # Nose
    $painter->setBrush(Qt4::black());
    $painter->drawEllipse(Qt4::RectF(-2, -22, 4, 4));

    # Pupils
    $painter->drawEllipse(Qt4::RectF(-8.0 + this->mouseEyeDirection, -17, 4, 4));
    $painter->drawEllipse(Qt4::RectF(4.0 + this->mouseEyeDirection, -17, 4, 4));

    # Ears
    $painter->setBrush(this->scene()->collidingItems(this)->isEmpty() ? Qt4::darkYellow() : Qt4::red());
    $painter->drawEllipse(-17, -12, 16, 16);
    $painter->drawEllipse(1, -12, 16, 16);

    # Tail
    my $path = Qt4::PainterPath(Qt4::PointF(0, 20));
    $path->cubicTo(-5, 22, -5, 22, 0, 25);
    $path->cubicTo(5, 27, 5, 32, 0, 30);
    $path->cubicTo(-5, 32, -5, 42, 0, 35);
    $painter->setBrush(Qt4::NoBrush());
    $painter->drawPath($path);
}
# [3]

# [4]
sub timerEvent
{
# [4]
    # Don't move too far away
# [5]
    my $lineToCenter = Qt4::LineF(Qt4::PointF(0, 0), this->mapFromScene(0, 0));
    if ($lineToCenter->length() > 150) {
        my $angleToCenter = acos($lineToCenter->dx() / $lineToCenter->length());
        if ($lineToCenter->dy() < 0) {
            $angleToCenter = $TwoPi - $angleToCenter;
        }
        $angleToCenter = normalizeAngle(($Pi - $angleToCenter) + $Pi / 2);

        if ($angleToCenter < $Pi && $angleToCenter > $Pi / 4) {
            # Rotate left
            this->{angle} += (this->angle < -$Pi / 2) ? 0.25 : -0.25;
        } elsif ($angleToCenter >= $Pi && $angleToCenter < ($Pi + $Pi / 2 + $Pi / 4)) {
            # Rotate right
            this->{angle} += (this->{angle} < $Pi / 2) ? 0.25 : -0.25;
        }
    } elsif (sin(this->angle) < 0) {
        this->{angle} += 0.25;
    } elsif (sin(this->angle) > 0) {
        this->{angle} -= 0.25;
# [5] #! [6]
    }
# [6]

    # Try not to crash with any other mice
# [7]
    my $dangerMice = this->scene()->items( [
        this->mapToScene(0, 0),
        this->mapToScene(-30, -50),
        this->mapToScene(30, -50)
    ] );

    foreach my $item ( @{$dangerMice} ) {
        if ($item == this) {
            next;
        }
        
        my $lineToMouse = Qt4::LineF(Qt4::PointF(0, 0), this->mapFromItem($item, 0, 0));
        my $angleToMouse = acos($lineToMouse->dx() / $lineToMouse->length());
        if ($lineToMouse->dy() < 0) {
            $angleToMouse = $TwoPi - $angleToMouse;
        }
        $angleToMouse = this->normalizeAngle(($Pi - $angleToMouse) + $Pi / 2);

        if ($angleToMouse >= 0 && $angleToMouse < $Pi / 2) {
            # Rotate right
            this->{angle} += 0.5;
        } elsif ($angleToMouse <= $TwoPi && $angleToMouse > ($TwoPi - $Pi / 2)) {
            # Rotate left
            this->{angle} -= 0.5;
# [7] #! [8]
        }
# [8] #! [9]
    }
# [9]

    # Add some random movement
# [10]
    if (scalar @{$dangerMice} > 1 && (rand(RAND_MAX) % 10) == 0) {
        if (rand(RAND_MAX) % 1) {
            this->{angle} += (rand(RAND_MAX) % 100) / 500.0;
        }
        else {
            this->{angle} -= (rand(RAND_MAX) % 100) / 500.0;
        }
    }
# [10]

# [11]
    this->{speed} += (-50 + rand(RAND_MAX) % 100) / 100.0;

    my $dx = sin(this->angle) * 10;
    this->{mouseEyeDirection} = (abs($dx / 5) < 1) ? 0 : $dx / 5;

    this->rotate($dx);
    this->setPos(this->mapToParent(0, -(3 + sin(this->speed) * 3)));
}
# [11]

sub setPos {
    this->graphicsItem->setPos( @_ );
}

sub rotate {
    this->graphicsItem->rotate( @_ );
}

1;