#include "M2File.h"

#include "File.h"
#include "Model2D.h"
#include "Text.h"

using namespace Framework;

// Contents of the M2File class from M2File.h
// Constructor
M2File::M2File()
    : ReferenceCounter()
{
    pfad = new Text();
    modelName = new RCArray<Text>();
    modelPos = new Array<__int64>();
}

M2File::M2File(const char* pfad)
    : ReferenceCounter()
{
    this->pfad = new Text(pfad);
    modelName = new RCArray<Text>();
    modelPos = new Array<__int64>();
}

M2File::M2File(Text* pfad)
    : ReferenceCounter()
{
    this->pfad = pfad;
    modelName = new RCArray<Text>();
    modelPos = new Array<__int64>();
}

// Destructor
M2File::~M2File()
{
    pfad->release();
    modelName->release();
    modelPos->release();
}

// non-constant
void M2File::setPfad(const char* pfad)
{
    this->pfad->setText(pfad);
}

void M2File::setPfadZ(Text* pfad)
{
    if (this->pfad) this->pfad->release();
    this->pfad = pfad;
}

void M2File::readData()
{
    File d;
    d.setFile(pfad->getText());
    d.open(File::Style::read);
    char anz = 0;
    d.read(&anz, 1);
    modelName->clear();
    modelPos->clear();
    for (int i = 0; i < anz; i++)
    {
        char len = 0;
        d.read(&len, 1);
        char* txt = new char[len + 1];
        d.read(txt, len);
        txt[(int)len] = 0;
        modelName->set(new Text(txt), i);
        delete[] txt;
        __int64 pos = 0;
        d.read((char*)&pos, 8);
        modelPos->set(pos, i);
    }
    d.close();
}

bool M2File::saveModel(Model2DData* zMdr, Text* name)
{
    bool ret = saveModel(zMdr, name->getText());
    name->release();
    return ret;
}

bool M2File::saveModel(Model2DData* zMdr, const char* name)
{
    int anz = modelName->getEntryCount();
    for (int i = 0; i < anz; i++)
    {
        if (modelName->z(i)->isEqual(name))
        {
            if (!removeModel(name)) return 0;
            break;
        }
    }
    anz = modelName->getEntryCount();
    File d;
    d.setFile(pfad->getText());
    d.open(File::Style::read);
    File neu;
    neu.setFile(pfad->getText());
    neu.zPfad()->append("0");
    while (neu.exists())
        neu.zPfad()->append("0");
    if (!neu.open(File::Style::write))
    {
        if (d.isOpen()) d.close();
        return 0;
    }
    modelName->add(new Text(name));
    int offs = textLength(name) + 9;
    for (int i = 0; i < anz; i++)
        modelPos->set(modelPos->get(i) + offs, i);
    if (d.getSize() < 0)
        modelPos->add(offs + 1);
    else
        modelPos->add(d.getSize() + offs);
    anz++;
    char tmp = (char)anz;
    neu.write(&tmp, 1);
    for (int i = 0; i < anz; i++)
    {
        char len = (char)modelName->z(i)->getLength();
        neu.write(&len, 1);
        neu.write(modelName->z(i)->getText(), len);
        __int64 pos = modelPos->get(i);
        neu.write((char*)&pos, 8);
    }
    if (d.exists())
    {
        d.setReadPosition(modelPos->get(0) - offs, 0);
        __int64 dl = d.getSize() - d.getReadPosition();
        char bytes[2048];
        while (dl)
        {
            int l = dl > 2048 ? 2048 : (int)dl;
            d.read(bytes, l);
            neu.write(bytes, l);
            dl -= l;
        }
    }
    d.close();
    char pAnz = (char)zMdr->polygons->getEntryCount();
    neu.write(&pAnz, 1);
    for (int p = 0; p < pAnz; p++)
    {
        char pNameL = (char)zMdr->polygons->get(p).name->getLength();
        int vAnz = zMdr->polygons->get(p).vertex->getEntryCount();
        char options = 1;
        for (int i = 0; i < vAnz; i++)
            options
                = (char)(options
                         & (char)zMdr->polygons->get(p).tCoordinates->has(i));
        if (pNameL != 0) options |= 2;
        if (zMdr->polygons->get(p).transparent) options |= 4;
        neu.write(&options, 1);
        if ((options | 2) == options) // name
        {
            neu.write(&pNameL, 1);
            neu.write(zMdr->polygons->get(p).name->getText(), pNameL);
        }
        neu.write((char*)&vAnz, 4);
        for (int i = 0; i < vAnz; i++)
        {
            float v = zMdr->polygons->get(p).vertex->get(i).x;
            neu.write((char*)&v, 4);
            v = zMdr->polygons->get(p).vertex->get(i).y;
            neu.write((char*)&v, 4);
            if ((options | 1) == options) // textur
            {
                float t = zMdr->polygons->get(p).tCoordinates->get(i).x;
                neu.write((char*)&t, 4);
                t = zMdr->polygons->get(p).tCoordinates->get(i).y;
                neu.write((char*)&t, 4);
            }
        }
    }
    d.remove();
    neu.close();
    neu.rename(pfad->getText());
    readData();
    return 1;
}

