/*
 * gamemodel.h - Battleship game plugin
 * Copyright (C) 2014  Aleksey Andreev
 *
 * 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 2
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

#ifndef GAMEMODEL_H
#define GAMEMODEL_H

#include <QObject>
#include <QStringList>
#include <QRect>

class GameShip : public QObject
{
    Q_OBJECT
public:
    enum ShipType { ShipDirUnknown, ShipHorizontal, ShipVertical };
    GameShip(int len, const QString &digest = QString(), QObject *parent = 0);
    int length() const { return length_; }
    int position() const { return firstPos_; }
    ShipType direction() const { return direction_; }
    QString digest() const { return digest_; }
    bool isDestroyed() const { return destroyed_ ; }
    void setDirection(ShipType dir);
    void setPosition(int pos);
    void setDigest(const QString &digest);
    void setDestroyed(bool destr);
    int  nextPosition(int prev);

private:
    int      length_;
    ShipType direction_;
    int      firstPos_;
    bool     destroyed_;
    QString  digest_;

};

class GameBoard : public QObject
{
    Q_OBJECT
public:
    enum CellStatus
    {
        CellFree, CellOccupied,
        CellUnknown, CellMiss, CellHit, CellMargin
    };
    struct GameCell
    {
        CellStatus status;
        int        ship;
        QString    digest;
        QString    seed;
        GameCell(CellStatus s) : status(s), ship(-1) {}
    };
    GameBoard(QObject *parent = 0);
    void init(CellStatus s, bool genseed);
    void makeShipRandomPosition();
    void calculateCellsHash();
    bool updateCellDigest(int pos, const QString &digest);
    bool updateCell(int pos, CellStatus cs, const QString &seed);
    bool updateShipDigest(int length, const QString &digest);
    void shot(int pos);
    GameShip::ShipType shipDirection(int pos);
    int  findAndInitShip(int pos);
    QRect shipRect(int snum, bool margin) const;
    void setShipDestroy(int n, bool margin);
    const GameCell &cell(int pos) const;
    QStringList toStringList(bool covered) const;
    bool isAllDestroyed() const;

private:
    static QString genSeed(int len);
    GameShip *findShip(int length, const QString &digest);
    bool isShipPositionLegal(int shipNum);
    void fillShipMargin(int n);

private:
    QList<GameCell>   cells_;
    QList<GameShip *> ships_;

signals:
    void shipDestroyed(int snum);

};

class GameModel : public QObject
{
    Q_OBJECT
public:
    enum GameStatus
    {
        StatusNone, StatusError, StatusBoardInit,
        StatusMyTurn, StatusWaitingTurnAccept, StatusWaitingOpponent,
        StatusWin, StatusLose, StatusDraw
    };
    GameModel(QObject *parent = 0);
    void init();
    GameStatus status() const { return status_; }
    void setStatus(GameStatus s);
    void setError();
    bool initOpponentBoard(const QStringList &data);
    bool uncoverOpponentBoard(const QStringList &data);
    const GameBoard &myBoard() const { return myBoard_; }
    const GameBoard &oppBoard() const { return opBoard_; }
    void setOpponentDraw(bool draw);
    bool isOpponentDraw() const { return oppDraw_; }
    void setOpponentAcceptedDraw(bool accept);
    void opponentResign();
    void opponentTurn(int pos);
    bool handleResult();
    bool handleTurnResult(const QString &res, const QString &seed);
    QString lastShotResult() const;
    QString lastShotSeed() const;
    QStringList getUncoveredBoard() const;

private:
    GameStatus status_;
    GameBoard  myBoard_;
    GameBoard  opBoard_;
    int        lastShot_;
    bool       draw_;
    bool       oppDraw_;
    bool       myAccept_;
    bool       oppResign_;
    bool       myResign_;
    bool       destroyed_;

private slots:
    void myShipDestroyed();

public slots:
    void sendCoveredBoard();
    void localTurn(int pos);
    void setLocalDraw(bool draw);
    void localAccept();
    void localResign();

signals:
    void acceptDraw();
    void statusChanged();
    void myBoardUpdated(int x, int y, int width, int height);
    void oppBoardUpdated(int x, int y, int width, int height);
    void gameEvent(QString data);

};

#endif // GAMEMODEL_H
