/* Domino-Chain
 *
 * Domino-Chain is the legal property of its developers, whose
 * names are listed in the AUTHORS file, which is included
 * within the source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
 */

#ifndef __FIGURE_H__
#define __FIGURE_H__

#include "levelplayer.h"

#include <stdint.h>

// this class contains all necessary code for the figure animation

// This enumeration contains all the possible animations that the figure can
// perform. The number of images in each animation is fixes and can be
// aquired using getFigureImages
typedef enum {
  FigureAnimWalkLeft,
  FigureAnimWalkRight,
  FigureAnimJunpUpLeft,
  FigureAnimJunpUpRight,
  FigureAnimJunpDownLeft,
  FigureAnimJunpDownRight,
  FigureAnimLadder1,
  FigureAnimLadder2,
  FigureAnimLadder3,
  FigureAnimLadder4,
  FigureAnimCarryLeft,
  FigureAnimCarryRight,
  FigureAnimCarryUpLeft,
  FigureAnimCarryUpRight,
  FigureAnimCarryDownLeft,
  FigureAnimCarryDownRight,
  FigureAnimCarryLadder1,
  FigureAnimCarryLadder2,
  FigureAnimCarryLadder3,
  FigureAnimCarryLadder4,
  FigureAnimCarryStopLeft,
  FigureAnimCarryStopRight,
  FigureAnimPullOutLeft,
  FigureAnimPullOutRight,
  FigureAnimPushInLeft,
  FigureAnimPushInRight,
  FigureAnimXXX1,
  FigureAnimXXX2,
  FigureAnimXXX3,
  FigureAnimXXX4,
  FigureAnimLoosingDominoRight,
  FigureAnimLoosingDominoLeft,
  FigureAnimStepOutForLoosingDomino,
  FigureAnimStop,
  FigureAnimTapping,
  FigureAnimYawning,
  FigureAnimEnterLeft,
  FigureAnimEnterRight,
  FigureAnimPushLeft,
  FigureAnimPushRight,
  FigureAnimPushStopperLeft,
  FigureAnimPushStopperRight,
  FigureAnimPushRiserLeft,
  FigureAnimPushRiserRight,
  FigureAnimPushDelayLeft,
  FigureAnimPushDelayRight,
  FigureAnimSuddenFallRight,
  FigureAnimSuddenFallLeft,
  FigureAnimFalling,
  FigureAnimInFrontOfExploder,
  FigureAnimInFrontOfExploderWait,
  FigureAnimLanding,
  FigureAnimGhost1,
  FigureAnimGhost2,
  FigureAnimLeaveDoorEnterLevel,
  FigureAnimStepAsideAfterEnter,
  FigureAnimEnterDoor,
  FigureAnimStepBackForDoor,
  FigureAnimStruggingAgainsFallLeft,
  FigureAnimStruggingAgainsFallRight,
  FigureAnimVictory,
  FigureAnimShrugging,
  FigureAnimNoNo,
  FigureAnimXXXA,
  FigureAnimDominoDying,
  FigureAnimLandDying,
  FigureAnimNothing     // this needs to be the last as it is also used to count the different animations

} FigureAnimationState;

// these are the return values for performAnimation. When they are signalled is given in the comment
typedef enum
{
  LS_undecided,      // still all things open, level undecided
  LS_solved,         // successfully and in time solved,         when exit door has closed and figure is out
  LS_solvedTime,     // solved but not in time,                  when exit door has closed and figure is out
  LS_died,           // the figure has died                      a while after the figure died
  LS_crashes,        // something crashed                        and level has become quiet (no domino action)
  LS_triggerNotFlat, // the trigger is not flat on the ground    and level has become quiet
  LS_triggerNotLast, // the trigger was not the last to fall     and level has become quiet
  LS_someLeft,       // some dominoes are left standing, no push left and level ha sbecome quiet
  LS_carrying        // you are still holding a domino           and level has become quiet

} LevelState;

class figure_c {

  private:

    // The current state of the figure. The state is the current action
    // Most stated do have their own animations, but some dont' in that
    FigureAnimationState state;

    // The animation currently played, normally the animation and state
    // is identical, but some states do share animations, this is why we
    // do have this separated
    FigureAnimationState animation;

    // The current image of the current animation
    unsigned int animationImage;

    // The domino that the figure currently carries
    DominoType carriedDomino;

    // this contains the number of ticks that have
    // to pass before the next animation image of
    // the current anmation is played
    unsigned int animationTimer;

    // current position of the figure
    int16_t blockX, blockY;

    // the level the figure acts on
    levelPlayer_c & level;

