##########################################################################
# This file is part of Vacuum Magic
# Copyright (C) 2008-2009 by UPi <upi at sourceforge.net>
##########################################################################

use strict;
use Carp;

our (@Cos, $DataDir, $Difficulty, $DifficultySetting, %Events, $Game, %GameEvents, @GameObjects, 
  %Keys, $Level, $NumGuys, @Players, $ScreenHeight, $ScreenWidth, @Sin, %Sprites, %Textures);

=comment

Tutorial levels are:

# Flying, collecting food.
# Flying, avoiding ice darts.
# Flying, avoiding poison.
# Flying, spitting at targets.
# Clearing a beehive + superball.
# Shooting a bonus box
# Clearing crosswind + fire shield.
# Killing a boss.

=cut

package main;

my @TutorialRegistry = qw(FoodTutorial IceTutorial PoisonTutorial TargetTutorial BeeTutorial BonusBoxTutorial);

sub InitTutorialLevels {
  return map { new PlaybackTutorialLevel($_, 'playback'), new TutorialLevel($_, 'game') } (1 .. scalar(@TutorialRegistry));
}

sub RecordTutorial {
  my ($number) = @_;
  
  &InitLevelFactory("Tutorial/$number");
  @Levels = ( new RecordTutorialLevel($number, 'record') );
  &StartRecorder();
  $Game = new NormalGame;
  $Game->Run();
  &StopRecorder();
}

sub RecordTutorials {
  $Difficulty = 1;
  $NumGuys = 1;
  &SetBackground(0);
  foreach (1 .. scalar(@TutorialRegistry)) {
    &RecordTutorial($_);
  }
}


##########################################################################
package FoodTutorial;
##########################################################################

@FoodTutorial::ISA = qw(NoCollisions GameObject);

sub InitializeTutorial {
  my ($self, $short) = @_;
  
  $Difficulty = 2;
  foreach (1 .. ($short ? 5 : 10)) { new Ball(); }
}

sub GetTitle { ::T("Collecting Food"), ::T("Use your vacuum field to collect food and the Fire key to turn.") }

sub Advance {
  my ($self) = @_;
  
  return $Level->OnTutorialOver()  if $Ball::Balls == 0;
}


##########################################################################
package IceTutorial;
##########################################################################

@IceTutorial::ISA = qw(NoCollisions GameObject);

sub InitializeTutorial {
  my ($self, $short) = @_;
  
  $self->{iceDelay} = 100;
  $Difficulty = 2;
  foreach (1 .. ($short ? 5 : 10)) { new Ball(); }
}

sub GetTitle { ::T("Dodge The Ice") }

sub Advance {
  my ($self) = @_;
  
  if (--$self->{iceDelay} < 0) {
    $self->{iceDelay} = $Ball::Balls * 10 + 40;
    new IceDart();
  }
  return $Level->OnTutorialOver()  if $Ball::Balls == 0;
}


##########################################################################
package PoisonTutorial;
##########################################################################

@PoisonTutorial::ISA = qw(NoCollisions GameObject);

sub InitializeTutorial {
  my ($self, $short) = @_;
  
  $Difficulty = 4;
  foreach (1 .. ($short ? 5 : 10)) { new PoisonBall(); }
}

sub GetTitle { ::T("Avoid The Poison"), ::T("Use the fire key to spit out poisonous food.") }

sub Advance {
  my ($self) = @_;
  
  return $Level->OnTutorialOver()  if $Ball::Balls == 0;
}


##########################################################################
package TargetTutorial;
##########################################################################

@TargetTutorial::ISA = qw(NoCollisions GameObject);

sub InitializeTutorial {
  my ($self, $short) = @_;
  my ($bullseyeLevel, @bullseyeParams, $bullseye, @wave);
  
  $Difficulty = 3;
  $self->{bullseyeLevels} = $short ? [ 5, 6, 7, 8 ] : [ 1, 2, 3, 4, 5, 6 ];
  foreach (1 .. 3) {
    $self->SpawnNextBullseye();
  }
}

