#include "Animation.h"

#include "Border.h"
#include "FileSystem.h"
#include "Image.h"
#include "InitFile.h"
#include "Text.h"
#include "ToolTip.h"

using namespace Framework;

// Contents of the Animation2DData class from Animation.h
// Constructor
Animation2DData::Animation2DData()
    : ReferenceCounter(),
      bilder(0),
      imageCount(0),
      fps(0),
      repeat(0),
      transparent(0)
{}

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

// non-constant
Lock& Animation2DData::readLock()
{
    return rwLock.getReadLock();
}

Lock& Animation2DData::writeLock()
{
    return rwLock.getWriteLock();
}

void Animation2DData::loadAnimation(InitFile* datei)
{
    if (!datei) return;
    reset();
    int anz = datei->getValueCount();
    rwLock.lockWrite();
    if (datei->valueExists("fps"))
    {
        --anz;
        fps = TextZuInt(datei->zValue("fps")->getText(), 10);
    }
    if (datei->valueExists("repeat"))
    {
        --anz;
        repeat = datei->zValue("repeat")->isEqual("true");
    }
    if (datei->valueExists("transparent"))
    {
        --anz;
        transparent = datei->zValue("transparent")->isEqual("true");
    }
    Image** bilder = new Image*[anz];
    int j = 0;
    for (int i = 0; i < anz; ++i)
    {
        bilder[j] = 0;
        if (datei->zName(i)->isEqual("fps")
            || datei->zName(i)->isEqual("repeat")
            || datei->zName(i)->isEqual("transparent"))
            continue;
        Text pfad = datei->zValue(i)->getText();
        if (pfad.has(".ltdb/") && pfad.getLength() > 7)
        {
            Text* name = pfad.getTeilText(
                pfad.positionOf(".ltdb/", pfad.countOf(".ltdb/") - 1) + 6);
            Text* tmp
                = pfad.getTeilText(0, pfad.getLength() - name->getLength() - 1);
            pfad.setText(*tmp);
            tmp->release();
            LTDBFile* dat = new LTDBFile();
            dat->setFile(dynamic_cast<Text*>(pfad.getThis()));
            dat->readData(0);
            bilder[j] = dat->load(0, name);
            dat->release();
        }
        ++j;
    }
    this->bilder = new Image*[imageCount];
    j = 0;
    for (int i = 0; i < anz; ++i)
    {
        if (!bilder[i])
            ++j;
        else
            this->bilder[i - j] = bilder[i];
    }
    delete[] bilder;
    rwLock.unlockWrite();
    datei->release();
}

void Animation2DData::loadAnimation(LTDBFile* datei)
{
    if (!datei) return;
    reset();
    datei->readData(0);
    int anz = datei->getImageCount();
    RCArray<Text>* list = datei->zImageListe();
    rwLock.lockWrite();
    Image** bilder = new Image*[anz];
    for (int i = 0; i < anz; ++i)
    {
        bilder[i] = datei->load(0, list->get(i));
        if (bilder[i]) ++imageCount;
    }
    this->bilder = new Image*[imageCount];
    int j = 0;
    for (int i = 0; i < anz; ++i)
    {
        if (!bilder[i])
            ++j;
        else
            this->bilder[i - j] = bilder[i];
    }
    delete[] bilder;
    rwLock.unlockWrite();
    datei->release();
}

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

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

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

void Animation2DData::reset()
{
    rwLock.lockWrite();
    for (int i = 0; i < imageCount; ++i)
        bilder[i] = (Image*)bilder[i]->release();
    delete[] bilder;
    bilder = 0;
    imageCount = 0;
    fps = 30;
    repeat = 0;
    transparent = 0;
    rwLock.unlockWrite();
}

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

Image* Animation2DData::zImage(int i) const
{
    return (i >= 0 && i < imageCount) ? bilder[i] : 0;
}

int Animation2DData::getImageCount() const
{
    return imageCount;
}

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

bool Animation2DData::isRepeating() const
{
    return repeat;
}

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

