/* Blinker
   Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
   a blinkenarea.org project */

#ifndef BLINKER_PONG_H
#define BLINKER_PONG_H

#include <string>
#include <vector>

#include <BlinkenLib/BlinkenFrame.h>

#include "Color.h"
#include "ColorFile.h"
#include "File.h"
#include "Format.h"
#include "FormatFile.h"
#include "Game.h"
#include "Mgrs.h"
#include "Module.h"
#include "NameFile.h"
#include "OpConn.h"
#include "OpConnIf.h"
#include "OpReqIf.h"
#include "OutStreamFile.h"
#include "Time.h"
#include "TimeCallee.h"
#include "UIntFile.h"

namespace Blinker {

/// pong game
class Pong: public Game, public OpReqIf
{
public:
  /**
   * @brief constructor
   * @param[in] name module name
   * @param[in] mgrs managers
   * @param[in] dirBase base directory
   */
  Pong(const std::string &name, Mgrs &mgrs, const Directory &dirBase);

  /// virtual destructor
  virtual ~Pong();

private:
  /// copy constructor disabled
  Pong(const Pong &that);

  /// assignment operator disabled
  const Pong & operator=(const Pong &that);

public:
  /**
   * @brief check for update of configuration (derived game)
   * @param[in,out] doReinit set to true to ask for reinitialization
   * @param[in,out] doRedraw set to true to ask for redrawing
   */
  virtual void updateConfigGame(bool &doReinit, bool &doRedraw);

  /**
   * @brief check if accepting new operator connection is possible
   * @param[in] name operator interface name
   * @return if accepting new connection is possible
   */
  virtual bool acceptNewOpConn(const std::string &name);

  /**
   * @brief new operator connection
   * @param[in] name operator interface name
   * @param[in] pConn operator connection object
   *
   * The new connection may not yet be used for sending inside this callback.
   */
  virtual void newOpConn(const std::string &name, OpConn *pConn);

  /**
   * @brief key command received on operator connection
   * @param[in] pConn operator connection object
   * @param[in] key key that was pressed
   */
  virtual void opConnRecvKey(OpConn *pConn, char key);

  /**
   * @brief play command received on operator connection
   * @param[in] pConn operator connection object
   * @param[in] sound name of sound to play
   */
  virtual void opConnRecvPlay(OpConn *pConn, const std::string &sound);

  /**
   * @brief operator connection is closed
   * @param[in] pConn operator connection object
   *
   * The connection may not be used for sending any more in this callback.
   */
  virtual void opConnClose(OpConn *pConn);

protected:
  /// re-initialize game (e.g. due to config change)
  virtual void reinitialize();

  /// redraw current game image, expected to call sendFrame() at end
  virtual void redraw();

  /// process next time step of game
  virtual void timeStep();

  /**
   * @brief process key received from phone player
   * @param[in] key received key from player
   * @param[in,out] padPosY y position of player's pad
   */
  void processKey(char key, int &padPosY);

  /**
   * @brief delay processing for computer players
   * @param[in,out] delay delay variable of computer player
   * @return whether computer player is allowed to move
   */
  bool computerDelay(int &delay) const;

  /**
   * @brief computation of ideal pad position for computer players
   * @param[in] padBallX x coordinate of position of ball when hitting the pad
   * @param[in] padY current y coordinate of pad
   * @param[out] padYmin minimum ideal y position of pad
   * @param[out] padYmax maximum ideal y position of pad
   */
  void computerComputePadPos(int padBallX, int padY,
                             int &padYmin, int &padYmax) const;

  /**
   * @brief move pad for computer players
   * @param[in] padYmin minimum desired y position of pad
   * @param[in] padYmax maximum desired y position of pad
   * @param[in,out] padPosY y position of pad
   */
  void computerMovePad(int padYmin, int padYmax, int &padPosY) const;

  /// computer player for left pad
  void computerLeft();

  /// computer player for right pad
  void computerRight();

  /// bounce ball
  void bounceBall();

  /// bounce ball at sides
  void bounceBallSide();

  /// bounce ball at left pad
  void bounceBallLeft();

  /// bounce ball at right pad
  void bounceBallRight();

  /// detect goal
  void detectGoal();

  /// set time for next time step of game - or unset if not needed
  void planTimeStep();

  /// start ball
  void startBall();

  /// game over: close player connections and deactivate
  void gameOver();

protected:

  /// descriptor for delay value
  static ValueDescr const c_delayDescr;
  /// descriptor for maximum score value
  static ValueDescr const c_maxScoreDescr;

  /// operator connection name suffix for left player's connection
  static std::string const c_opConnSuffixLeft;
  /// operator connection name suffix for right player's connection
  static std::string const c_opConnSuffixRight;

  ColorFile m_fileBallColor;     ///< color file for ball color
  ColorFile m_fileLineColor;     ///< color file for center line
  ColorFile m_filePadColor;      ///< color file for phone player pad
  ColorFile m_fileComputerColor; ///< color file for computer player pad
  ColorFile m_fileScoreColor;    ///< color file for score when ball is moving
  ColorFile m_fileGoalColor;     ///< color file for score during goal
  UIntFile  m_fileDelay;         ///< file for initial delay in ms per frame
  UIntFile  m_fileMaxScore;      ///< file for maximum score (end of game)

  NameFile m_fileLeftPlayerSound;  ///< "left player" sound name file
  NameFile m_fileRightPlayerSound; ///< "right player" sound name file
  NameFile m_fileScoreSound;       ///< "you scored" sound name file
  NameFile m_fileOtherScoreSound;  ///< "other player scored" sound name file
  NameFile m_fileWinSound;         ///< "you win" sound name file
  NameFile m_fileLoseSound;        ///< "you lose" sound name file

  ColorData    m_ballColor;     ///< ball color
  ColorData    m_lineColor;     ///< center line color
  ColorData    m_padColor;      ///< phone player pad color
  ColorData    m_computerColor; ///< computer player pad color
  ColorData    m_scoreColor;    ///< score color when ball is moving
  ColorData    m_goalColor;     ///< score color during goal
  unsigned int m_delay;         ///< initial delay in ms per frame (ball speed)
  unsigned int m_maxScore;      ///< maximum score (end of game)

  OpConn *m_pConnLeft;  ///< operator connection left player (or NULL)
  OpConn *m_pConnRight; ///< operator connection right player (or NULL)

  int m_ballPosX;   ///< ball position X
  int m_ballPosY;   ///< ball position Y
  int m_ballDirX;   ///< ball direction X
  int m_ballDirY;   ///< ball direction Y
  int m_padSize;    ///< size of player pads
  int m_leftPosY;   ///< position of top pixel of left pad
  int m_rightPosY;  ///< position of top pixel of right pad
  int m_leftDelay;  ///< delay for computer moving left pad
  int m_rightDelay; ///< delay for computer moving right pad
  int m_goalDelay;  ///< delay after goal (blinking ball)
  int m_bounceCnt;  ///< how often the current ball bounced at pad
  int m_scoreLeft;  ///< score of left player
  int m_scoreRight; ///< score of right player
}; // class Pong

} // namespace Blinker

#endif // #ifndef BLINKER_PONG_H