sub GetTitle { ::T("Target Spitting"), ::T("Keeping fire key pressed will give you more time to aim")  }

sub SpawnNextBullseye {
  my ($self) = @_;
  my ($bullseyeLevel, @bullseyeParams, $bullseye, @wave);
  
  $bullseyeLevel = shift @{$self->{bullseyeLevels}};
  return  unless defined($bullseyeLevel);
  (undef, undef, @bullseyeParams) = @{$TargetPracticeLevel::Levels[$bullseyeLevel-1]};
  $bullseye = new Bullseye(@bullseyeParams);
  $bullseye->{score} = 1000;
  $bullseye->{isboss} = 1;
  $bullseye->SetOnDeleted(\&OnBullseyeDeleted, $self);
  ++$self->{bullseyes};
}

sub OnBullseyeDeleted {
  my ($self) = @_;
  
  --$self->{bullseyes};
  $self->SpawnNextBullseye();
  $Level->OnTutorialOver()  if  $self->{bullseyes} <= 0;
}

sub Advance {
  my ($self) = @_;
  while ($Ball::Balls < 6) {
    new Ball();
  }
}


##########################################################################
package BeeTutorial;
##########################################################################

@BeeTutorial::ISA = qw(NoCollisions GameObject);

sub InitializeTutorial {
  my ($self, $short) = @_;
  my ($bullseyeLevel, @bullseyeParams, $bullseye, @wave);
  
  $Difficulty = 3;
  $self->{waves} = $short
    ? [ {bees=>3, difficulty=>4} ]
    : [ {bees=>1, difficulty=>3},  {bees=>3, difficulty=>4}, {bees=>6, difficulty=>5} ];
  $self->SpawnNextWave();
  new SuperBall();
}

sub GetTitle { ::T("Beware of the Bees"), ::T("Vacuum them or spit them at each other.") }

sub SpawnNextWave {
  my ($self) = @_;
  my ($wave, $bees, $bee);
  
  $wave = shift @{$self->{waves}};
  return  unless $wave;
  $Difficulty = $wave->{difficulty};
  $bees = $wave->{bees};
  foreach (1 .. $bees) {
    ++$self->{bees};
    $bee = new Bee;
    $bee->{isboss} = 1;
    $bee->SetOnDeleted(\&OnBeeDeleted, $self);
  }
}

sub OnBeeDeleted {
  my ($self) = @_;
  
  --$self->{bees};
  $self->SpawnNextWave()  if $self->{bees} <= 0;
  $Level->OnTutorialOver()  if  $self->{bees} <= 0;
}

sub Advance {}


##########################################################################
package BonusBoxTutorial;
##########################################################################

@BonusBoxTutorial::ISA = qw(NoCollisions GameObject);

sub InitializeTutorial {
  my ($self, $short) = @_;
  
  $Difficulty = 2;
  $self->SpawnBonusBox();
}

sub GetTitle { ::T("Shoot the Magic Box!"), ::T("It contains valuable powerups, so perfect your aim!") }

sub SpawnBonusBox {
  my ($self) = @_;
  $self->{bonusBox} = new BonusBox;
  $self->{bonusBox}->SetOnDeleted(\&OnBonusBoxDeleted, $self);
}

sub OnBonusBoxDeleted {
  my ($self) = @_;
  
  if ($self->{bonusBox}->{opened}) {
    my ($bonusItem) = grep { ref($_) eq 'BonusItem' } @GameObjects;
    $bonusItem->SetOnDeleted(\&OnBonusItemDeleted);
  } else {
    new GameLevelIndicator( level=>::T("Try again!") );
    $self->SpawnBonusBox();
  }
}

sub OnBonusItemDeleted {
  $Level->OnTutorialOver();
}

sub Advance {
  my ($self) = @_;
  
  while ($Ball::Balls < 5) {
    new Ball();
  }
}


##########################################################################
package TutorialLevel;
##########################################################################

@TutorialLevel::ISA = qw(Level);

