#include "splashes.h"
#include <cmath>

Splashes::Splashes(Game_Data *gd, QObject *parent) :
    QObject(parent)
{
    gameData = gd;
    nextId = 1;
}


void Splashes::addSplash(int player, srcSplashType *src, QPointF pos, QPointF dest, int aim)
{
    SplashType spl;
    spl.id = nextId;
    spl.towerId = QPoint(-1,-1);
    spl.srcTow = 0;
    spl.PlayerId = player;
    spl.AlienId = 0;
    spl.pos = pos;
    spl.destination = dest;
    spl.AlienId = aim;
    spl.angle = 180.0f*(- std::atan2(spl.pos.x() - spl.destination.x(),spl.pos.y() - spl.destination.y()))/M_PI;
    spl.life = 0;
    spl.src = src;
    addSplash(&spl);
}


void Splashes::addSplash(const TowerType &tower)
{
    SplashType spl;
    spl.src = &(tower.src->splashes[0]);
    spl.id = nextId;
    spl.towerId = tower.pos;
    spl.PlayerId = tower.PlayerId;
    spl.AlienId = tower.aim;
    spl.srcTow = tower.src;
    spl.pos = QPointF(tower.pos);
    if (gameData->aliens->curAliens.contains(spl.AlienId))
        spl.destination = gameData->aliens->curAliens.value(spl.AlienId).pos+QPointF(0.5,0.5);
    else {qCritical("aim alien not exists(!)");return;}
    spl.angle = 180.0f*(- std::atan2(spl.pos.x() - spl.destination.x(),spl.pos.y() - spl.destination.y()))/M_PI;
    spl.life = 0;
    addSplash(&spl);
}


void Splashes::addSplash(srcTowerType *srctower, int id, QPointF pos, QPointF dest, int aim, QPoint towerId)
{
    SplashType spl;
    if (srctower != 0)
        spl.src = &(srctower->splashes[id]);
    else spl.src = &(gameData->srcSplashes[id]);
    spl.id = nextId;
    spl.towerId = towerId;
    //qDebug() << "player"<< srctower->PlayerId;
    spl.PlayerId = gameData->curTowers[towerId].PlayerId;
    spl.AlienId = aim;
    spl.pos = pos;
    spl.destination = dest;
    spl.angle = 180.0f*(- std::atan2(spl.pos.x() - spl.destination.x(),spl.pos.y() - spl.destination.y()))/M_PI;
    spl.life = 0;
    spl.srcTow = srctower;
    addSplash(&spl);
}


void Splashes::addSplash(SplashType * spl)
{
    srcTriggerType * trig;
    for(int i=0; i<spl->src->triggerIndexes.size(); i++)
    {
        TriggerOnDest tod;
        TriggerOnAlien toa;
        TriggerOnTimer tot;
        if (spl->srcTow != 0)
            trig = &(spl->srcTow->triggers[spl->src->triggerIndexes.at(i)]);
        else {qDebug("no parent tower"); trig = &(gameData->srcTriggers[spl->src->triggerIndexes.at(i)]);}
        switch (trig->type)
        {
        case srcTriggerType::onDestination :
            tod.src = trig;
            spl->trigsOnDest.append(tod);
            break;
        case srcTriggerType::onAlienInRadius :
            toa.src = trig;
            spl->trigsOnAlien.append(toa);
            break;
        case srcTriggerType::onTimer :
            tot.src = trig;
            tot.timer = 0;
            spl->trigsOnTimer.append(tot);
            break;
        }

    }
    spl->item = new ADItem(spl->id, ADItem::Splash, spl->src->images);
    spl->item->setPos(spl->pos*cellSize);
    spl->item->setRotation(spl->angle);
    spl->item->setZValue(spl->src->ZValue);
    spl->item->setCompositionMode(spl->src->compositionMode);
    scene->addItem(spl->item);
    gameData->curSplashes.insert(spl->id,*spl);
    nextId++;
}


