#include "loader.h"

bool tbWaveOnLevel_lessThan(const tbWaveOnLevel &a, const tbWaveOnLevel &b) {return a.waveId < b.waveId;}

Loader::Loader()
{
    doc = new QDomDocument("data");
    qDebug() << createMapExample();
}


void Loader::load(QString filename)
{
    validate(filename);
    file = new QFile(filename);
    if (!file->open(QIODevice::ReadOnly))
        qFatal("No file data.xml found!");
    if (!doc->setContent(file))
    {
        file->close();
        qFatal("Error parsing data.xml");
    }
    readAnimations();
    readAliens();
    readMaps();
    readLevels();
    readSplashes();
    readTowers();
    readTriggers();
    readChains();
    //readUpgrades();
    readLevTowers();
    readMorphs();
    readWaves();
    //    readWaveParts();
    readLevWaves();

    file->close();
}


void Loader::save(QString filename)
{
    QDomDocument data("data");
    QFile cfile(filename);
    if (!cfile.open(QIODevice::WriteOnly))
        return;
    cfile.resize(0);
    QDomElement elem;
    QDomElement root = data.createElement("ADdata");
    data.appendChild(root);
    QDomElement child = data.createElement("aliens");
    root.appendChild(child);
    for (int i=0; i<aliens.size(); i++)
    {
        tbAlien al = aliens.values().at(i);
        elem = data.createElement("alien");
        child.appendChild(elem);
        elem.setAttribute("id",al.id);
        elem.setAttribute("name",al.name);
        elem.setAttribute("health",al.health);
        if (al.armor > 0)
            elem.setAttribute("armor",al.armor);
        if (al.isFlying)
            elem.setAttribute("flying","true");
        elem.setAttribute("prise",al.prise);
        if (al.regeneration > 0)
            elem.setAttribute("regeneration",al.regeneration);
        elem.setAttribute("score",al.score);
        elem.setAttribute("speed",al.speed);
        elem.setAttribute("imageType",al.imgType);
    }
    child = data.createElement("maps");
    root.appendChild(child);
    for (int i=0; i<maps.size(); i++)
    {
        tbMap map = maps.values().at(i);
        elem = data.createElement("map");
        child.appendChild(elem);
        elem.setAttribute("name",map.name);
        elem.setAttribute("maxPlayers",map.maxPlayers);
        elem.setAttribute("id",map.id);
        elem.setAttribute("imageType",map.imgType);
        elem.setAttribute("width",map.size.width());
        elem.setAttribute("height",map.size.height());
        elem.setAttribute("data",QString(map.data));
    }
    child = data.createElement("waves");
    root.appendChild(child);
    for (int i=0; i<waves.size(); i++)
    {
        tbWave w = waves.values().at(i);
        elem = data.createElement("wave");
        child.appendChild(elem);
        elem.setAttribute("id",w.id);
        elem.setAttribute("prise",w.prise);
        elem.setAttribute("timeout",w.timeout);
        QDomElement cc;
        for (int k=0; k<w.parts.size(); k++)
        {
            tbWavePart wp = w.parts.at(k);
            cc = data.createElement("wavePart");
            elem.appendChild(cc);
            cc.setAttribute("alien",wp.alienId);
            cc.setAttribute("count",wp.count);
        }
    }
    child = data.createElement("levels");
    root.appendChild(child);
    for (int i=0; i<levels.size(); i++)
    {
        tbLevel lev = levels.values().at(i);
        elem = data.createElement("level");
        child.appendChild(elem);
        elem.setAttribute("id",lev.id);
        elem.setAttribute("map",lev.mapId);
        elem.setAttribute("name",lev.name);
        elem.setAttribute("score",lev.score);
        elem.setAttribute("startMoney",lev.money);
    }
    //    child = data.createElement("waveParts");
    //    root.appendChild(child);
    //    for (int i=0; i<waveParts.size(); i++)
    //    {
    //        tbWavePart wp = waveParts.values().at(i);
    //        elem = data.createElement("wavePart");
    //        child.appendChild(elem);
    //        elem.setAttribute("wave",wp.waveId);
    //        elem.setAttribute("alien",wp.alienId);
    //        elem.setAttribute("count",wp.count);
    //    }
    child = data.createElement("wavesOnLevels");
    root.appendChild(child);
    for (int i=0; i<levWaves.size(); i++)
    {
        tbWaveOnLevel wol = levWaves.values().at(i);
        elem = data.createElement("waveOnLevel");
        child.appendChild(elem);
        elem.setAttribute("level",wol.levelId);
        elem.setAttribute("wave",wol.waveId);
    }
    child = data.createElement("splashes");
    root.appendChild(child);
    for (int i=0; i<splashes.size(); i++)
    {
        tbSplash spl = splashes.values().at(i);
        elem = data.createElement("splash");
        child.appendChild(elem);
        elem.setAttribute("name",spl.name);
        elem.setAttribute("id",spl.id);
        elem.setAttribute("imageType",spl.imgType);
        elem.setAttribute("lifetime",spl.lifetime);
        elem.setAttribute("speed",spl.speed);
        elem.setAttribute("animSpeed",spl.animSpeed);
        elem.setAttribute("ZValue",spl.ZValue);
        if (spl.autoControl)
            elem.setAttribute("autoControl","true");
        if (spl.keepOnAlien)
            elem.setAttribute("keepOnAlien","true");
        switch (spl.compositionMode)
        {
        case QPainter::RasterOp_NotSource :
            elem.setAttribute("compositionMode","NotSource");
            break;
        case QPainter::CompositionMode_Plus :
            elem.setAttribute("compositionMode","Plus");
            break;
        case QPainter::CompositionMode_Multiply :
            elem.setAttribute("compositionMode","Multiply");
            break;
        case QPainter::CompositionMode_Screen :
            elem.setAttribute("compositionMode","Screen");
            break;
        case QPainter::CompositionMode_Darken :
            elem.setAttribute("compositionMode","Darken");
            break;
        case QPainter::CompositionMode_Lighten :
            elem.setAttribute("compositionMode","Lighten");
            break;
        case QPainter::CompositionMode_ColorDodge :
            elem.setAttribute("compositionMode","ColorDodge");
            break;
        case QPainter::CompositionMode_ColorBurn :
            elem.setAttribute("compositionMode","ColorBurn");
            break;
        case QPainter::CompositionMode_HardLight :
            elem.setAttribute("compositionMode","HardLight");
            break;
        case QPainter::CompositionMode_SoftLight :
            elem.setAttribute("compositionMode","SoftLight");
            break;
        case QPainter::CompositionMode_Difference :
            elem.setAttribute("compositionMode","Difference");
            break;
        case QPainter::CompositionMode_SourceOver :
        default:
            elem.setAttribute("compositionMode","Source");
            break;
        }
    }
    child = data.createElement("towers");
    root.appendChild(child);
    for (int i=0; i<towers.size(); i++)
    {
        tbTower tw = towers.values().at(i);
        elem = data.createElement("tower");
        child.appendChild(elem);
        elem.setAttribute("name",tw.name);
        elem.setAttribute("imageType",tw.imgType);
        elem.setAttribute("reload",tw.reload);
        elem.setAttribute("buildTime",tw.buildTime);
        elem.setAttribute("cost",tw.cost);
        elem.setAttribute("radius",tw.radius);
        elem.setAttribute("splash",tw.splashId);
        elem.setAttribute("id",tw.id);
        if (tw.expByDamage > 0)
            elem.setAttribute("expByDamage",tw.expByDamage);
        if (tw.expByKill > 0)
            elem.setAttribute("expByKill",tw.expByKill);
        if (tw.expByShot > 0)
            elem.setAttribute("expByShot",tw.expByShot);
    }
    child = data.createElement("triggers");
    root.appendChild(child);
    for (int i=0; i<triggers.size(); i++)
    {
        tbTrigger trig = triggers.values().at(i);
        elem = data.createElement("trigger");
        child.appendChild(elem);
        elem.setAttribute("id",trig.id);
        elem.setAttribute("name",trig.name);
        if (trig.chance != 100)
            elem.setAttribute("chance",trig.chance);
        if (trig.count > 0)
            elem.setAttribute("count",trig.count);
        if (trig.damage > 0)
            elem.setAttribute("damage",trig.damage);
        if (trig.radius > 0)
            elem.setAttribute("radius",trig.radius);
        if (trig.delParent)
            elem.setAttribute("delParent","true");
        if (trig.randomPosRadius > 0)
            elem.setAttribute("randomPosRadius",trig.randomPosRadius);
        switch (trig.childAim)
        {
        case srcTriggerType::allSide :
            elem.setAttribute("childAim","allSide");
            break;
        case srcTriggerType::nearestAlien :
            elem.setAttribute("childAim","nearestAlien");
            break;
        case srcTriggerType::parentAim :
            elem.setAttribute("childAim","parentAim");
            break;
        default:
            elem.setAttribute("childAim","noAim");
            break;
        }
        switch (trig.type)
        {
        case srcTriggerType::onTimer :
            elem.setAttribute("type","onTimer");
            elem.setAttribute("timer",trig.timer);
            break;
        case srcTriggerType::onDestination :
            elem.setAttribute("type","onDestination");
            break;
        case srcTriggerType::onAlienInRadius :
            elem.setAttribute("type","onAlienInRadius");
            break;
        }
    }
    child = data.createElement("chains");
    root.appendChild(child);
    for (int i=0; i<chains.size(); i++)
    {
        tbChain ch = chains.values().at(i);
        elem = data.createElement("chain");
        child.appendChild(elem);
        elem.setAttribute("tower",ch.towerId);
        elem.setAttribute("trigger",ch.triggerId);
        elem.setAttribute("parent",ch.parentSplashId);
        if (ch.childSplashId > 0)
            elem.setAttribute("child",ch.childSplashId);
    }
    child = data.createElement("towersOnLevels");
    root.appendChild(child);
    for (int i=0; i<levTowers.size(); i++)
    {
        tbTowerOnLevel tol = levTowers.values().at(i);
        elem = data.createElement("towerOnLevel");
        child.appendChild(elem);
        elem.setAttribute("level",tol.levelId);
        elem.setAttribute("tower",tol.towerId);
    }
    child = data.createElement("morphs");
    root.appendChild(child);
    for (int i=0; i<morphs.size(); i++)
    {
        tbMorph m = morphs.values().at(i);
        elem = data.createElement("morph");
        child.appendChild(elem);
        elem.setAttribute("from",m.fromId);
        elem.setAttribute("to",m.toId);
        elem.setAttribute("cost",m.cost);
        elem.setAttribute("expRequired",m.expRequired);
    }
    child = data.createElement("animations");
    root.appendChild(child);
    for (int i=0; i<animations.size(); i++)
    {
        tbAnimation ta = animations.values().at(i);
        elem = data.createElement("animation");
        child.appendChild(elem);
        elem.setAttribute("id",ta.id);
        elem.setAttribute("name",ta.name);
        elem.setAttribute("pathes",ta.pathes.join(";"));
    }
    cfile.write(data.toByteArray());
    cfile.close();
    //qDebug() << data.toByteArray();
}


