/*
    This file is part of the KDE games library
    SPDX-FileCopyrightText: 2001 Martin Heni <kde at heni-online.de>
    SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de>

    SPDX-License-Identifier: LGPL-2.0-only
*/

#ifndef __KPLAYER_H_
#define __KPLAYER_H_

// own
#include "kdegamesprivate_export.h"
// Qt
#include <QList>
#include <QObject>
// Std
#include <memory>

class KGame;
class KGameIO;
class KGamePropertyBase;
class KGamePropertyHandler;

class KPlayerPrivate;

/**
 * \class KPlayer kplayer.h <KPlayer>
 *
 * @short Base class for a game player
 *
 * The KPlayer class is the central player object. It holds
 * information about the player and is responsible for any
 * input the player does. For this arbitrary many KGameIO
 * modules can be plugged into it. Main features are:
 * - Handling of IO devices
 * - load/save (mostly handled by KGamePropertyHandler)
 * - Turn handling (turn based, asynchronous)
 *
 * A KPlayer depends on a KGame object. Call KGame::addPlayer() to plug
 * a KPlayer into a KGame object. Note that you cannot do much with a
 * KPlayer object before it has been plugged into a KGame. This is because
 * most properties of KPlayer are KGameProperty which need to send messages
 * through a KGame object to be changed.
 *
 * A KGameIO represents the input methods of a player and you should make all
 * player inputs through it. So call something like playerInput->move(4);
 * instead which should call KGameIO::sendInput() to actually move. This way
 * you gain a *very* big advantage: you can exchange a KGameIO whenever you
 * want! You can e.g. remove the KGameIO of a local (human) player and just
 * replace it by a computerIO on the fly! So from that point on all playerInputs
 * are done by the computerIO instead of the human player. You also can replace
 * all network players by computer players when the network connection is broken
 * or a player wants to quit.
 * So remember: use KGameIO whenever possible! A KPlayer should just
 * contain all data of the player (KGameIO must not!) and several common
 * functions which are shared by all of your KGameIOs.
 */
class KDEGAMESPRIVATE_EXPORT KPlayer : public QObject
{
    Q_OBJECT

public:
    typedef QList<KGameIO *> KGameIOList;

    // explicit KPlayer(KGame *,KGameIO * input=0);
    /**
     * Create a new player object. It will be automatically
     * deleted if the game it belongs to is deleted.
     */
    explicit KPlayer();

    ~KPlayer() override;

    /**
     * The identification of the player. Overwrite this in
     * classes inheriting KPlayer to run time identify them.
     *
     * @return 0 for default KPlayer.
     */
    virtual int rtti() const;

    /**
     * Gives debug output of the game status
     */
    void Debug();

    // properties
    /**
     * Returns a list of input devices
     *
     * @return list of devices
     */
    KGameIOList *ioList();

    /**
     * sets the game the player belongs to. This
     * is usually automatically done when adding a
     * player
     *
     * @param game the game
     */
    void setGame(KGame *game);

    /**
     * Query to which game the player belongs to
     *
     * @return the game
     */
    KGame *game() const;

    /**
     * Set whether this player can make turns/input
     * all the time (true) or only when it is its
     * turn (false) as it is used in turn based games
     *
     * @param a async=true turn based=false
     */
    void setAsyncInput(bool a);

    /**
     * Query whether this player does asynchronous
     * input
     *
     * @return true/false
     */
    bool asyncInput() const;

    /**
     * Is this player a virtual player, i.e. is it
     * created by mirroring a real player from another
     * network game. This mirroring is done automatically
     * as soon as a network connection is build and it affects
     * all players regardless what type
     *
     * @return true/false
     */
    bool isVirtual() const;

    /**
     * @internal
     * Sets whether this player is virtual. This is internally
     * called
     *
     * @param v virtual true/false
     */
    void setVirtual(bool v);

    /**
     * Is this player an active player. An player is usually
     * inactivated if it is replaced by a network connection.
     * But this could also be called manually
     *
     * @return true/false
     */
    bool isActive() const;

    /**
     * Set an player as active (true) or inactive (false)
     *
     * @param v true=active, false=inactive
     */
    void setActive(bool v);

    /**
     * Returns the id of the player
     *
     * @return the player id
     */
    quint32 id() const;

    /* Set the players id. This is done automatically by
     * the game object when adding a new player!
     *
     * @param i the player id
     */
    void setId(quint32 i);

    /**
     * Returns the user defined id of the player
     * This value can be used arbitrary by you to
     * have some user identification for your player,
     * e.g. 0 for a white chess player, 1 for a black
     * one. This value is more reliable than the player
     * id which can even change when you make a network
     * connection.
     *
     * @return the user defined player id
     */
    int userId() const;

    /* Set the user defined players id.
     *
     * @param i the user defined player id
     */
    void setUserId(int i);

    /**
     * Returns whether this player can be replaced by a network
     * connection player. The name of this function can be
     * improved ;-) If you do not overwrite the function to
     * select what players shall play in a network the KGame
     * does an automatic selection based on the networkPriority
     * This is not a terrible important function at the moment.
     *
     * @return true/false
     */
    int networkPriority() const;

    /**
     * Set whether this player can be replaced by a network
     * player. There are to possible games. The first type
     * of game has arbitrary many players. As soon as a network
     * players connects the game runs with more players (not tagged
     * situation). The other type is e.g. games like chess which
     * require a constant player number. In a network game situation
     * you would tag one or both players of all participants. As
     * soon as the connect the tagged player will then be replaced
     * by the network partner and it is then controlled over the network.
     * On connection loss the old situation is automatically restored.
     *
     * The name of this function can be improved;-)
     *
     * @param b should this player be tagged
     */
    void setNetworkPriority(int b);

