#include "Animation.h"

#include "Bild.h"
#include "DateiSystem.h"
#include "InitDatei.h"
#include "Rahmen.h"
#include "Text.h"
#include "ToolTip.h"

using namespace Framework;

// Inhalt der Animation2DData Klasse aus Animation.h
// Konstruktor
Animation2DData::Animation2DData()
    : ReferenceCounter(),
      bilder(0),
      bildAnzahl(0),
      fps(0),
      wiederhohlen(0),
      transparent(0)
{}

// Destruktor
Animation2DData::~Animation2DData()
{
    reset();
}

// nicht constant
void Animation2DData::lock()
{
    cs.lock();
}

void Animation2DData::unlock()
{
    cs.unlock();
}

void Animation2DData::ladeAnimation(InitDatei* datei)
{
    if (!datei) return;
    reset();
    int anz = datei->getWertAnzahl();
    lock();
    if (datei->wertExistiert("fps"))
    {
        --anz;
        fps = TextZuInt(datei->zWert("fps")->getText(), 10);
    }
    if (datei->wertExistiert("wiederhohlen"))
    {
        --anz;
        wiederhohlen = datei->zWert("wiederhohlen")->istGleich("true");
    }
    if (datei->wertExistiert("transparent"))
    {
        --anz;
        transparent = datei->zWert("transparent")->istGleich("true");
    }
    Bild** bilder = new Bild*[anz];
    int j = 0;
    for (int i = 0; i < anz; ++i)
    {
        bilder[j] = 0;
        if (datei->zName(i)->istGleich("fps")
            || datei->zName(i)->istGleich("wiederhohlen")
            || datei->zName(i)->istGleich("transparent"))
            continue;
        Text pfad = datei->zWert(i)->getText();
        if (pfad.hat(".ltdb/") && pfad.getLength() > 7)
        {
            Text* name = pfad.getTeilText(
                pfad.positionVon(".ltdb/", pfad.anzahlVon(".ltdb/") - 1) + 6);
            pfad.setText(
                pfad.getTeilText(0, pfad.getLength() - name->getLength() - 1));
            LTDBDatei* dat = new LTDBDatei();
            dat->setDatei(dynamic_cast<Text*>(pfad.getThis()));
            dat->leseDaten(0);
            bilder[j] = dat->laden(0, name);
            dat->release();
        }
        ++j;
    }
    this->bilder = new Bild*[bildAnzahl];
    j = 0;
    for (int i = 0; i < anz; ++i)
    {
        if (!bilder[i])
            ++j;
        else
            this->bilder[i - j] = bilder[i];
    }
    delete[] bilder;
    unlock();
    datei->release();
}

void Animation2DData::ladeAnimation(LTDBDatei* datei)
{
    if (!datei) return;
    reset();
    datei->leseDaten(0);
    int anz = datei->getBildAnzahl();
    RCArray<Text>* list = datei->zBildListe();
    lock();
    Bild** bilder = new Bild*[anz];
    for (int i = 0; i < anz; ++i)
    {
        bilder[i] = datei->laden(0, list->get(i));
        if (bilder[i]) ++bildAnzahl;
    }
    this->bilder = new Bild*[bildAnzahl];
    int j = 0;
    for (int i = 0; i < anz; ++i)
    {
        if (!bilder[i])
            ++j;
        else
            this->bilder[i - j] = bilder[i];
    }
    delete[] bilder;
    unlock();
    datei->release();
}

void Animation2DData::setFPS(int fps)
{
    this->fps = fps;
}

void Animation2DData::setWiederhohlend(bool wh)
{
    wiederhohlen = wh;
}

void Animation2DData::setTransparent(bool trp)
{
    transparent = trp;
}

void Animation2DData::reset()
{
    lock();
    for (int i = 0; i < bildAnzahl; ++i)
        bilder[i] = (Bild*)bilder[i]->release();
    delete[] bilder;
    bilder = 0;
    bildAnzahl = 0;
    fps = 30;
    wiederhohlen = 0;
    transparent = 0;
    unlock();
}

// constant
Bild* Animation2DData::getBild(int i) const
{
    return (i >= 0 && i < bildAnzahl)
             ? dynamic_cast<Bild*>(bilder[i]->getThis())
             : 0;
}

Bild* Animation2DData::zBild(int i) const
{
    return (i >= 0 && i < bildAnzahl) ? bilder[i] : 0;
}

int Animation2DData::getBildAnzahl() const
{
    return bildAnzahl;
}

int Animation2DData::getFPS() const
{
    return fps;
}

bool Animation2DData::istWiederhohlend() const
{
    return wiederhohlen;
}

bool Animation2DData::istTransparent() const
{
    return transparent;
}

// Inhalt der Animation2D Klasse aus Animation.h
// Konstruktor
Animation2D::Animation2D()
    : Zeichnung(),
      data(0),
      jetzt(0),
      ausgleich(0),
      alpha(0),
      maxAlpha(255),
      rahmen(0),
      ram(0),
      aps(255 * 60),
      sichtbar(0)
{}