Game_Data * Loader::loadlevel(int id)
{
    if (!levels.contains(id))
    {
        qCritical() << tr("Error Level %1 not found!").arg(id);
        return 0;
    }
    qDebug() << tr("Loading level %1 ...").arg(levels.value(id).name);
    tbMap tbm = maps.value(levels.value(id).mapId);
    Map * map = new Map(tbm.data,tbm.size,tbm.name,tbm.maxPlayers,tbm.imgType);
    Game_Data * gd = new Game_Data(map);
    for (int i=0; i<map->maxPlayers(); i++)
    {
        Player * pl = new Player(i);
        gd->players.append(pl);
        gd->players.at(i)->money = levels.value(id).money/tbm.maxPlayers;
    }
    qDebug() << gd->players.size();
    for (int i=0; i<map->maxPlayers(); ++i)
        gd->players.push_back(new Player(i));
    QList <tbWaveOnLevel> wollist = levWaves.values(id);
    if (wollist.isEmpty())
    {
        qCritical() << tr("Error: no waves on level!");
        return 0;
    }
    tbWave tbw;
    tbWavePart tbwpt;
    //QList <tbWavePart> wplist;
    QList <int> alienIds;
    QList <int> animIds;
    qSort(wollist.begin(),wollist.end(),tbWaveOnLevel_lessThan);
    for (int i=0; i<wollist.size(); ++i)
    {
        WaveType wt;
        tbw = waves.value(wollist.at(i).waveId);
        wt.prise = tbw.prise;
        wt.timeout = tbw.timeout;
        //wplist = waveParts.values(tbw.id);
        if (tbw.parts.isEmpty())
        {
            qCritical() << tr("Error: wave %1 is empty!").arg(tbw.id);
            //return 0;
        }
        for (int j=0; j<tbw.parts.size(); j++)
        {
            tbwpt = tbw.parts.at(j);
            if (alienIds.contains(tbwpt.alienId))
            {
                wt.types.append(alienIds.indexOf(tbwpt.alienId));
            } else {
                alienIds.append(tbwpt.alienId);
                wt.types.append(alienIds.size()-1);
            }
            wt.counts.append(tbwpt.count);
        }
        gd->waves.append(wt);
        qDebug() << tr("load wave #%1 prise=%2 timeout=%3").arg(i+1).arg(wt.prise).arg(wt.timeout) << wt.types << wt.counts;
        //wplist.clear();
    }
    wollist.clear();
    for (int i=0; i<alienIds.size(); ++i)
    {
        srcAlienType salt;
        tbAlien tbal = aliens.value(alienIds.at(i));
        salt.armor = tbal.armor;
        salt.health = tbal.health;
        if (!animIds.contains(tbal.imgType))
            animIds.append(tbal.imgType);
        salt.imgType = animIds.indexOf(tbal.imgType);
        salt.isFlying = tbal.isFlying;
        salt.name = tbal.name;
        salt.prise = tbal.prise;
        salt.regeneration = tbal.regeneration;
        salt.score = tbal.score;
        salt.speed = tbal.speed;
        gd->aliens->srcAliens.append(salt);
        //qDebug() << tr("Alien %1 #%2 ").arg(salt.name).arg(i);
    }
    QList <tbTowerOnLevel> tollist = levTowers.values(id);
    tbTower tbt;
    QList < QList <MorphType> > morph_indexes;
    for (int i=0; i<tollist.size(); ++i)
    {
        srcTowerType stt;
        tbt = towers.value(tollist.at(i).towerId);
        stt.buildTime = tbt.buildTime;
        stt.cost = tbt.cost;
        stt.expByDamage = tbt.expByDamage;
        stt.expByKill = tbt.expByKill;
        stt.expByShot = tbt.expByShot;
        if (!animIds.contains(tbt.imgType))
            animIds.append(tbt.imgType);
        stt.imgType = animIds.indexOf(tbt.imgType);
        stt.name = tbt.name;
        stt.radius = tbt.radius+1;
        stt.reload = tbt.reload;
        QList <tbChain> chlist = chains.values(tbt.id);
        QList <int> trigIds;
        tbSplash tbs = splashes.value(tbt.splashId);
        srcSplashType sst;
        sst.autoControl = tbs.autoControl;
        if (!animIds.contains(tbs.imgType))
            animIds.append(tbs.imgType);
        sst.imgType = animIds.indexOf(tbs.imgType);
        sst.lifetime = tbs.lifetime;
        sst.name = tbs.name;
        sst.speed = tbs.speed;
        sst.animSpeed = tbs.animSpeed;
        sst.ZValue = tbs.ZValue;
        sst.compositionMode = tbs.compositionMode;
        sst.keepOnAlien = tbs.keepOnAlien;
        stt.splashes.append(sst);
        stt.isRoot=true;
        for (QMultiHash<int,tbMorph>::iterator m = morphs.begin();
             m !=  morphs.end(); ++m)
        {
            if (tbt.id == (*m).toId) stt.isRoot=false;
        }
        int recursiveCheck = 1;
        fillTowerChains(&animIds,&chlist,&trigIds,&stt,
                        tbt.splashId,0,&recursiveCheck);
        morph_indexes.append(QList<MorphType>());
        QList <tbMorph> morphlist = morphs.values(tbt.id);
        bool contain;
        tbTowerOnLevel j = tbTowerOnLevel();
        MorphType m;
        foreach (tbMorph tmh, morphlist)
        {
            contain = false;
            foreach(j,tollist)
            {
                if (j.towerId == tmh.toId)
                {
                    contain = true;
                    break;
                }
            }
            if (contain)
            {
                m.cost = tmh.cost;
                m.expRequired = tmh.expRequired;
                m.morph = (srcTowerType*)tollist.indexOf(j);
                morph_indexes[i].append(m);
            }
        }
        for (int k=0; k<map->maxPlayers(); ++k)
            gd->players[k]->srcTowers.append(stt);
        qDebug() << tr("Tower %1 trig=%2 splash=%3").arg(stt.name).arg(stt.triggers.size()).arg(stt.splashes.size()) << tr("chain lenght %1").arg(recursiveCheck);
    }
    for (int i=0; i<morph_indexes.size(); ++i)
    {
        piForeachA (j, morph_indexes[i])
        {
            long int index = (long int)j.morph;
            for (int k=0; k<map->maxPlayers(); ++k)
            {
                j.morph = &(gd->players[k]->srcTowers[index]);
                gd->players[k]->srcTowers[i].morphs.append(j);
            }
        }
    }
    QString morphtree;
    foreach (srcTowerType i, gd->players[0]->srcTowers)
    {
        if (i.isRoot)
            printMorphs(&i,morphtree,"");
    }
    qDebug() << morphtree + "\n";

    /// ===================================

    for (int i=0; i<animIds.size(); i++)
    {
        AnimationType anim;
        anim.images = new QList<QImage *>;
        for (int k=0; k<animations.value(animIds.at(i)).pathes.size(); k++)
            anim.images->append(new QImage(animations.value(animIds.at(i))
                                           .pathes.at(k)));
        gd->curAnimations.append(anim);
    }
    for (int i=0; i<gd->aliens->srcAliens.size(); i++)
        gd->aliens->srcAliens[i].images = gd->curAnimations.at(gd->aliens->srcAliens.at(i).imgType).images;
    for (int i=0; i<gd->players.size(); i++)
        for (int j=0; j<gd->players.at(i)->srcTowers.size(); j++)
        {
            gd->players[i]->srcTowers[j].image = gd->curAnimations.at(gd->players.at(i)->srcTowers.at(j).imgType).images->at(0);
            for (int k=0; k<gd->players.at(i)->srcTowers.at(j).splashes.size(); k++)
                gd->players[i]->srcTowers[j].splashes[k].images = gd->curAnimations.at(gd->players.at(i)->srcTowers.at(j).splashes.at(k).imgType).images;
        }
    gd->towerSelect.images = gd->alienSelect.images = 0;
    gd->blood = 0;
    for (QHash<int,tbAnimation>::iterator i = animations.begin();
         i != animations.end(); ++i)
    {
        if (i->name == "tower select")
        {
            AnimationType anim;
            anim.images = new QList<QImage *>;
            for (int k = 0; k < i->pathes.size(); k++)
                anim.images->append(new QImage(i->pathes.at(k)));
            gd->towerSelect = anim;
        }
        if (i->name == "alien select")
        {
            AnimationType anim;
            anim.images = new QList<QImage *>;
            for (int k = 0; k < i->pathes.size(); k++)
                anim.images->append(new QImage(i->pathes.at(k)));
            gd->alienSelect = anim;
        }
        if (i->name == "blood")
        {
            gd->blood = new QImage(i->pathes.at(0));
            QSize size = gd->blood->size();
            qDebug() << size;
            gd->bloodrect = QRectF(QPointF(-(size.width()/2),-(size.height()/2)),size);
        }
    }
    if (gd->blood == 0) gd->blood = new QImage();
    if (gd->towerSelect.images == 0)
    {
        AnimationType anim;
        anim.images = new QList<QImage *>;
        anim.images->append(new QImage(":/images/select.png"));
        gd->towerSelect = anim;
    }
    if (gd->alienSelect.images == 0)
    {
        AnimationType anim;
        anim.images = new QList<QImage *>;
        anim.images->append(new QImage(":/images/select.png"));
        gd->alienSelect = anim;
    }
    return gd;
}