void Splashes::update()
{
    QHash<int,int> deadIndexes;
    for (QHash<int, SplashType>::iterator
         spl = gameData->curSplashes.begin();
         spl != gameData->curSplashes.end(); ++spl)
    {
        spl->life++;
        if (spl->life > spl->src->lifetime)
            deadIndexes.insert(spl->id,spl->id);
        if (!spl->trigsOnTimer.isEmpty())
            doTriggerOnTimer(spl,&deadIndexes);
        if (!spl->trigsOnAlien.isEmpty())
            doTriggerOnAlien(spl,&deadIndexes);
        float arctg;
        float speed = spl->src->speed;
        float speed2 = speed*speed;
        bool keep = spl->src->keepOnAlien;
        if (spl->src->keepOnAlien)
        {
//            qDebug() << "keep on alien start";
            if (gameData->aliens->curAliens.contains(spl->AlienId))
            {
                spl->pos = gameData->aliens->curAliens[spl->AlienId].pos;
                spl->angle = gameData->aliens->curAliens[spl->AlienId].angle;
            } else deadIndexes.insert(spl->id,spl->id);
//            qDebug() << "keep on alien done";
        } else {
            if (spl->src->autoControl && spl->AlienId != -1)
            {
                bool badAl = false;
                if (gameData->aliens->curAliens.contains(spl->AlienId))
                {
                    spl->destination = gameData->aliens->curAliens.value(spl->AlienId).pos+QPointF(0.5,0.5);
                } else {
                    badAl = true;
                    spl->AlienId = -1;
                    keep = false;
                }
                if (distance2(spl->pos, spl->destination) < speed2)
                {
                    if (badAl)
                        deadIndexes.insert(spl->id,spl->id);
                    spl->pos = spl->destination;
                    doTriggerOnDest(spl,&deadIndexes,badAl);
                }
                arctg = std::atan2(spl->pos.x() - spl->destination.x(), spl->pos.y() - spl->destination.y());
                spl->angle = 180.0f*(-arctg)/M_PI;
            } else {
                if (!gameData->map->rect().contains(spl->pos.toPoint()))
                    deadIndexes.insert(spl->id,spl->id);
                if (!spl->trigsOnDest.isEmpty())
                {
                    if (distance2(spl->pos, spl->destination) < speed2)
                    {
                        spl->pos = spl->destination;
                        doTriggerOnDest(spl,&deadIndexes,true);
                    }
                }
                arctg = -spl->angle*M_PI/180.f;
            }
            spl->pos.rx() -= speed*std::sin(arctg);
            spl->pos.ry() -= speed*std::cos(arctg);
        }
        /// TODO: smooth splash rotate
        spl->item->setPos(spl->pos*cellSize);
        spl->item->setRotation(spl->angle);
        spl->item->next(spl->src->animSpeed);
    }
    for (QHash<int,int>::iterator j = deadIndexes.begin();
         j != deadIndexes.end(); ++j)
        delSplash(*j);
    for (QHash<int,DeadAlienIndex>::iterator j = dead.begin();
         j != dead.end(); ++j)
    {
//        qDebug() << j->tower;
        gameData->curTowers[j->tower].experience += gameData->curTowers[j->tower].src->expByKill;
//        qDebug() << "emit1";
        emit killAlien(j->player,j->id);
//        qDebug() << "emit2";
    }
    dead.clear();
//    qDebug() << "finish";
}


void Splashes::delSplash(int Id)
{
    scene->removeItem(gameData->curSplashes[Id].item);
    delete gameData->curSplashes[Id].item;
    gameData->curSplashes.remove(Id);
}


/// FIXME : correct onTrigTimer()
void Splashes::doTriggerOnTimer(QHash<int, SplashType>::iterator
                                spl, QHash<int,int> *deadIndexes)
{
    for(QList<TriggerOnTimer>::iterator i = spl->trigsOnTimer.begin();
        i != spl->trigsOnTimer.end(); ++i)
    {
//        qDebug() << "timer trigger start";
        srcTriggerType * strig = i->src;
        i->timer++;
        if (i->timer >= strig->timer)
        {
            /// TODO : add damage and other effects
            float dmg = strig->damage;
            float rad = strig->radius;
            if (dmg > 0)
            {
                float hh;
                if (!(rad > 0))
                {
                    if (gameData->aliens->curAliens.contains(spl->AlienId))
                    {
                        hh = gameData->aliens->curAliens[spl->AlienId].health -= dmg;
//                        qDebug() << "shot from" << spl->towerId << spl->AlienId;
                        if (hh <= 0) dead.insert(spl->AlienId, DeadAlienIndex(spl->AlienId, spl->PlayerId, spl->towerId));
                        else gameData->curTowers[spl->towerId].experience += hh * gameData->curTowers[spl->towerId].src->expByDamage;
                    } else {
                        deadIndexes->insert(spl->id,spl->id);
                    }
                } else {
                    for (QHash<int, AlienType>::iterator
                         a = gameData->aliens->curAliens.begin();
                         a != gameData->aliens->curAliens.end(); ++a)
                    {
                        if (distance2(spl->pos, a->pos) < rad*rad)
                        {
                            hh = a->health -= dmg;
                            if (hh <= 0) dead.insert(a->id, DeadAlienIndex(a->id, spl->PlayerId, spl->towerId));
                            else gameData->curTowers[spl->towerId].experience += hh * gameData->curTowers[spl->towerId].src->expByDamage;
                        }
                    }
                }
            }
            i->timer = 0;
            if (strig->delParent) deadIndexes->insert(spl->id,spl->id);
            if (strig->count > 0 && strig->childId > 0)
            {
                int aim;
                switch(strig->childAim)
                {
                /// TODO : othet aim types
                case srcTriggerType::parentAim : aim = spl->AlienId; break;
                case srcTriggerType::noAim : aim = -1; break;
                default : aim = -1;
                }
                for (int j=0; j<strig->count; ++j)
                {
                    /// TODO: randomRadiusPos
                    addSplash(spl->srcTow, strig->childId, spl->pos, spl->destination, aim, spl->towerId);
                }
            }
        }
//        qDebug() << "timer trigger stop";
    }
}