// Destruktor
Animation2D::~Animation2D()
{
    if (data) data->release();
    if (ram) ram->release();
}

// nicht constant
void Animation2D::setRahmen(bool ram)
{
    rahmen = ram;
}

void Animation2D::setRahmenZ(Rahmen* ram)
{
    if (this->ram) this->ram->release();
    this->ram = ram;
}

void Animation2D::setRahmenBreite(int br)
{
    if (!ram) ram = new LRahmen();
    ram->setRamenBreite(br);
}

void Animation2D::setRahmenFarbe(int f)
{
    if (!ram) ram = new LRahmen();
    ram->setFarbe(f);
}

void Animation2D::setAnimationDataZ(Animation2DData* data)
{
    lockZeichnung();
    if (this->data) this->data->release();
    this->data = data;
    if (alpha) rend = 1;
    unlockZeichnung();
}

void Animation2D::setAlphaMaske(unsigned char alpha)
{
    maxAlpha = alpha;
}

void Animation2D::setAPS(int aps)
{
    this->aps = aps;
}

void Animation2D::setSichtbar(bool sichtbar)
{
    this->sichtbar = sichtbar;
}

bool Animation2D::tick(double zeit)
{
    lockZeichnung();
    if (!data || (!alpha && !sichtbar))
    {
        bool ret = rend;
        rend = 0;
        unlockZeichnung();
        return ret;
    }
    if (sichtbar && alpha < maxAlpha)
    {
        if (alpha + aps * zeit >= maxAlpha)
            alpha = maxAlpha;
        else
            alpha = (unsigned char)(alpha + aps * zeit);
        rend = 1;
    }
    else if (!sichtbar && alpha > 0)
    {
        if (alpha - aps * zeit <= 0)
            alpha = 0;
        else
            alpha = (unsigned char)(alpha - aps * zeit);
        rend = 1;
    }
    ausgleich += zeit;
    int tmp = jetzt;
    data->lock();
    if (ausgleich >= 1.0 / data->getFPS())
    {
        ausgleich -= 1.0 / data->getFPS();
        ++jetzt;
        if (jetzt >= data->getBildAnzahl())
        {
            if (data->istWiederhohlend())
                jetzt = 0;
            else
                jetzt = data->getBildAnzahl();
        }
    }
    data->unlock();
    if (tmp != jetzt) rend = 1;
    unlockZeichnung();
    return Zeichnung::tick(zeit);
}

void Animation2D::render(Bild& zRObj)
{
    lockZeichnung();
    if (!data)
    {
        unlockZeichnung();
        return;
    }
    Zeichnung::render(zRObj);
    data->lock();
    if (data->zBild(jetzt))
    {
        zRObj.setAlpha(alpha);
        if (data->istTransparent())
            zRObj.alphaBild(pos.x, pos.y, gr.x, gr.y, *data->zBild(jetzt));
        else
            zRObj.drawBild(pos.x, pos.y, gr.x, gr.y, *data->zBild(jetzt));
        if (ram && rahmen)
        {
            ram->setPosition(pos);
            ram->setSize(gr);
            ram->render(zRObj);
        }
        zRObj.releaseAlpha();
    }
    data->unlock();
    unlockZeichnung();
}

// constant
Animation2DData* Animation2D::getAnimationData() const
{
    return data ? dynamic_cast<Animation2DData*>(data->getThis()) : 0;
}

Animation2DData* Animation2D::zAnimationData() const
{
    return data;
}

bool Animation2D::istSichtbar() const
{
    return sichtbar;
}

int Animation2D::getJetzt() const
{
    return jetzt;
}

unsigned char Animation2D::getAlphaMaske() const
{
    return maxAlpha;
}

bool Animation2D::hatRahmen() const
{
    return rahmen;
}

Rahmen* Animation2D::getRahmen() const
{
    return ram ? dynamic_cast<Rahmen*>(ram->getThis()) : 0;
}

Rahmen* Animation2D::zRahmen() const
{
    return ram;
}

int Animation2D::getRahmenBreite() const
{
    return ram ? ram->getRBreite() : 0;
}

int Animation2D::getRahmenFarbe() const
{
    return ram ? ram->getFarbe() : 0;
}

Zeichnung* Animation2D::dublizieren() const
{
    Animation2D* ret = new Animation2D();
    ret->setPosition(pos);
    ret->setSize(gr);
    ret->setMausEreignisParameter(makParam);
    ret->setTastaturEreignisParameter(takParam);
    ret->setMausEreignis(mak);
    ret->setTastaturEreignis(tak);
    if (toolTip) ret->setToolTipZ((ToolTip*)toolTip->dublizieren());
    if (data)
        ret->setAnimationDataZ(dynamic_cast<Animation2DData*>(data->getThis()));
    ret->setAPS(aps);
    ret->setSichtbar(sichtbar);
    ret->setAlphaMaske(maxAlpha);
    ret->setRahmen(rahmen);
    if (ram)
    {
        ret->setRahmenBreite(ram->getRBreite());
        ret->setRahmenFarbe(ram->getFarbe());
    }
    return ret;
}