void Loader::fillTowerChains(QList <int> * animIds, QList <tbChain> * chlist,
                             QList <int> * trigIds, srcTowerType * stt,
                             int parId, int parIndex, int * recursiveCheck)
{
    if (*recursiveCheck > 100)
    {
        qCritical("circle dependencies detected!");
        return;
    }
    for (int i=0; i<chlist->size(); i++)
    {
        if (chlist->at(i).parentSplashId == parId)
        {
            int trigid = chlist->at(i).triggerId;
            if (!(trigIds->contains(trigid)))
            {
                srcTriggerType strigt;
                tbTrigger tbtrig = triggers.value(trigid);
                strigt.count = tbtrig.count;
                strigt.damage = tbtrig.damage;
                strigt.delParent = tbtrig.delParent;
                strigt.radius = tbtrig.radius;
                if (strigt.radius <= 0 && strigt.damage>0 && !splashes.value(parId).autoControl)
                    qCritical("Wrong trigger or chain, because damage >0 radius=0 and not autocontrol.\nThis damage will be ignored!");
                strigt.randomPosRadius = tbtrig.randomPosRadius;
                strigt.timer = tbtrig.timer;
                strigt.type = tbtrig.type;
                strigt.childAim = tbtrig.childAim;
                strigt.childId = -1;
                stt->triggers.append(strigt);
                trigIds->append(trigid);
            }
            stt->splashes[parIndex].triggerIndexes.append(trigIds->indexOf(trigid));
            if (chlist->at(i).childSplashId > 0 && stt->triggers.at(trigIds->indexOf(trigid)).count > 0)
            {
                tbSplash tbs = splashes.value(chlist->at(i).childSplashId);
                srcSplashType sst;
                sst.autoControl = tbs.autoControl;
                if (!animIds->contains(tbs.imgType))
                    animIds->append(tbs.imgType);
                sst.imgType = animIds->indexOf(tbs.imgType);
                sst.lifetime = tbs.lifetime;
                sst.name = tbs.name;
                sst.speed = tbs.speed;
                sst.animSpeed = tbs.animSpeed;
                sst.ZValue = tbs.ZValue;
                sst.compositionMode = tbs.compositionMode;
                sst.keepOnAlien = tbs.keepOnAlien;
                stt->splashes.append(sst);
                stt->triggers[stt->splashes.at(parIndex).triggerIndexes.size()-1].childId = stt->splashes.size() - 1;
                *recursiveCheck += 1;
                fillTowerChains(animIds,chlist,trigIds,stt,chlist->at(i).childSplashId, stt->splashes.size()-1,recursiveCheck);
            }
        }
    }
}


