/***************************************************************************
 *   Copyright (C) 1999-2005 Trolltech AS                                  *
 *   Copyright (C) 2006 David Saxton <david@bluehaze.org>                  *
 *                                                                         *
 *   This file may be distributed and/or modified under the terms of the   *
 *   GNU General Public License version 2 as published by the Free         *
 *   Software Foundation                                                   *
 ***************************************************************************/

#ifndef KTL_CANVASITEMS_H_
#define KTL_CANVASITEMS_H_

#include "canvasitemlist.h"

#include <QBrush>
#include <QObject>
#include <QPen>
#include <QPolygon>
#include <QRect>

class QPainter;

class KtlQCanvasPolygonalItem;
class KtlQCanvasRectangle;
class KtlQCanvasPolygon;
class KtlQCanvasEllipse;
class KtlQCanvasLine;
class KtlQCanvasChunk;
class KtlQCanvas;
class KtlQCanvasItem;
class KtlQCanvasView;

class KtlQCanvasItemExtra;

class KtlQCanvasItem : public QObject
{
    Q_OBJECT
public:
    KtlQCanvasItem(KtlQCanvas *canvas);
    ~KtlQCanvasItem() override;

    double x() const
    {
        return myx;
    }
    double y() const
    {
        return myy;
    }
    double z() const
    {
        return myz;
    } // (depth)

    virtual void moveBy(double const dx, double const dy);
    void move(double const x, double const y);
    void setX(double a)
    {
        move(a, y());
    }
    void setY(double a)
    {
        move(x(), a);
    }
    void setZ(double a);

    virtual bool collidesWith(const KtlQCanvasItem *) const = 0;

    KtlQCanvasItemList collisions(const bool exact /* NO DEFAULT */) const;

    virtual void setCanvas(KtlQCanvas *);

    virtual void draw(QPainter &) = 0;

    void show();
    void hide();

    virtual void setVisible(bool yes);
    bool isVisible() const
    {
        return vis;
    }
    virtual void setSelected(const bool yes);
    bool isSelected() const
    {
        return sel;
    }

    virtual QRect boundingRect() const = 0;

    KtlQCanvas *canvas() const
    {
        return cnv;
    }

    virtual bool collidesWith(const KtlQCanvasPolygonalItem *, const KtlQCanvasRectangle *, const KtlQCanvasEllipse *) const = 0;

    bool needRedraw() const
    {
        return m_bNeedRedraw;
    }
    void setNeedRedraw(const bool needRedraw)
    {
        m_bNeedRedraw = needRedraw;
    }

protected:
    void update()
    {
        changeChunks();
    }

    virtual QPolygon chunks() const;
    virtual void addToChunks();
    virtual void removeFromChunks();
    virtual void changeChunks();

    bool val;
    double myx, myy, myz;

private:
    KtlQCanvas *cnv;
    static KtlQCanvas *current_canvas;
    KtlQCanvasItemExtra *ext;
    KtlQCanvasItemExtra &extra();
    bool m_bNeedRedraw;
    bool vis;
    bool sel;
};

class KtlQPolygonalProcessor;

class KtlQCanvasPolygonalItem : public KtlQCanvasItem
{
public:
    KtlQCanvasPolygonalItem(KtlQCanvas *canvas);
    ~KtlQCanvasPolygonalItem() override;

    bool collidesWith(const KtlQCanvasItem *) const override;

    virtual void setPen(const QPen &p);
    virtual void setBrush(const QBrush &b);

    QPen pen() const
    {
        return pn;
    }
    QBrush brush() const
    {
        return br;
    }

    virtual QPolygon areaPoints() const = 0;
    QRect boundingRect() const override;

protected:
    void draw(QPainter &) override;
    virtual void drawShape(QPainter &) = 0;

    bool winding() const;
    void setWinding(bool);

    void invalidate();
    bool isValid() const
    {
        return val;
    }

private:
    void scanPolygon(const QPolygon &pa, int winding, KtlQPolygonalProcessor &process) const;

    QPolygon chunks() const override;

    bool collidesWith(const KtlQCanvasPolygonalItem *, const KtlQCanvasRectangle *, const KtlQCanvasEllipse *) const override;

    QBrush br;
    QPen pn;
    bool wind;
};

class KtlQCanvasRectangle : public KtlQCanvasPolygonalItem
{
public:
    KtlQCanvasRectangle(KtlQCanvas *canvas);
    KtlQCanvasRectangle(const QRect &, KtlQCanvas *canvas);
    KtlQCanvasRectangle(int x, int y, int width, int height, KtlQCanvas *canvas);

    ~KtlQCanvasRectangle() override;

    int width() const;
    int height() const;
    void setSize(const int w, const int h);
    QSize size() const
    {
        return QSize(w, h);
    }
    QPolygon areaPoints() const override;
    QRect rect() const
    {
        return QRect(int(x()), int(y()), w, h);
    }

    bool collidesWith(const KtlQCanvasItem *) const override;

protected:
    void drawShape(QPainter &) override;
    QPolygon chunks() const override;

private:
    bool collidesWith(const KtlQCanvasPolygonalItem *, const KtlQCanvasRectangle *, const KtlQCanvasEllipse *) const override;

    int w, h;
};

class KtlQCanvasPolygon : public KtlQCanvasPolygonalItem
{
public:
    KtlQCanvasPolygon(KtlQCanvas *canvas);
    ~KtlQCanvasPolygon() override;
    void setPoints(QPolygon);
    QPolygon points() const;
    void moveBy(double dx, double dy) override;

    QPolygon areaPoints() const override;

protected:
    void drawShape(QPainter &) override;
    // TODO FIXME guarts are added for debugging memory corruption (poly takes non-pointer values)
    int guardBef[10];
    QPolygon *poly;
    int guardAft[10];
};

class KtlQCanvasLine : public KtlQCanvasPolygonalItem
{
public:
    KtlQCanvasLine(KtlQCanvas *canvas);
    ~KtlQCanvasLine() override;
    void setPoints(int x1, int y1, int x2, int y2);

    QPoint startPoint() const
    {
        return QPoint(x1, y1);
    }
    QPoint endPoint() const
    {
        return QPoint(x2, y2);
    }

    void setPen(const QPen &p) override;
    void moveBy(double dx, double dy) override;

protected:
    void drawShape(QPainter &) override;
    QPolygon areaPoints() const override;

private:
    int x1, y1, x2, y2;
};

class KtlQCanvasEllipse : public KtlQCanvasPolygonalItem
{
public:
    KtlQCanvasEllipse(KtlQCanvas *canvas);
    KtlQCanvasEllipse(int width, int height, KtlQCanvas *canvas);
    KtlQCanvasEllipse(int width, int height, int startangle, int angle, KtlQCanvas *canvas);

    ~KtlQCanvasEllipse() override;

    int width() const;
    int height() const;
    void setSize(int w, int h);
    void setAngles(int start, int length);
    int angleStart() const
    {
        return a1;
    }
    int angleLength() const
    {
        return a2;
    }
    QPolygon areaPoints() const override;

    bool collidesWith(const KtlQCanvasItem *) const override;

protected:
    void drawShape(QPainter &) override;

private:
    bool collidesWith(const KtlQCanvasPolygonalItem *, const KtlQCanvasRectangle *, const KtlQCanvasEllipse *) const override;
    int w, h;
    int a1, a2;
};

#endif // KTL_CANVASITEMS_H_