sub Initialize {
  my ($self) = @_;
  my ($number, $tutorialMode) = @{$self->{params}};
  
  $Game->RemoveActionByName('BonusBoxSpawner');
  $Game->RemoveActionByName('SuperBallSpawner');
  
  $self->{tutorialMode} = $tutorialMode;
  $self->{level} = $number;
  $self->InitializeTutorialObject();
  
  $self->SUPER::Initialize();
  push @{$self->{actions}},
    &::MakePlayerDeathHandler();
}

sub InitializeTutorialObject {
  my ($self) = @_;

  if ($self->{tutorialMode} eq 'innerPlayback') {
    $self->{tutorialObject} = $self->MakeTutorialObject(1);
  } elsif ($self->{tutorialMode} eq 'game') {
    $self->{tutorialObject} = $self->MakeTutorialObject(0);
    new GameLevelIndicator(level => ::T("Now you try it!"), bonusText => ($self->{tutorialObject}->GetTitle())[1]);
  } else {
    Carp::confess "Bad tutorialMode: $self->{tutorialMode}";
  }
}

sub MakeTutorialObject {
  my ($self, $short) = @_;
  my ($result, $tutorialClass);
  
  $tutorialClass = $TutorialRegistry[$self->{level} - 1];
  eval("\$result = new $tutorialClass();");  Carp::confess $@ if $@;
  $result->InitializeTutorial($short);
  return $result;
}

sub IsLevelOver {
  return $_[0]->{tutorialObject}->{deleted};
}

sub OnTutorialOver {
  my ($self) = @_;
  
  $Game->{storedGuys} = [@Guy::Guys];
  while (@Guy::Guys) {
    $Guy::Guys[0]->Leave();
  }
  foreach (@::GameObjects) {
    $_->Leave()  if ref($_) eq 'Ball';
    delete $_->{isboss};
  }
  $self->{tutorialObject}->Delete();
}

sub OnLevelOver {}

sub GetFilename {
  "$DataDir/tutorial$_[0]->{level}";
}

sub CreateGameLevelIndicator {
  my ($self, $tutorialObject) = @_;
  
  my ($title, $bonusText) = $tutorialObject->GetTitle();
  new GameLevelIndicator(level => "$self->{level}. $title", bonusText => $bonusText);
}


##########################################################################
package PlaybackTutorialLevel;
##########################################################################

@PlaybackTutorialLevel::ISA = qw(TutorialLevel);

sub InitializeTutorialObject {
  my ($self) = @_;

  if (@Guy::Guys) {
    $Game->{storedGuys} = [@Guy::Guys];
    while (@Guy::Guys) {
      $Guy::Guys[0]->Leave();
    }
  }
  $self->{tutorialObject} = &::LoadRecord($self->GetFilename(), 1);
  $self->{tutorialObject}->{splayers}->[0]->{color} = $Guy::Guys[0]->{color} = [1,0.68,0.12];
  $self->CreateGameLevelIndicator($self->{tutorialObject}->{slevel}->{tutorialObject});
}

sub OnLevelOver {
  my ($self) = @_;
  
  foreach (@{$Game->{storedGuys}}) {
    $_->Spawn();
  }
}


##########################################################################
package RecordTutorialLevel;
##########################################################################

@RecordTutorialLevel::ISA = qw(TutorialLevel);

sub InitializeTutorialObject {
  my ($self) = @_;
  
  $::LevelSet = "Tutorial/$self->{level}";
  $self->{tutorialObject} = $self->MakeTutorialObject(1);
  $self->CreateGameLevelIndicator($self->{tutorialObject});
}

sub IsLevelOver {
  my ($self) = @_;
  
  if ($self->{tutorialObject}->{deleted}) {
    return $Game->{storedGuys}->[0]->{y} < -400;
  }
  return 0;
}

sub OnLevelOver {
  my ($self) = @_;
  
  &::SaveRecord($self->GetFilename());
  $Game->{abortgame} = 1
}