QString Loader::createMapExample()
{
    QVector < QVector < int > > Cells;
    Cells.resize(32);
    for (int i=0; i<Cells.size(); i++)
    {
        Cells[i].resize(32);
        Cells[i][0]=Map::Wall;
        Cells[i][Cells[i].size()-1]=Map::Wall;
    }
    for (int i=0; i<Cells.size(); i++)
    {
        for (int j=0; j<Cells[i].size(); j++)
        {
            Cells[0][j]=Map::Wall;
            Cells[Cells.size()-1][j]=Map::Wall;
        }
    }
    for (int i=1; i<Cells.size()-1; i++)
        for (int j=1; j<Cells[i].size()-1; j++)
            Cells[i][j]=Map::Player;
    //for (int i=2; i<Cells.size()-2; i++)
    //Cells[i][Cells[i].size()/2] = Map::Wall;
    //Cells[0][Cells[0].size()/2]=Map::Start;
    //Cells[Cells.size()/2-1][0]=Map::Start;
    //Cells[Cells.size()-1][Cells[Cells.size()-1].size()/2]=Map::Finish;
    //Cells[Cells.size()/2-1][Cells[Cells.size()/2].size()-1]=Map::Finish;
    for (int i=4; i<6; i++)
    {
        Cells[0][i]=Map::Start;
        Cells[Cells.size()-1][Cells.size()-1-i]=Map::Finish;
    }
    for (int i=4; i<6; i++)
    {
        Cells[i][0]=Map::Start;
        Cells[Cells.size()-1-i][Cells.size()-1]=Map::Finish;
    }
    QByteArray ba;
    QDataStream tmps(&ba,QIODevice::ReadWrite);
    tmps << Cells;
    ba = qCompress(ba);
    ba = ba.toBase64();
    QString str(ba);
    return str;
}


