#ifndef BASE_TYPES_H
#define BASE_TYPES_H

#include <QPointF>
#include <QPoint>
#include <QList>
#include <QVector>
#include <QHash>
#include <QMultiHash>
#include <QDebug>
#include <QString>
#include <QObject>
#include <QSize>
#include <QRect>
#include <QRectF>
#include <QImage>
#include <QStringList>
#include <QPainter>
#include "aditem.h"

const float cellSize = 20.f;

struct srcAlienType;
struct srcTowerType;
struct srcSplashType;
struct srcTriggerType;

struct srcAlienType
{
    QString name;
    float speed;
    float health;
    float armor;
    float regeneration;
    bool isFlying;
    unsigned int score;
    int imgType;
    QList<QImage *> * images;
    unsigned int prise;
};


struct AlienType
{
    AlienType ()
    {
        id = -1;
        src = 0;
        pos = QPointF();
        wave = -1;
        item = 0;
    }

    int id; // uses for QHash
    int wave;
    srcAlienType * src;
    QPointF pos;
    float angle; // -180 .. 180
    float speed;  // cells per tick (speed=1 is 60cells by 1 sec for 60fps e.g.)
    float health;
    QPoint finish;
    QVector <QPointF> path;
    int pathIndex;
    ADItem * item;
};


struct srcTriggerType
{
    enum triggerType
    {
        onDestination,
        onTimer,
        onAlienInRadius
    };

    enum aimType
    {
        noAim,
        parentAim,
        allSide,
        nearestAlien
    };
    int childId; // =-1 for none birth splash (e.q. only damage)
    aimType childAim;
    bool delParent;
    float damage;
    float radius;
    triggerType type;
    unsigned int timer;
    int count;
    float randomPosRadius;
};


struct TriggerOnDest
{
    srcTriggerType * src;
};


struct TriggerOnTimer
{
    srcTriggerType * src;
    unsigned int timer;
};


struct TriggerOnAlien
{
    srcTriggerType * src;
    QList <int> triggerAliens;
};


struct MorphType
{
    srcTowerType * morph;
    float expRequired;
    unsigned int cost;
};


struct srcTowerType
{
    QString name;
    QImage * image;
    int imgType;
    //int PlayerId; // tower's owner
    unsigned int cost;
    unsigned int reload;
    unsigned int buildTime;
    float radius; // in cells
    float expByShot;
    float expByKill;
    float expByDamage;
    bool isRoot;
    QList <srcTriggerType> triggers;
    QList <srcSplashType> splashes;
    QList<MorphType> morphs;
};


struct TowerType
{
    srcTowerType * src;
    int aim; // aim alien Id
    int PlayerId; // tower's owner
    QPoint pos; // same as Id; not QPointF because tower fixed on grid
    float angle; // -180 .. 180
    unsigned int reload; // time for reload in ticks
    unsigned int build;
    float experience;
    ADItem * item;
    bool isBilding;
};


struct srcSplashType
{
    QString name;
    int imgType;
    float speed; // in cells
    int lifetime;
    bool autoControl;
    bool keepOnAlien;
    float animSpeed;
    float ZValue;
    QPainter::CompositionMode compositionMode;
    QList <int> triggerIndexes;
    QList<QImage *> * images;
};


struct SplashType
{
    int id;
    srcSplashType * src;
    srcTowerType * srcTow;
    QPoint towerId; // parent tower, (0,0) or null for non parent
    int PlayerId;
    int AlienId; // aim alien (different from tower.AlienId)
    QPointF destination;
    int life; // in ticks
    float angle; // -180 .. 180
    QPointF pos;
    QList <TriggerOnAlien> trigsOnAlien;
    QList <TriggerOnDest> trigsOnDest;
    QList <TriggerOnTimer> trigsOnTimer;
    ADItem * item;
};


struct AnimationType
{
    QList<QImage * > * images;
};


struct WaveType
{
    QList <int> types; // some types of aliens (e.g. 2 fly and 3 grount in one wave)
    QList <unsigned int> counts; // count aliens of each type
    unsigned int timeout; // time in ticks
    unsigned int prise;
};


inline uint qHash(const QPoint &pos)
{
    return qHash(pos.x() + (pos.y() << 16));
}


inline float distance2(const QPointF &p1, const QPointF &p2)
{
    return (p1.x()-p2.x())*(p1.x()-p2.x())+(p1.y()-p2.y())*(p1.y()-p2.y());
}


template<typename Type>
class _PIForeachC {
public:
    _PIForeachC(const Type & t): _t(t) {_it = _t.begin(); _break = false;}
    typename Type::value_type _var;
    typename Type::const_iterator _it;
    const Type & _t;
    bool _break, _inv;
    inline bool isEnd() {return _it == _t.end();}
    inline void operator ++() {_it++; _break = false;}
};

template<typename Type>
class _PIForeach {
public:
    _PIForeach(Type & t): _t(t) {_it = _t.begin(); _break = false;}
    typename Type::value_type _var;
    typename Type::iterator _it;
    Type & _t;
    bool _break, _inv;
    inline bool isEnd() {return _it == _t.end();}
    inline void operator ++() {_it++; _break = false;}
};

#define piForTimes(c) for(int i = 0; i < c; ++i)

#define piForeach(i,c) for(_PIForeach<typeof(c)> _for(c); !_for.isEnd(); ++_for) for(i = *_for._it; !_for._break; _for._break = true)
#define piForeachA(i,c) for(_PIForeach<typeof(c)> _for(c); !_for.isEnd(); ++_for) for(typeof(_for._var) & i(*_for._it); !_for._break; _for._break = true)
#define piForeachC(i,c) for(_PIForeachC<typeof(c)> _for(c); !_for.isEnd(); ++_for) for(const i = *_for._it; !_for._break; _for._break = true)
#define piForeachCA(i,c) for(_PIForeachC<typeof(c)> _for(c); !_for.isEnd(); ++_for) for(const typeof(_for._var) & i(*_for._it); !_for._break; _for._break = true)

#define piForeachAC piForeachCA

#endif // BASE_TYPES_H