void Splashes::doTriggerOnDest(QHash<int, SplashType>::iterator
                               spl, QHash<int,int> *deadIndexes,
                               bool badAl)
{
    foreach(TriggerOnDest t, spl->trigsOnDest)
    {
        /// TODO: different aims for childs
        if (t.src->delParent) deadIndexes->insert(spl->id,spl->id);
        int aim;
        switch(t.src->childAim)
        {
        case srcTriggerType::parentAim :
            aim = spl->AlienId;
            break;
            /// TODO : other aim types
        case srcTriggerType::nearestAlien :

        case srcTriggerType::noAim :
        default : aim = -1;
        }
        if (t.src->count > 0 && t.src->childId > 0)
        {
            for (int j=0; j<t.src->count; ++j)
            {
                /// TODO: randomRadiusPos
                addSplash(spl->srcTow,t.src->childId,
                          spl->pos,spl->destination,aim,spl->towerId);
            }
        }
        float dmg = t.src->damage;
        float rad = t.src->radius;
        if (dmg > 0)
        {
            float hh;
            if (!(rad > 0))
            {
                if (!badAl)
                {
                    hh = gameData->aliens->curAliens[spl->AlienId].health -= dmg;
                    if (hh <= 0) dead.insert(spl->AlienId, DeadAlienIndex(spl->AlienId, spl->PlayerId, spl->towerId));
                    else gameData->curTowers[spl->towerId].experience += hh * gameData->curTowers[spl->towerId].src->expByDamage;
                }
            } else {
                for (QHash<int, AlienType>::iterator
                     i = gameData->aliens->curAliens.begin();
                     i != gameData->aliens->curAliens.end(); ++i)
                {
                    if (distance2(spl->pos, i->pos) < rad*rad)
                    {
                        hh = i->health -= dmg;
                        if (hh <= 0) dead.insert(i->id, DeadAlienIndex(i->id, spl->PlayerId, spl->towerId));
                        else gameData->curTowers[spl->towerId].experience += hh * gameData->curTowers[spl->towerId].src->expByDamage;
                    }
                }
            }
        }
    }
}


void Splashes::doTriggerOnAlien(QHash<int, SplashType>::iterator spl, QHash<int, int> *deadIndexes)
{
    for (int n=0; n<spl->trigsOnAlien.size(); n++)
    {
        TriggerOnAlien * t = &spl->trigsOnAlien[n];
        /// TODO: different aims for childs
        float dmg = t->src->damage;
        float rad = t->src->radius;
        if (dmg > 0 && rad > 0)
        {
            float hh;
            for (QHash<int, AlienType>::iterator
                 i = gameData->aliens->curAliens.begin();
                 i != gameData->aliens->curAliens.end(); ++i)
            {
                if (distance2(spl->pos, i->pos) < rad*rad && !t->triggerAliens.contains(i->id))
                {
                    if (t->src->delParent) deadIndexes->insert(spl->id,spl->id);
                    hh = i->health -= dmg;
                    if (hh <= 0) dead.insert(i->id, DeadAlienIndex(i->id, spl->PlayerId, spl->towerId));
                    else gameData->curTowers[spl->towerId].experience += hh * gameData->curTowers[spl->towerId].src->expByDamage;
                    if (t->src->count > 0 && t->src->childId > 0)
                    {
//                        if (!t.triggerAliens.contains(i->id))
//                        {
                            for (int j=0; j<t->src->count; ++j)
                            {
                                /// TODO: randomRadiusPos
                                addSplash(spl->srcTow,t->src->childId,
                                          spl->pos,i->pos,i->id,spl->towerId);
                            }
//                        }
                    }
                    t->triggerAliens.append(i->id);
                }
            }
        }
    }
}