    // how many ticks the figure has been inactive
    unsigned int inactiveTimer;

    // how high have we falln
    unsigned int fallingHight;

    // current looking direction
    signed int direction;

    // in PushLeft or PushRight we only have the start of the push
    // animation, after that it branches off depending on the domino
    // pushed. This variable contains the number of ticks until
    // the 2nd part of the push animation is played
    unsigned int pushDelay;
    // this contains the 2nd part of the push animation
    FigureAnimationState pushAnimation;

    // has the final jubilation animation been played?
    bool finalAnimationPlayed;
    bool downChecker, upChecker;

    // number of pushed left, normally 1 or 0
    int numPushsLeft;

    // if the fail or success of the level has been decided
    // it is shown in these 2 variables
    bool levelFail, levelSuccess;

    // set, when the trigger is falln, but something else still moving
    bool triggerNotLast;

  public:

    // initialize the figure state for level entering
    // the level is saved and used later on for dirty block
    // marking, and level modification
    figure_c(levelPlayer_c & level);

    void initForLevel(void);

    // do one animation step for the figure
    LevelState performAnimation(unsigned int keyMask);

    bool isLiving(void) { return state != FigureAnimDominoDying && state != FigureAnimLandDying && state != FigureAnimGhost2; }

    bool isVisible(void) const;

    int16_t getBlockX(void) const { return blockX; }
    int16_t getBlockY(void) const { return blockY; }
    DominoType getCarriedDomino(void) const { return carriedDomino; }
    FigureAnimationState getAnimation(void) const { return animation; }
    unsigned int getAnimationImage(void) const { return animationImage; }

    static uint16_t getFigureImages(FigureAnimationState figure);

  private:

    FigureAnimationState callStateFunction(unsigned int state, unsigned int keyMask);
    bool animateFigure(unsigned int delay);


    FigureAnimationState SFLeaveDoor(void);
    FigureAnimationState SFStepAside(void);
    FigureAnimationState SFWalkLeft(void);
    FigureAnimationState SFWalkRight(void);
    FigureAnimationState SFJumpUpLeft(void);
    FigureAnimationState SFJumpUpRight(void);
    FigureAnimationState SFJumpDownLeft(void);
    FigureAnimationState SFJumpDownRight(void);
    FigureAnimationState SFInFrontOfExploder(void);
    FigureAnimationState SFInactive(void);
    FigureAnimationState SFLazying(void);
    FigureAnimationState SFFlailing(void);
    FigureAnimationState SFStartFallingLeft(void);
    FigureAnimationState SFStartFallingRight(void);
    FigureAnimationState SFFalling(void);
    FigureAnimationState SFLanding(void);
    FigureAnimationState SFLadder1(void);
    FigureAnimationState SFLadder2(void);
    FigureAnimationState SFLadder3(void);
    FigureAnimationState SFPullOutLeft(void);
    FigureAnimationState SFPullOutRight(void);
    FigureAnimationState SFPushInLeft(void);
    FigureAnimationState SFPushInRight(void);
    FigureAnimationState SFLeaveLadderRight(void);
    FigureAnimationState SFLeaveLadderLeft(void);
    FigureAnimationState SFEnterLadder(void);
    FigureAnimationState SFLooseRight(void);
    FigureAnimationState SFLooseLeft(void);
    FigureAnimationState SFStepOutForLoosingDomino(void);
    FigureAnimationState SFEnterDominoesLeft(void);
    FigureAnimationState SFEnterDominoesRight(void);
    FigureAnimationState SFPushLeft(void);
    FigureAnimationState SFPushRight(void);
    FigureAnimationState SFPushSpecialLeft(void);
    FigureAnimationState SFPushSpecialRight(void);
    FigureAnimationState SFPushDelayLeft(void);
    FigureAnimationState SFPushDelayRight(void);
    FigureAnimationState SFGhost1(void);
    FigureAnimationState SFGhost2(void);
    FigureAnimationState SFLandDying(void);
    FigureAnimationState SFEnterDoor(void);
    FigureAnimationState SFStepBackForDoor(void);
    FigureAnimationState SFNoNo(void);
    FigureAnimationState SFVictory(void);
    FigureAnimationState SFShrugging(void);
    FigureAnimationState SFStruck(void);


    FigureAnimationState SFNextAction(unsigned int keyMask);

    FigureAnimationState checkForNoKeyActions(void);
    bool CanPlaceDomino(int x, int y, int ofs);
    bool PushableDomino(int x, int y, int ofs);
};

// get the keymask for the figure movements
unsigned int getKeyMask(void);


#endif