bool M2File::removeModel(Text* name)
{
    bool ret = removeModel(name->getText());
    name->release();
    return ret;
}

bool M2File::removeModel(const char* name)
{
    int anz = modelName->getEntryCount();
    int p = -1;
    for (int i = 0; i < anz; i++)
    {
        if (modelName->z(i)->isEqual(name))
        {
            p = i;
            break;
        }
    }
    if (p < 0) return 0;
    File d;
    d.setFile(pfad->getText());
    if (!d.open(File::Style::read)) return 0;
    File neu;
    neu.setFile(pfad->getText());
    neu.zPfad()->append("0");
    while (neu.exists())
        neu.zPfad()->append("0");
    if (!neu.open(File::Style::write))
    {
        if (d.isOpen()) d.close();
        return 0;
    }
    char nAnz = (char)(anz - 1);
    neu.write(&nAnz, 1);
    int offs = modelName->z(p)->getLength() + 9;
    __int64 startP = 0, endP = 0, start2P = 0;
    for (int i = 0; i < anz; i++)
    {
        char nLen = (char)modelName->z(i)->getLength();
        const char* n = modelName->z(i)->getText();
        __int64 pos = modelPos->get(i);
        if (!startP) startP = pos;
        if (i == p + 1) start2P = pos;
        if (i == p)
        {
            if (!endP) endP = pos;
            if (i < anz - 1) offs += (int)(modelPos->get(i + 1) - pos);
        }
        if (i != p)
        {
            pos -= offs;
            neu.write(&nLen, 1);
            neu.write(n, nLen);
            neu.write((char*)&pos, 8);
        }
    }
    if (d.isOpen())
    {
        d.setReadPosition(startP, 0);
        __int64 bLen = endP - startP;
        char bytes[2048];
        while (bLen > 0)
        {
            int l = 2048 > bLen ? (int)bLen : 2048;
            d.read(bytes, l);
            neu.write(bytes, l);
            bLen -= l;
        }
        if (start2P)
        {
            d.setReadPosition(start2P, 0);
            bLen = d.getSize() - start2P;
            while (bLen > 0)
            {
                int l = 2048 > bLen ? (int)bLen : 2048;
                d.read(bytes, l);
                neu.write(bytes, l);
                bLen -= l;
            }
        }
        d.close();
    }
    d.remove();
    neu.close();
    neu.rename(pfad->getText());
    readData();
    return 1;
}

// constant
Model2DData* M2File::loadModel(Text* name) const
{
    Model2DData* ret = loadModel(name->getText());
    name->release();
    return ret;
}

Model2DData* M2File::loadModel(const char* name) const
{
    File d;
    d.setFile(pfad->getText());
    if (!d.open(File::Style::read)) return 0;
    int anz = modelName->getEntryCount();
    for (int i = 0; i < anz; i++)
    {
        if (modelName->z(i)->isEqual(name))
        {
            d.setReadPosition(modelPos->get(i), 0);
            break;
        }
    }
    if (!d.getReadPosition())
    {
        d.close();
        return 0;
    }
    char pAnz = 0;
    d.read(&pAnz, 1);
    Array<Polygon2D>* polygons = new Array<Polygon2D>();
    for (int p = 0; p < pAnz; p++)
    {
        char options = 0;
        d.read(&options, 1);
        Polygon2D polygon;
        polygon.center = new Vertex(0, 0);
        polygon.transparent = (options | 4) == options;
        polygon.name = new Text();
        if ((options | 2) == options)
        {
            char nameL = 0;
            d.read(&nameL, 1);
            char* name = new char[nameL + 1];
            name[(int)nameL] = 0;
            d.read(name, nameL);
            polygon.name->setText(name);
            delete[] name;
        }
        int vAnz = 0;
        d.read((char*)&vAnz, 4);
        if (polygons->has(p))
        {
            if (polygons->get(p).vertex) polygons->get(p).vertex->release();
            if (polygons->get(p).tCoordinates)
                polygons->get(p).tCoordinates->release();
        }
        polygon.vertex = new Array<Vertex>();
        if ((options | 1) == options) // wenn textur
            polygon.tCoordinates = new Array<Vertex>();
        else
            polygon.tCoordinates = 0;
        for (int v = 0; v < vAnz; v++)
        {
            Vertex p;
            d.read((char*)&p.x, 4);
            d.read((char*)&p.y, 4);
            *polygon.center += p * (float)(1.0 / vAnz);
            polygon.vertex->add(p);
            if ((options | 1) == options) // wenn textur
            {
                Vertex tp;
                d.read((char*)&tp.x, 4);
                d.read((char*)&tp.y, 4);
                polygon.tCoordinates->add(tp);
            }
        }
        polygons->add(polygon);
    }
    d.close();
    Model2DData* ret = new Model2DData();
    ret->createModell(polygons);
    return ret;
}

bool M2File::hasModel(const char* name) const
{
    int anz = modelName->getEntryCount();
    for (int i = 0; i < anz; i++)
    {
        if (modelName->z(i)->isEqual(name)) return 1;
    }
    return 0;
}

int M2File::getModelCount() const
{
    return modelName->getEntryCount();
}

Text* M2File::zModelName(int i) const
{
    return modelName->z(i);
}