// Contents of the Animation2D class from Animation.h
// Constructor
Animation2D::Animation2D()
    : Drawable(),
      data(0),
      now(0),
      compensation(0),
      alpha(0),
      maxAlpha(255),
      border(0),
      ram(0),
      aps(255 * 60),
      visible(0)
{}

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

// non-constant
void Animation2D::setBorder(bool ram)
{
    border = ram;
}

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

void Animation2D::setBorderWidth(int br)
{
    if (!ram) ram = new LBorder();
    ram->setBorderWidth(br);
}

void Animation2D::setBorderColor(int f)
{
    if (!ram) ram = new LBorder();
    ram->setColor(f);
}

void Animation2D::setAnimationDataZ(Animation2DData* data)
{
    rwLock.lockWrite();
    if (this->data) this->data->release();
    this->data = data;
    if (alpha) rend = 1;
    rwLock.unlockWrite();
}

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

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

void Animation2D::setVisible(bool visible)
{
    this->visible = visible;
}

bool Animation2D::tick(double zeit)
{
    rwLock.lockRead();
    if (!data || (!alpha && !visible))
    {
        bool ret = rend;
        rend = 0;
        rwLock.unlockRead();
        return ret;
    }
    if (visible && alpha < maxAlpha)
    {
        if (alpha + aps * zeit >= maxAlpha)
            alpha = maxAlpha;
        else
            alpha = (unsigned char)(alpha + aps * zeit);
        rend = 1;
    }
    else if (!visible && alpha > 0)
    {
        if (alpha - aps * zeit <= 0)
            alpha = 0;
        else
            alpha = (unsigned char)(alpha - aps * zeit);
        rend = 1;
    }
    compensation += zeit;
    int tmp = now;
    data->readLock().lock();
    if (compensation >= 1.0 / data->getFPS())
    {
        compensation -= 1.0 / data->getFPS();
        ++now;
        if (now >= data->getImageCount())
        {
            if (data->isRepeating())
                now = 0;
            else
                now = data->getImageCount();
        }
    }
    data->readLock().unlock();
    if (tmp != now) rend = 1;
    rwLock.unlockRead();
    return Drawable::tick(zeit);
}

void Animation2D::render(Image& zRObj)
{
    rwLock.lockRead();
    if (!data)
    {
        rwLock.unlockRead();
        return;
    }
    Drawable::render(zRObj);
    data->readLock().lock();
    if (data->zImage(now))
    {
        zRObj.setAlpha(alpha);
        if (data->isTransparent())
            zRObj.alphaImage(pos.x, pos.y, gr.x, gr.y, *data->zImage(now));
        else
            zRObj.drawImage(pos.x, pos.y, gr.x, gr.y, *data->zImage(now));
        if (ram && border)
        {
            ram->setPosition(pos);
            ram->setSize(gr);
            ram->render(zRObj);
        }
        zRObj.releaseAlpha();
    }
    data->readLock().unlock();
    rwLock.unlockRead();
}

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

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

bool Animation2D::isVisible() const
{
    return visible;
}

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

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

bool Animation2D::hasBorder() const
{
    return border;
}

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

Border* Animation2D::zBorder() const
{
    return ram;
}

int Animation2D::getBorderWidth() const
{
    return ram ? ram->getRWidth() : 0;
}

int Animation2D::getBorderColor() const
{
    return ram ? ram->getColor() : 0;
}

Drawable* Animation2D::duplicate() const
{
    Animation2D* ret = new Animation2D();
    ret->setPosition(pos);
    ret->setSize(gr);
    ret->setMouseEventParameter(makParam);
    ret->setKeyboardEventParameter(takParam);
    ret->setMouseEvent(mak);
    ret->setKeyboardEvent(tak);
    if (toolTip) ret->setToolTipZ((ToolTip*)toolTip->duplicate());
    if (data)
        ret->setAnimationDataZ(dynamic_cast<Animation2DData*>(data->getThis()));
    ret->setAPS(aps);
    ret->setVisible(visible);
    ret->setAlphaMaske(maxAlpha);
    ret->setBorder(border);
    if (ram)
    {
        ret->setBorderWidth(ram->getRWidth());
        ret->setBorderColor(ram->getColor());
    }
    return ret;
}