    /**
     * Returns the player which got inactivated to allow
     * this player to be set up via network. Mostly internal
     * function
     */
    KPlayer *networkPlayer() const;

    /**
     * Sets this network player replacement. Internal stuff
     */
    void setNetworkPlayer(KPlayer *p);

    // A name and group the player belongs to
    /**
     * A group the player belongs to. This
     * Can be set arbitrary by you.
     */
    void setGroup(const QString &group);

    /**
     * Query the group the player belongs to.
     */
    virtual const QString &group() const;

    /**
     * Sets the name of the player.
     * This can be chosen arbitrary.
     * @param name The player's name
     */
    void setName(const QString &name);

    /**
     * @return The name of the player.
     */
    virtual const QString &name() const;

    // set devices
    /**
     * Adds an IO device for the player. Possible KGameIO devices
     * can either be taken from the existing ones or be self written.
     * Existing are e.g. Keyboard, Mouse, Computerplayer
     *
     * @param input the inut device
     * @return true if ok
     */
    bool addGameIO(KGameIO *input);

    /**
     * remove (and delete) a game IO device
     *
     * The remove IO(s) is/are deleted by default. If
     * you do not want this set the parameter deleteit to false
     *
     * @param input the device to be removed or 0 for all devices
     * @param deleteit true (default) to delete the device otherwise just remove it
     * @return true on ok
     */
    bool removeGameIO(KGameIO *input = nullptr, bool deleteit = true);

    /**
     * Finds the KGameIO devies with the given rtti code.
     * E.g. find the mouse or network device
     *
     * @param rtti the rtti code to be searched for
     * @return the KGameIO device
     */
    KGameIO *findRttiIO(int rtti) const;

    /**
     * Checks whether this player has a IO device of the
     * given rtti type
     *
     * @param rtti the rtti typed to be checked for
     * @return true if it exists
     */
    bool hasRtti(int rtti) const;

    // Message exchange
    /**
     * Forwards input to the game object..internal use only
     *
     * This method is used by KGameIO::sendInput(). Use that function
     * instead to send player inputs!
     *
     * This function forwards a player input (see KGameIO classes) to the
     * game object, see KGame, either to KGame::sendPlayerInput() (if
     * transmit=true, ie the message has just been created) or to
     * KGame::playerInput() (if player=false, ie the message *was* sent through
     * KGame::sendPlayerInput).
     */
    virtual bool forwardInput(QDataStream &msg, bool transmit = true, quint32 sender = 0);

    /**
     * Forwards Message to the game object..internal use only
     */
    virtual bool forwardMessage(QDataStream &msg, int msgid, quint32 receiver = 0, quint32 sender = 0);

    // Game logic
    /**
     * is it my turn to go
     *
     * @return true/false
     */
    bool myTurn() const;

    /**
     * Sets whether this player is the next to turn.
     * If exclusive is given all other players are set
     * to setTurn(false) and only this player can move
     *
     * @param b true/false
     * @param exclusive true (default)/ false
     * @return should be void
     */
    bool setTurn(bool b, bool exclusive = true);

    // load/save
    /**
     * Load a saved player, from file OR network. By default all
     * KGameProperty objects in the dataHandler of this player are loaded
     * and saved when using load or save. If you need to save/load more
     * you have to replace this function (and save). You will probably
     * still want to call the default implementation additionally!
     *
     * @param stream a data stream where you can stream the player from
     *
     * @return true?
     */
    virtual bool load(QDataStream &stream);

    /**
     * Save a player to a file OR to network. See also load
     *
     * @param stream a data stream to load the player from
     *
     * @return true?
     */
    virtual bool save(QDataStream &stream);

    /**
     * Receives a message
     * @param msgid The kind of the message. See messages.txt for further
     * information
     * @param stream The message itself
     * @param sender
     */
    void networkTransmission(QDataStream &stream, int msgid, quint32 sender);

    /**
     * Searches for a property of the player given its id.
     * @param id The id of the property
     * @return The property with the specified id
     */
    KGamePropertyBase *findProperty(int id) const;

    /**
     * Adds a property to a player. You would add all
     * your player specific game data as KGameProperty and
     * they are automatically saved and exchanged over network.
     *
     * @param data The property to be added. Must have an unique id!
     * @return false if the given id is not valid (ie another property owns
     * the id) or true if the property could be added successfully
     */
    bool addProperty(KGamePropertyBase *data);

    /**
     * Calculates a checksum over the IO devices. Can be used to
     * restore the IO handlers. The value returned is the 'or'ed
     * value of the KGameIO rtti's.
     * this is internally used for saving and restoring a player.
     */
    int calcIOValue();

    /**
     * @return the property handler
     */
    KGamePropertyHandler *dataHandler();

Q_SIGNALS:
    /**
     *  The player object got a message which was targeted
     *  at it but has no default method to process it. This
     *  means probably a user message. Connecting to this signal
     *  allowed to process it.
     */
    void signalNetworkData(int msgid, const QByteArray &buffer, quint32 sender, KPlayer *me);

    /**
     * This signal is emitted if a player property changes its value and
     * the property is set to notify this change. This is an
     * important signal as you should base the actions on a reaction
     * to this property changes.
     */
    void signalPropertyChanged(KGamePropertyBase *property, KPlayer *me);

protected Q_SLOTS:
    /**
     * Called by KGameProperty only! Internal function!
     */
    void sendProperty(int msgid, QDataStream &stream, bool *sent);
    /**
     * Called by KGameProperty only! Internal function!
     */
    void emitSignal(KGamePropertyBase *me);

private:
    void init();

private:
    friend class KPlayerPrivate;
    std::unique_ptr<KPlayerPrivate> const d;

    Q_DISABLE_COPY(KPlayer)
};

#endif