void Loader::readAliens()
{
    aliens.clear();
    QDomNodeList alelems = doc->elementsByTagName("alien");
    for (int i=0; i<alelems.size(); i++)
    {
        bool OK = true;
        tbAlien al;
        QDomNamedNodeMap nm = alelems.at(i).attributes();
        al.id = nm.namedItem("id").nodeValue().toInt();
        if (aliens.contains(al.id)) qCritical("Same alien ID detected, replacing...");
        al.name = nm.namedItem("name").nodeValue();
        al.speed = nm.namedItem("speed").nodeValue().toFloat();
        al.health = nm.namedItem("health").nodeValue().toFloat();
        al.prise = nm.namedItem("prise").nodeValue().toInt();
        al.armor = nm.namedItem("armor").nodeValue().toFloat();
        al.imgType = nm.namedItem("imageType").nodeValue().toInt();
        if (!animations.contains(al.imgType))
        {
            qCritical("Invalid animation Id");
            OK = false;
        }
        al.regeneration = nm.namedItem("regeneration").nodeValue().toFloat();
        al.score = nm.namedItem("score").nodeValue().toInt();
        al.isFlying = (nm.namedItem("flying").nodeValue() == "true");
        if (OK)
        {
            aliens.insert(al.id,al);
            //qDebug() << tr("Alien %1 id=%2 health=%3 speed=%4 prise=%5 imgType=%6 armor=%7 regeneration=%8 score=%10").arg(al.name).arg(al.id).arg(al.health).arg(al.speed).arg(al.prise).arg(al.imgType).arg(al.armor).arg(al.regeneration).arg(al.score);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 aliens").arg(aliens.size());
    //qDebug("================");
}


void Loader::readWaves()
{
    waves.clear();
    QDomNodeList welems = doc->elementsByTagName("wave");
    for (int i=0; i<welems.size(); i++)
    {
        tbWave w;
        QDomNamedNodeMap nm = welems.at(i).attributes();
        w.id = nm.namedItem("id").nodeValue().toInt();
        if (waves.contains(w.id)) qCritical("Same wave ID detected, replacing...");
        w.prise = nm.namedItem("prise").nodeValue().toInt();
        w.timeout = nm.namedItem("timeout").nodeValue().toInt();
        QDomNodeList wpelems = welems.at(i).childNodes();
        for (int j=0; j<wpelems.size(); j++)
        {
            if (wpelems.at(j).nodeName() == "wavePart")
            {
                bool OK = true;
                tbWavePart wp;
                QDomNamedNodeMap nm = wpelems.at(j).attributes();
                wp.alienId = nm.namedItem("alien").nodeValue().toInt();
                if (!aliens.contains(wp.alienId))
                {
                    qCritical("invalid alien Id");
                    OK = false;
                }
                wp.count = nm.namedItem("count").nodeValue().toInt();
                if (OK)
                {
                    w.parts.append(wp);
                    //qDebug() << tr("Wave=%1 alien=%2 count=%3").arg(wp.waveId).arg(wp.alienId).arg(wp.count);
                }
            }
        }
        waves.insert(w.id,w);
        //qDebug() << tr("Wave %1 prise=%2 timeout=%3").arg(w.id).arg(w.prise).arg(w.timeout);
    }
    //qDebug("================");
    qDebug() << tr("Found %1 waves").arg(waves.size());
    //qDebug("================");
}


//void Loader::readWaveParts()
//{
//    //waveParts.clear();
//    QDomNodeList wpelems = doc->elementsByTagName("wavePart");
//    for (int i=0; i<wpelems.size(); i++)
//    {
//        bool OK = true;
//        tbWavePart wp;
//        QDomNamedNodeMap nm = wpelems.at(i).attributes();
//        wp.waveId = nm.namedItem("wave").nodeValue().toInt();
//        if (!waves.contains(wp.waveId))
//        {
//            qCritical("invalid wave Id");
//            OK = false;
//        }
//        wp.alienId = nm.namedItem("alien").nodeValue().toInt();
//        if (!aliens.contains(wp.alienId))
//        {
//            qCritical("invalid alien Id");
//            OK = false;
//        }
//        wp.count = nm.namedItem("count").nodeValue().toInt();
//        if (OK)
//        {
//            waves[wp.waveId].parts.append(wp);
//            //qDebug() << tr("Wave=%1 alien=%2 count=%3").arg(wp.waveId).arg(wp.alienId).arg(wp.count);
//        }
//    }
//    //qDebug("================");
//    //qDebug() << tr("Found %1 waveParts").arg(waveParts.size());
//    //qDebug("================");
//}


void Loader::readChains()
{
    chains.clear();
    QDomNodeList celems = doc->elementsByTagName("chain");
    for (int i=0; i<celems.size(); i++)
    {
        bool OK = true;
        tbChain chain;
        QDomNamedNodeMap nm = celems.at(i).attributes();
        chain.towerId = nm.namedItem("tower").nodeValue().toInt();
        if (!towers.contains(chain.towerId))
        {
            qCritical("invalid tower Id");
            OK = false;
        }
        chain.childSplashId = nm.namedItem("child").nodeValue().toInt();
        if (chain.childSplashId > 0)
        {
            if (!splashes.contains(chain.childSplashId))
            {
                qCritical("invalid child splash Id");
                OK = false;
            }
        }
        chain.parentSplashId = nm.namedItem("parent").nodeValue().toInt();
        if (!splashes.contains(chain.parentSplashId))
        {
            qCritical("invalid parent splash Id");
            OK = false;
        }
        chain.triggerId = nm.namedItem("trigger").nodeValue().toInt();
        if (!triggers.contains(chain.triggerId))
        {
            qCritical("invalid parent splash Id");
            OK = false;
        }
        if (OK)
        {
            chains.insert(chain.towerId,chain);
            //qDebug() << tr("Tower=%1 parent=%2 child=%3 trigger=%4").arg(chain.towerId).arg(chain.parentSplashId).arg(chain.childSplashId).arg(chain.triggerId);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 chains").arg(chains.size());
    //qDebug("================");
}


void Loader::readMorphs()
{
    morphs.clear();
    QDomNodeList celems = doc->elementsByTagName("morph");
    for (int i=0; i<celems.size(); i++)
    {
        bool OK = true;
        tbMorph morph;
        QDomNamedNodeMap nm = celems.at(i).attributes();
        morph.fromId = nm.namedItem("from").nodeValue().toInt();
        if (!towers.contains(morph.fromId))
        {
            qCritical("invalid tower from Id");
            OK = false;
        }
        morph.toId = nm.namedItem("to").nodeValue().toInt();
        if (!towers.contains(morph.toId))
        {
            qCritical("invalid tower to Id");
            OK = false;
        }
        morph.cost = nm.namedItem("cost").nodeValue().toInt();
        morph.expRequired = nm.namedItem("expRequired").nodeValue().toFloat();
        if (OK)
        {
            morphs.insert(morph.fromId,morph);
            //qDebug() << tr("from=%1 to=%2 cost=%3 expRequired=%4").arg(morph.fromId).arg(morph.toId).arg(morph.cost).arg(morph.expRequired);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 morphs").arg(morphs.size());
    //qDebug("================");
}


void Loader::readLevWaves()
{
    levWaves.clear();
    QDomNodeList lwelems = doc->elementsByTagName("waveOnLevel");
    for (int i=0; i<lwelems.size(); i++)
    {
        bool OK = true;
        tbWaveOnLevel lw;
        QDomNamedNodeMap nm = lwelems.at(i).attributes();
        lw.levelId= nm.namedItem("level").nodeValue().toInt();
        if (!levels.contains(lw.levelId))
        {
            qCritical("invalid level Id");
            OK = false;
        }
        lw.waveId = nm.namedItem("wave").nodeValue().toInt();
        if (!waves.contains(lw.waveId))
        {
            qCritical("invalid wave Id");
            OK = false;
        }
        if (OK)
        {
            levWaves.insert(lw.levelId,lw);
            //qDebug() << tr("level=%1 wave=%2").arg(lw.levelId).arg(lw.waveId);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 Waves On Level").arg(levWaves.size());
    //qDebug("================");
}


void Loader::readLevTowers()
{
    levTowers.clear();
    QDomNodeList ltelems = doc->elementsByTagName("towerOnLevel");
    for (int i=0; i<ltelems.size(); i++)
    {
        bool OK = true;
        tbTowerOnLevel lt;
        QDomNamedNodeMap nm = ltelems.at(i).attributes();
        lt.levelId= nm.namedItem("level").nodeValue().toInt();
        if (!levels.contains(lt.levelId))
        {
            qCritical("invalid level Id");
            OK = false;
        }
        lt.towerId = nm.namedItem("tower").nodeValue().toInt();
        if (!towers.contains(lt.towerId))
        {
            qCritical("invalid tower Id");
            OK = false;
        }
        if (OK)
        {
            levTowers.insert(lt.levelId,lt);
            //qDebug() << tr("level=%1 tower=%2").arg(lt.levelId).arg(lt.towerId);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 Towers On Level").arg(levTowers.size());
    //qDebug("================");
}


void Loader::readLevels()
{
    levels.clear();
    QDomNodeList lelems = doc->elementsByTagName("level");
    for (int i=0; i<lelems.size(); i++)
    {
        bool OK = true;
        tbLevel l;
        QDomNamedNodeMap nm = lelems.at(i).attributes();
        l.id = nm.namedItem("id").nodeValue().toInt();
        l.name = nm.namedItem("name").nodeValue();
        if (levels.contains(l.id)) qCritical("Same wave ID detected, replacing...");
        l.mapId = nm.namedItem("map").nodeValue().toInt();
        l.money = nm.namedItem("startMoney").nodeValue().toUInt();
        if (!maps.contains(l.mapId))
        {
            qCritical("invalid map Id");
            OK = false;
        }
        if (OK)
        {
            levels.insert(l.id,l);
            //qDebug() << tr("Level %1 id=%2 map=%3 ").arg(l.name).arg(l.id).arg(l.mapId);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 levels").arg(levels.size());
    //qDebug("================");
}


void Loader::readTriggers()
{
    triggers.clear();
    QString str;
    QDomNodeList trelems = doc->elementsByTagName("trigger");
    for (int i=0; i<trelems.size(); i++)
    {
        tbTrigger trig;
        QDomNamedNodeMap nm = trelems.at(i).attributes();
        trig.id = nm.namedItem("id").nodeValue().toInt();
        if (triggers.contains(trig.id)) qCritical("Same splash ID detected, replacing...");
        trig.name = nm.namedItem("name").nodeValue();
        trig.damage = nm.namedItem("damage").nodeValue().toFloat();
        trig.radius = nm.namedItem("radius").nodeValue().toFloat();
        trig.randomPosRadius = nm.namedItem("randomPosRadius").nodeValue().toFloat();
        trig.count = nm.namedItem("count").nodeValue().toInt();
        trig.timer = nm.namedItem("timer").nodeValue().toInt();
        trig.chance = nm.namedItem("chance").nodeValue().toInt();
        if (!(trig.chance>0)) trig.chance = 100;
        str = nm.namedItem("delParent").nodeValue();
        trig.delParent = (str == "true");
        str = nm.namedItem("type").nodeValue();
        if (str == "onDestination")
            trig.type = srcTriggerType::onDestination;
        if (str == "onTimer")
            trig.type = srcTriggerType::onTimer;
        if (str == "onAlienInRadius")
            trig.type = srcTriggerType::onAlienInRadius;
        str = nm.namedItem("childAim").nodeValue();
        trig.childAim = srcTriggerType::noAim;
        if (str == "allSide")
            trig.childAim = srcTriggerType::allSide;
        if (str == "parentAim")
            trig.childAim = srcTriggerType::parentAim;
        if (str == "nearestAlien")
            trig.childAim = srcTriggerType::nearestAlien;
        triggers.insert(trig.id,trig);
        //qDebug() << tr("Trigger %1 id=%2 damage=%3 radius=%4 delParent=%5 timer=%6").arg(trig.name).arg(trig.id).arg(trig.damage).arg(trig.radius).arg(trig.delParent).arg(trig.timer)
        //		<< tr("count=%1 chance=%2 randomPosRadius=%3 type=%4 childAim=%5").arg(trig.count).arg(trig.chance).arg(trig.randomPosRadius).arg(trig.type).arg(trig.childAim);
    }
    //qDebug("================");
    qDebug() << tr("Found %1 triggers").arg(triggers.size());
    //qDebug("================");
}


void Loader::readSplashes()
{
    splashes.clear();
    QString str;
    QDomNodeList selems = doc->elementsByTagName("splash");
    for (int i=0; i<selems.size(); i++)
    {
        bool OK = true;
        tbSplash s;
        QDomNamedNodeMap nm = selems.at(i).attributes();
        s.id = nm.namedItem("id").nodeValue().toInt();
        if (splashes.contains(s.id)) qCritical("Same splash ID detected, replacing...");
        s.name = nm.namedItem("name").nodeValue();
        s.speed = nm.namedItem("speed").nodeValue().toFloat();
        s.lifetime = nm.namedItem("lifetime").nodeValue().toInt();
        s.imgType = nm.namedItem("imageType").nodeValue().toInt();
        if (!animations.contains(s.imgType))
        {
            qCritical("Invalid animation ID");
            OK = false;
        }
        str = nm.namedItem("autoControl").nodeValue();
        s.autoControl = (str == "true");
        str = nm.namedItem("keepOnAlien").nodeValue();
        s.keepOnAlien = (str == "true");
        s.animSpeed = nm.namedItem("animSpeed").nodeValue().toFloat();
        if  (s.animSpeed == 0.f) s.animSpeed = 1.f;
        s.ZValue = nm.namedItem("ZValue").nodeValue().toFloat();
        str = nm.namedItem("compositionMode").nodeValue();
        s.compositionMode = QPainter::CompositionMode_SourceOver;
        if (str == "NotSource") s.compositionMode = QPainter::RasterOp_NotSource;
        if (str == "Plus") s.compositionMode = QPainter::CompositionMode_Plus;
        if (str == "Multiply") s.compositionMode = QPainter::CompositionMode_Multiply;
        if (str == "Screen") s.compositionMode = QPainter::CompositionMode_Screen;
        if (str == "Overlay") s.compositionMode = QPainter::CompositionMode_Overlay;
        if (str == "Darken") s.compositionMode = QPainter::CompositionMode_Darken;
        if (str == "Lighten") s.compositionMode = QPainter::CompositionMode_Lighten;
        if (str == "ColorDodge") s.compositionMode = QPainter::CompositionMode_ColorDodge;
        if (str == "ColorBurn") s.compositionMode = QPainter::CompositionMode_ColorBurn;
        if (str == "HardLight") s.compositionMode = QPainter::CompositionMode_HardLight;
        if (str == "SoftLight") s.compositionMode = QPainter::CompositionMode_SoftLight;
        if (str == "Difference") s.compositionMode = QPainter::CompositionMode_Difference;
        if (OK)
        {
            splashes.insert(s.id,s);
            //qDebug() << tr("Splash %1 id=%2 speed=%3 imgType=%4 autoControl=%5 lifetime=%6").arg(s.name).arg(s.id).arg(s.speed).arg(s.imgType).arg(s.autoControl).arg(s.lifetime);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 splashes").arg(splashes.size());
    //qDebug("================");
}


void Loader::readAnimations()
{
    animations.clear();
    QDomNodeList animelems = doc->elementsByTagName("animation");
    for (int i=0; i<animelems.size(); i++)
    {
        tbAnimation anim;
        QDomNamedNodeMap nm = animelems.at(i).attributes();
        anim.id = nm.namedItem("id").nodeValue().toInt();
        if (animations.contains(anim.id)) qCritical("Same animation ID detected, replacing...");
        anim.name = nm.namedItem("name").nodeValue();
        anim.pathes = nm.namedItem("pathes").nodeValue().split(";");
        animations.insert(anim.id,anim);
        //qDebug() << tr("animation id=%1 pathes=%2").arg(anim.id).arg(anim.pathes.join(";"));
    }
    qDebug() << tr("Found %1 animations").arg(animations.size());
}


void Loader::readTowers()
{
    towers.clear();
    QDomNodeList twelems = doc->elementsByTagName("tower");
    for (int i=0; i<twelems.size(); i++)
    {
        bool OK = true;
        tbTower tw;
        QDomNamedNodeMap nm = twelems.at(i).attributes();
        tw.id = nm.namedItem("id").nodeValue().toInt();
        if (towers.contains(tw.id)) qCritical("Same tower ID detected, replacing...");
        tw.name = nm.namedItem("name").nodeValue();
        tw.radius = nm.namedItem("radius").nodeValue().toFloat();
        tw.expByDamage = nm.namedItem("expByDamage").nodeValue().toFloat();
        tw.expByKill = nm.namedItem("expByKill").nodeValue().toFloat();
        tw.expByShot = nm.namedItem("expByShot").nodeValue().toFloat();
        tw.imgType = nm.namedItem("imageType").nodeValue().toInt();
        if (!animations.contains(tw.imgType))
        {
            qCritical("Invalid animation ID");
            OK = false;
        }
        tw.reload = nm.namedItem("reload").nodeValue().toInt();
        tw.cost = nm.namedItem("cost").nodeValue().toInt();
        tw.buildTime = nm.namedItem("buildTime").nodeValue().toInt();
        tw.splashId = nm.namedItem("splash").nodeValue().toInt();
        if (!splashes.contains(tw.splashId))
        {
            qCritical("Invalid splash Id");
            OK = false;
        }
        if (OK)
        {
            towers.insert(tw.id,tw);
            //qDebug() << tr("Tower %1 id=%2 radius=%3 cost=%4 reload=%5 buildTime=%6 splash=%7").arg(tw.name).arg(tw.id).arg(tw.radius).arg(tw.cost).arg(tw.reload).arg(tw.buildTime).arg(tw.splashId)
            //		<< tr("imgType=%1 expByKill=%2 expByShot=%3 expByDamage=%4").arg(tw.imgType).arg(tw.expByKill).arg(tw.expByShot).arg(tw.expByDamage);
        }
    }
    //qDebug("================");
    qDebug() << tr("Found %1 towers").arg(towers.size());
    //qDebug("================");
}


void Loader::readMaps()
{
    maps.clear();
    QDomNodeList melems = doc->elementsByTagName("map");
    for (int i=0; i<melems.size(); i++)
    {
        tbMap m;
        QDomNamedNodeMap mn = melems.at(i).attributes();
        m.id = mn.namedItem("id").nodeValue().toInt();
        if (maps.contains(m.id)) qCritical("Same map ID detected, replacing...");
        m.name = mn.namedItem("name").nodeValue();
        m.imgType = mn.namedItem("imageType").nodeValue().toInt();
        m.maxPlayers = mn.namedItem("maxPlayers").nodeValue().toInt();
        m.data=mn.namedItem("data").nodeValue().toUtf8();
        m.size.setWidth(mn.namedItem("width").nodeValue().toInt());
        m.size.setHeight(mn.namedItem("height").nodeValue().toInt());
        maps.insert(m.id,m);
        //qDebug() << tr("Map %1 id=%2 imgType=%3 maxPlayers=%5 data=%6 ").arg(m.name).arg(m.id).arg(m.imgType).arg(m.maxPlayers).arg(QString(m.data)) << m.size;
    }
    //qDebug("================");
    qDebug() << tr("Found %1 maps").arg(maps.size());
    //qDebug("================");
}


void Loader::validate(QString filename)
{
    QFile sfile(":/schema.xml");
    if (!sfile.open(QIODevice::ReadOnly))
        qFatal("No file schema.xml found!");
    QXmlSchema schema;
    if (!schema.load(&sfile))
        qFatal("Error parsing schema.xml");
    QXmlSchemaValidator sval(schema);
    QFile dfile(filename);
    if (!dfile.open(QIODevice::ReadOnly))
        qFatal("ERROR File not found!");
    if (!sval.validate(&dfile))
        qFatal("Validation failure!");
    else
    {
        sfile.close();
        dfile.close();
        qDebug("Validation SUCCESS!!!");
        qDebug("================");
    }
}


void Loader::printMorphs(srcTowerType *stt, QString &str, const QString & prefix)
{
    str += "\n" + prefix + stt->name + ":";
    foreach (MorphType i, stt->morphs)
        printMorphs(i.morph,str,prefix+"  ");
}

