#include "Model2D.h"
#include "Textur2D.h"
#include "FrameworkMath.h"
#include "Mat3.h"
#include "MausEreignis.h"
#include "Bild.h"

using namespace Framework;

// Inhalt der Model2DData Klasse aus Model2D.h
// Konstruktor
Model2DData::Model2DData()
    : ReferenceCounter(),
    polygons( 0 ),
    vListen( 0 ),
    minP( 0, 0 ),
    maxP( 0, 0 )
{}

// Destruktor
Model2DData::~Model2DData()
{
    if( polygons )
    {
        int anz = polygons->getEintragAnzahl();
        for( int i = 0; i < anz; i++ )
        {
            if( polygons->get( i ).name )
                polygons->get( i ).name->release();
            if( polygons->get( i ).tKordinaten )
                polygons->get( i ).tKordinaten->release();
            if( polygons->get( i ).vertex )
                polygons->get( i ).vertex->release();
            if( polygons->get( i ).schwerpunkt )
                delete polygons->get( i ).schwerpunkt;
        }
        polygons = (Array<Polygon2D> *)polygons->release();
    }
    if( vListen )
        vListen->release();
}

// privat
bool Model2DData::istPunktInnen( Vertex p, int polygonId ) const
{
    if( p < minP || p > maxP || !polygons )
        return 0;
    int num = 0;
    auto outListP = outList.begin();
    for( auto polygon = polygons->begin(); polygon; polygon++, num++, outListP++ )
    {
        if( polygonId >= 0 && num != polygonId )
            continue;
        int anz = polygon._.vertex->getEintragAnzahl();
        bool c = 0;
        int j = anz - 1;
        for( auto outListPP = outListP->begin(); outListPP; outListPP++ )
        {
            Punkt out = outListPP;
            if( out.x < out.y && j > out.x && j < out.y )
                j = out.x;
            if( out.x > out.y && (j > out.x || j < out.y) )
                j = out.x;
        }
        auto point = polygon._.vertex->begin();
        for( int i = 0; i < anz; i++, point++ )
        {
            bool cont = 0;
            for( auto outListPP = outListP->begin(); outListPP; outListPP++ )
            {
                Punkt out = outListPP;
                if( out.x < out.y && i > out.x && i < out.y )
                    cont = 1;
                if( out.x > out.y && (i > out.x || i < out.y) )
                    cont = 1;
            }
            if( cont )
                continue;
            Vertex a = point;
            Vertex b = polygon._.vertex->get( j );
            if( ((a.y >= p.y) != (b.y >= p.y)) && (p.x <= (b.x - a.x) * (p.y - a.y) / (float)(b.y - a.y) + a.x) )
                c = !c;
            j = i;
        }
        if( c )
            return 1;
    }
    return 0;
}

bool Model2DData::istLinieInnen( Vertex a, Vertex b, int polygonId ) const
{
    if( !polygons )
        return 0;
    int pAnz = polygons->getEintragAnzahl();
    for( int p = 0; p < pAnz; p++ )
    {
        if( polygonId >= 0 && p != polygonId )
            continue;
        int ola = outList.z( p )->getEintragAnzahl();
        int anz = polygons->get( p ).vertex->getEintragAnzahl();
        int j = anz - 1;
        for( int k = 0; k < ola; k++ )
        {
            Punkt out = outList.z( p )->get( k );
            if( out.x < out.y && j > out.x && j < out.y )
                j = out.x;
            if( out.x > out.y && (j > out.x || j < out.y) )
                j = out.x;
        }
        for( int i = 0; i < anz; i++ )
        {
            bool cont = 0;
            for( int k = 0; k < ola; k++ )
            {
                Punkt out = outList.z( p )->get( k );
                if( out.x < out.y && i > out.x && i < out.y )
                    cont = 1;
                if( out.x > out.y && (i > out.x || i < out.y) )
                    cont = 1;
            }
            if( cont )
                continue;
            Punkt va = polygons->get( p ).vertex->get( i );
            Punkt vb = polygons->get( p ).vertex->get( j );
            if( (Punkt)a == va && (Punkt)b == vb )
                return 1;
            if( (Punkt)a == vb && (Punkt)b == va )
                return 1;
            j = i;
        }
        Vertex len = b - a;
        Vertex speed( len.x > 0 ? 1 : -1.f, len.y > 0 ? 1 : -1.f );
        int mLen = 0;
        if( fabs( len.x ) > fabs( len.y ) )
        {
            mLen = (int)fabs( len.x );
            speed.y = len.y / (float)fabs( len.x );
        }
        else
        {
            mLen = (int)fabs( len.y );
            speed.x = len.x / (float)fabs( len.y );
        }
        int i = 1;
        bool inside = 1;
        for( Vertex vp = speed + a; (Punkt)vp != (Punkt)(b - speed) && inside && i < mLen - 1; vp += speed, i++ )
            inside &= istPunktInnen( vp, p );
        if( inside )
            return 1;
    }
    return 0;
}

// nicht constant
bool Model2DData::erstelleModell( Array< Polygon2D >* polygons )
{
    removeModell();
    if( !polygons || !polygons->getEintragAnzahl() )
    {
        this->polygons = polygons;
        vListen = new RCArray< RCArray< DreieckListe< Vertex > > >();
        return 1;
    }
    this->polygons = polygons;
    int pAnz = polygons->getEintragAnzahl();
    vListen = new RCArray< RCArray< DreieckListe< Vertex > > >();
    for( int p = 0; p < pAnz; p++ )
    {
        Polygon2D pg = polygons->get( p );
        if( !pg.vertex || pg.vertex->getEintragAnzahl() < 3 )
            continue;
        vListen->add( new RCArray< DreieckListe< Vertex > >() );
        outList.add( new Array< Punkt > );
        int vAnz = pg.vertex->getEintragAnzahl();
        bool textur = pg.tKordinaten != 0;
        for( int i = 0; i < vAnz && textur; i++ )
            textur &= pg.tKordinaten->hat( i );
        for( int i = 0; i < vAnz; i++ )
        {
            if( (float)maxP.x < fabs( pg.vertex->get( i ).x ) )
            {
                maxP.x = abs( (int)pg.vertex->get( i ).x ) + 1;
                maxP.y = abs( (int)pg.vertex->get( i ).x ) + 1;
            }
            if( (float)maxP.y < fabs( pg.vertex->get( i ).y ) )
            {
                maxP.x = abs( (int)pg.vertex->get( i ).y ) + 1;
                maxP.y = abs( (int)pg.vertex->get( i ).y ) + 1;
            }
        }
        minP = -maxP;
        if( !textur )
        {
            if( pg.tKordinaten )
                pg.tKordinaten->leeren();
        }
        RCArray< RCArray< DreieckListe< Vertex > > > lists;
        int lauf = 0;
        while( 1 )
        {
            lists.add( new RCArray< DreieckListe< Vertex > >() );
            outList.z( p )->add( Punkt( 0, 0 ) );
            bool fertig = 0;
            Vertex a;
            Vertex b;
            Array< Punkt > tmpOutList;
            for( int i = 0; i < vAnz; i++ )
            {
                bool cont = 0;
                int vorher = i - 1;
                int nachher = i + 1;
                if( nachher >= vAnz )
                    nachher = 0;
                if( vorher < 0 )
                    vorher = vAnz - 1;
                int ola = outList.z( p )->getEintragAnzahl();
                for( int j = 0; j < ola; j++ )
                {
                    Punkt out = outList.z( p )->get( j );
                    if( out.x < out.y )
                    {
                        if( nachher > out.x && nachher < out.y )
                            nachher = out.y;
                        if( vorher > out.x && vorher < out.y )
                            vorher = out.x;
                    }
                    if( out.x > out.y )
                    {
                        if( nachher > out.x || nachher < out.y )
                            nachher = out.y;
                        if( vorher > out.x || vorher < out.y )
                            vorher = out.x;
                    }
                    if( out.x < out.y && i > out.x && i < out.y )
                        cont = 1;
                    if( out.x > out.y && (i > out.x || i < out.y) )
                        cont = 1;
                }
                if( cont )
                    continue;
                if( vorher < 0 )
                    a = pg.vertex->get( vAnz + vorher );
                else
                    a = pg.vertex->get( vorher );
                if( nachher > vAnz - 1 )
                    b = pg.vertex->get( nachher - vAnz + 1 );
                else
                    b = pg.vertex->get( nachher );
                if( istLinieInnen( a, b, p ) )
                {
                    DreieckListe< Vertex >* lowL = new DreieckListe< Vertex >();
                    DreieckListe< Vertex >* heightL = new DreieckListe< Vertex >();
                    lowL->addPunkt( new Vertex( pg.vertex->get( i ) ), textur ? new Vertex( pg.tKordinaten->get( i ) ) : 0 );
                    heightL->addPunkt( new Vertex( pg.vertex->get( i ) ), textur ? new Vertex( pg.tKordinaten->get( i ) ) : 0 );
                    int height = i + 1;
                    int low = i - 1;
                    Punkt outL( 0, 0 );
                    Punkt outH( 0, 0 );
                    for( int k = 0; k < 2; k++ )
                    {
                        bool lowp = !k;
                        while( 1 )
                        {
                            if( height >= vAnz )
                                height = 0;
                            if( low < 0 )
                                low = vAnz - 1;
                            for( int j = 0; j <= lauf; j++ )
                            {
                                Punkt out = outList.z( p )->get( j );
                                if( out.x < out.y )
                                {
                                    if( height > out.x && height < out.y )
                                        height = out.y;
                                    if( low > out.x && low < out.y )
                                        low = out.x;
                                }
                                if( out.x > out.y )
                                {
                                    if( height > out.x || height < out.y )
                                        height = out.y;
                                    if( low > out.x || low < out.y )
                                        low = out.x;
                                }
                            }
                            Vertex a = pg.vertex->get( height );
                            Vertex b = pg.vertex->get( low );
                            if( low == height )
                            {
                                fertig = 1;
                                outList.z( p )->set( Punkt( 0, 0 ), lauf );
                                if( !k )
                                    lowL->addPunkt( new Vertex( b ), textur ? new Vertex( pg.tKordinaten->get( low ) ) : 0 );
                                else
                                    heightL->addPunkt( new Vertex( b ), textur ? new Vertex( pg.tKordinaten->get( low ) ) : 0 );
                                break;
                            }
                            bool inside = istLinieInnen( a, b, p );
                            if( inside )
                            {
                                if( !k )
                                    outL = Punkt( low, height );
                                else
                                    outH = Punkt( low, height );
                                outList.z( p )->set( Punkt( low, height ), lauf );
                            }
                            if( lowp )
                            {
                                if( !k )
                                    lowL->addPunkt( new Vertex( b ), textur ? new Vertex( pg.tKordinaten->get( low ) ) : 0 );
                                else
                                    heightL->addPunkt( new Vertex( b ), textur ? new Vertex( pg.tKordinaten->get( low ) ) : 0 );
                                low--;
                            }
                            else
                            {
                                if( !k )
                                    lowL->addPunkt( new Vertex( a ), textur ? new Vertex( pg.tKordinaten->get( height ) ) : 0 );
                                else
                                    heightL->addPunkt( new Vertex( a ), textur ? new Vertex( pg.tKordinaten->get( height ) ) : 0 );
                                height++;
                            }
                            lowp = !lowp;
                            if( !inside )
                            {
                                height = i + 1;
                                low = i - 1;
                                outList.z( p )->set( Punkt( 0, 0 ), lauf );
                                break;
                            }
                        }
                        if( fertig )
                            break;
                    }
                    if( lowL->getDreieckAnzahl() > heightL->getDreieckAnzahl() )
                    {
                        lists.z( lauf )->add( lowL );
                        tmpOutList.add( outL );
                        heightL->release();
                    }
                    else
                    {
                        lists.z( lauf )->add( heightL );
                        tmpOutList.add( outH );
                        lowL->release();
                    }
                }
                else
                    lists.z( lauf )->add( new DreieckListe< Vertex >() );
                if( fertig )
                    break;
            }
            int maxP = -1;
            int max = 0;
            for( int i = 0; i < vAnz; i++ )
            {
                if( lists.z( lauf )->hat( i ) && lists.z( lauf )->z( i )->getDreieckAnzahl() > max )
                {
                    max = lists.z( lauf )->z( i )->getDreieckAnzahl();
                    maxP = i;
                }
            }
            if( !max || maxP < 0 )
                break;
            vListen->z( p )->add( lists.z( lauf )->get( maxP ) );
            outList.z( p )->set( tmpOutList.get( maxP ), lauf );
            if( fertig )
                break;
            lauf++;
        }
        outList.z( p )->leeren();
    }
    return 1;
}

void Model2DData::removeModell() // setzt die Vertex daten zurück
{
    if( polygons )
    {
        int anz = polygons->getEintragAnzahl();
        for( int i = 0; i < anz; i++ )
        {
            if( polygons->get( i ).name )
                polygons->get( i ).name->release();
            if( polygons->get( i ).tKordinaten )
                polygons->get( i ).tKordinaten->release();
            if( polygons->get( i ).vertex )
                polygons->get( i ).vertex->release();
            if( polygons->get( i ).schwerpunkt )
                delete polygons->get( i ).schwerpunkt;
        }
        polygons = (Array<Polygon2D>*)polygons->release();
    }
    if( vListen )
        vListen = (RCArray< RCArray< DreieckListe< Vertex > > > *)vListen->release();
    outList.leeren();
    minP = Punkt( 0, 0 );
    maxP = Punkt( 0, 0 );
}

bool Model2DData::calcHitPoint( Vertex pos, Vertex dir, const char* polygonName, Vertex& hitpoint, Vertex& moveSpeed, float& rotSpeed ) const
{
    if( dir.x == 0 && dir.y == 0 )
        return 0;
    bool ret = 0;
    for( Polygon2D polygon : *polygons )
    {
        if( polygon.name->istGleich( polygonName ) )
        {
            int anz = polygon.vertex->getEintragAnzahl();
            for( int i = 0; i < anz; i++ )
            {
                Vertex a = polygon.vertex->get( i );
                Vertex b = polygon.vertex->get( (i + 1) % anz );
                b -= a;
                float offset = 0;
                if( dir.y != 0 && dir.x != 0 )
                    offset = ((a.y - pos.y) / dir.y - (a.x - pos.x) / dir.x) / (b.x / dir.x - b.y / dir.y); // solve hitpoint equasion
                else if( dir.y == 0 )
                {
                    if( b.y == 0 )
                        continue;
                    offset = (pos.y - a.y) / b.y;
                }
                else if( dir.x == 0 )
                {
                    if( b.x == 0 )
                        continue;
                    offset = (pos.x - a.x) / b.x;
                }
                Vertex point = a + (b * offset);
                if( offset >= 0 && offset <= 1 )
                {
                    float f = (point.x - pos.x) / dir.x;
                    if( !dir.x )
                        f = (point.y - pos.y) / dir.y;
                    if( (!ret || (hitpoint - pos).getLengthSq() > (point - pos).getLengthSq()) && f > 0 )
                    {
                        Vertex normal = b.CW90().normalize();
                        Vertex kNorm = Vertex( dir ).normalize();
                        moveSpeed = normal * (normal * kNorm) * dir.getLength();
                        normal = (point - *polygon.schwerpunkt).CW90().normalize();
                        Vertex rotKraft = normal * (normal * kNorm) * dir.getLength();
                        rotSpeed = ((float)sqrt( rotKraft.getLength() * (point - *polygon.schwerpunkt).getLength() ) / 180.f) * 3.14f * (normal * kNorm);
                        hitpoint = point;
                        if( isnan( moveSpeed.x ) || isnan( moveSpeed.y ) || isnan( rotSpeed ) )
                            return 0;
                        ret = 1;
                    }
                }
            }
        }
    }
    return ret;
}

bool Model2DData::split( Vertex pos, Vertex dir, char* polygonName, Polygon2D& partA, Polygon2D& partB, Punkt& posA, Punkt& posB, std::function< double() > random ) const
{
    Vertex originalDir = dir;
    bool ret = 0;
    int num = 0;
    for( Polygon2D polygon : *polygons )
    {
        if( polygon.name->istGleich( polygonName ) )
        {
            while( istPunktInnen( pos, num ) )
            {
                pos -= dir;
            }
            int anz = polygon.vertex->getEintragAnzahl();
            Vertex startPoint;
            Vertex texturSP;
            int leftI = 0;
            int rightI = 0;
            Vertex txtChpPix( 0, 0 );
            for( int i = 0; i < anz; i++ )
            {
                Vertex a = polygon.vertex->get( i );
                Vertex b = polygon.vertex->get( (i + 1) % anz );
                b -= a;
                if( (txtChpPix.x == 0 || txtChpPix.y == 0) && b.x != 0 && b.y != 0 )
                {
                    Vertex ta = polygon.tKordinaten->get( i );
                    Vertex tb = polygon.tKordinaten->get( (i + 1) % anz );
                    tb -= ta;
                    txtChpPix = Vertex( tb.x / b.x, tb.y / b.y );
                }
                float offset = 0;
                if( dir.y != 0 && dir.x != 0 )
                    offset = ((a.y - pos.y) / dir.y - (a.x - pos.x) / dir.x) / (b.x / dir.x - b.y / dir.y); // solve hitpoint equasion
                else if( dir.y == 0 )
                    offset = (pos.y - a.y) / b.y;
                else if( dir.x == 0 )
                    offset = (pos.x - a.x) / b.x;
                Vertex point = a + (b * offset);
                if( offset >= 0 && offset <= 1 )
                {
                    if( !ret || (startPoint - pos).getLengthSq() > (point - pos).getLengthSq() )
                    {
                        leftI = i;
                        rightI = (i + 1) % anz;
                        startPoint = point;
                        texturSP = polygon.tKordinaten->get( i ) + (polygon.tKordinaten->get( (i + 1) % anz ) - polygon.tKordinaten->get( i )) * offset;
                    }
                    ret = 1;
                }
            }
            if( ret )
            {
                partA.transparent = polygon.transparent;
                partA.schwerpunkt = new Vertex( 0, 0 );
                partA.tKordinaten = new Array< Vertex >();
                partA.name = new Text( polygon.name->getText() );
                partA.vertex = new Array< Vertex >();
                partB.transparent = polygon.transparent;
                partB.schwerpunkt = new Vertex( 0, 0 );
                partB.tKordinaten = new Array< Vertex >();
                partB.name = new Text( polygon.name->getText() );
                partB.vertex = new Array< Vertex >();
                *partA.schwerpunkt += startPoint;
                *partB.schwerpunkt += startPoint;
                partA.vertex->add( startPoint );
                partB.vertex->add( startPoint );
                partA.tKordinaten->add( texturSP );
                partB.tKordinaten->add( texturSP );
                int leftIE = 0;
                int rightIE = 0;
                while( 1 )
                {
                    pos = startPoint;
                    Vertex next = startPoint + dir;
                    Vertex nextT = texturSP + Vertex( dir.x * txtChpPix.x, dir.y * txtChpPix.y );
                    ret = 0;
                    bool needOne = !istPunktInnen( next );
                    int bestI = -1;
                    float bo1 = 1000;
                    float bo2 = 1000;
                    for( int i = 0; i < anz; i++ )
                    {
                        if( i == leftI )
                            continue;
                        Vertex a = polygon.vertex->get( i );
                        Vertex b = polygon.vertex->get( (i + 1) % anz );
                        b -= a;
                        float offset1 = 0;
                        if( dir.y != 0 && dir.x != 0 )
                            offset1 = ((a.y - pos.y) / dir.y - (a.x - pos.x) / dir.x) / (b.x / dir.x - b.y / dir.y); // solve hitpoint equasion
                        else if( dir.y == 0 )
                            offset1 = (pos.y - a.y) / b.y;
                        else if( dir.x == 0 )
                            offset1 = (pos.x - a.x) / b.x;
                        Vertex point = a + (b * offset1);
                        float offset2 = 0;
                        if( dir.x != 0 )
                            offset2 = (point.x - pos.x) / dir.x;
                        else
                            offset2 = (point.y - pos.y) / dir.y;
                        if( needOne && MIN( abs( bo1 ), abs( bo1 - 1 ) ) + MIN( abs( bo2 ), bo2 - 1 ) > MIN( abs( offset1 ), abs( offset1 - 1 ) ) + MIN( abs( offset2 ), abs( offset2 - 1 ) ) )
                        {
                            bo1 = offset1;
                            bo2 = offset2;
                            bestI = i;
                        }
                        if( offset1 >= 0 && offset1 <= 1 && offset2 >= 0 && offset2 <= 1 )
                        {
                            if( !ret || (startPoint - pos).getLengthSq() > (point - pos).getLengthSq() )
                            {
                                leftIE = i;
                                rightIE = (i + 1) % anz;
                                startPoint = point;
                                texturSP = polygon.tKordinaten->get( i ) + (polygon.tKordinaten->get( (i + 1) % anz ) - polygon.tKordinaten->get( i )) * offset1;
                            }
                            ret = 1;
                        }
                    }
                    if( needOne && !ret )
                    {
                        Vertex a = polygon.vertex->get( bestI );
                        Vertex b = polygon.vertex->get( (bestI + 1) % anz );
                        b -= a;
                        leftIE = bestI;
                        rightIE = (bestI + 1) % anz;
                        startPoint = a + (b * bo1);
                        texturSP = polygon.tKordinaten->get( bestI ) + (polygon.tKordinaten->get( (bestI + 1) % anz ) - polygon.tKordinaten->get( bestI )) * bo1;
                        ret = 1;
                    }
                    if( ret )
                        break;
                    *partA.schwerpunkt += next;
                    *partB.schwerpunkt += next;
                    partA.vertex->add( next );
                    partB.vertex->add( next );
                    partA.tKordinaten->add( nextT );
                    partB.tKordinaten->add( nextT );
                    startPoint = next;
                    texturSP = nextT;
                    dir = originalDir.rotation( (float)(random() - 0.5) );
                }
                *partA.schwerpunkt += startPoint;
                *partB.schwerpunkt += startPoint;
                partA.vertex->add( startPoint );
                partB.vertex->add( startPoint );
                partA.tKordinaten->add( texturSP );
                partB.tKordinaten->add( texturSP );
                for( int i = rightIE; i != leftI; i++ )
                {
                    i = i % anz;
                    if( i == leftI )
                        break;
                    *partA.schwerpunkt += polygon.vertex->get( i );
                    partA.vertex->add( polygon.vertex->get( i ) );
                    partA.tKordinaten->add( polygon.tKordinaten->get( i ) );
                }
                *partA.schwerpunkt += polygon.vertex->get( leftI );
                partA.vertex->add( polygon.vertex->get( leftI ) );
                partA.tKordinaten->add( polygon.tKordinaten->get( leftI ) );
                for( int i = leftIE; i != rightI; i-- )
                {
                    if( i < 0 )
                        i += anz;
                    if( i == rightI )
                        break;
                    *partB.schwerpunkt += polygon.vertex->get( i );
                    partB.vertex->add( polygon.vertex->get( i ) );
                    partB.tKordinaten->add( polygon.tKordinaten->get( i ) );
                }
                *partB.schwerpunkt += polygon.vertex->get( rightI );
                partB.vertex->add( polygon.vertex->get( rightI ) );
                partB.tKordinaten->add( polygon.tKordinaten->get( rightI ) );
                *partA.schwerpunkt /= (float)partA.vertex->getEintragAnzahl();
                *partB.schwerpunkt /= (float)partB.vertex->getEintragAnzahl();
                posA = (Punkt)*partA.schwerpunkt;
                posB = (Punkt)*partB.schwerpunkt;
                for( int i = 0; i < partA.vertex->getEintragAnzahl(); i++ )
                    partA.vertex->set( partA.vertex->get( i ) - *partA.schwerpunkt, i );
                for( int i = 0; i < partB.vertex->getEintragAnzahl(); i++ )
                    partB.vertex->set( partB.vertex->get( i ) - *partB.schwerpunkt, i );
                *partA.schwerpunkt = Vertex( 0, 0 );
                *partB.schwerpunkt = Vertex( 0, 0 );
            }
        }
        num++;
    }
    return ret;
}

float Model2DData::getMasse() const
{
    float m = 0;
    for( Polygon2D p : *polygons )
    {
        if( p.transparent )
            continue;
        int anz = p.vertex->getEintragAnzahl();
        if( anz < 3 )
            continue;
        Vertex p1 = p.vertex->get( anz - 1 );
        Vertex p2 = p.vertex->get( 0 );
        m += (p1.y + p2.y) * (p1.x - p2.x);
        for( int i = 1; i < anz; i++ )
        {
            p1 = p.vertex->get( i - 1 );
            p2 = p.vertex->get( i );
            m += (p1.y + p2.y) * (p1.x - p2.x);
        }
    }
    m *= 0.5f;
    return m;
}



// Inhalt der Model2D Klasse aus Model2D.h
// Konstruktor
Model2DObject::Model2DObject()
    : Object2D()
{
    rData = 0;
    textur = new RCArray< Textur2D >();
}

// Destruktor
Model2DObject::~Model2DObject()
{
    if( rData )
        rData->release();
    textur->release();
}

// nicht constant
void Model2DObject::setModel( Model2DData* mdl )
{
    if( rData )
        rData->release();
    rData = mdl;
}

void Model2DObject::setTextur( Textur2D* t )
{
    if( rData )
    {
        textur->leeren();
        for( int i = 0; i < rData->polygons->getEintragAnzahl(); i++ )
            textur->add( dynamic_cast<Textur2D*>(t->getThis()) );
    }
    t->release();
}

void Model2DObject::impuls( Vertex start, Vertex speed, float strength )
{
    start = getObjectPos( start );
    speed = getObjectDir( speed );
    if( rData )
    {
        Vertex resSpeed;
        float resRotSpeed = 0;
        Vertex hp;
        Vertex mSpeed;
        float rSpeed;
        float dist = INFINITY;
        for( Polygon2D p : *rData->polygons )
        {
            if( !p.transparent && rData->calcHitPoint( start, speed, p.name->getText(), hp, mSpeed, rSpeed ) )
            {
                float f = (hp.x - start.x) / speed.x;
                if( !speed.x )
                    f = (hp.y - start.y) / speed.y;
                if( (hp - start).getLengthSq() < dist && f > 0 )
                {
                    resSpeed = mSpeed.rotation( rotation );
                    resRotSpeed = rSpeed;
                    dist = (hp - start).getLengthSq();
                }
            }
        }
        // TODO schleife über alle polygone und translation von start und speed in Object koordinaten
        if( dist < INFINITY )
        {
            this->speed += resSpeed * strength;
            this->rSpeed += resRotSpeed * strength;
        }
    }
}

void Model2DObject::setTextur( Textur2D* t, const char* polygonName )
{
    int index = 0;
    for( Polygon2D i : *rData->polygons )
    {
        if( i.name->istGleich( polygonName ) )
            textur->set( dynamic_cast<Textur2D*>(t->getThis()), index );
        index++;
    }
    t->release();
}

void Model2DObject::render( Mat3< float >& kamMat, Bild& zRObj, const char* kamName )
{
    if( !rData || !rData->polygons || !textur )
        return;
    int num = 0;
    for( auto p : *rData->vListen )
    {
        Mat3< float > mat = kamMat * getObjectMatrix();
        if( textur->hat( num ) )
        {
            Bild* txt = textur->z( num )->zTextur();
            for( auto i = p->begin(); i && txt; i++ )
            {
                for( auto j = i->zListe()->begin(); j.hasNext() && j.next().hasNext(); j++ )
                {
                    Vertex a = mat * *j->punkt;
                    Vertex b = mat * *j.next()->punkt;
                    Vertex c = mat * *j.next().next()->punkt;
                    Punkt ta = (Punkt)Vertex( j->textur->x * (float)(txt->getBreite() - 1), j->textur->y * (float)(txt->getHeight() - 1) );
                    Punkt tb = (Punkt)Vertex( j.next()->textur->x * (float)(txt->getBreite() - 1), j.next()->textur->y * (float)(txt->getHeight() - 1) );
                    Punkt tc = (Punkt)Vertex( j.next().next()->textur->x * (float)(txt->getBreite() - 1), j.next().next()->textur->y * (float)(txt->getHeight() - 1) );
                    zRObj.drawDreieckTexturAlpha( a, b, c, ta, tb, tc, *txt );
                }
            }
        }
        num++;
    }
    /* Draws 2D Mesh
    for( auto *p = &rData->vListen->getIterator(); p && p->set; p = p->next, num++ )
    {
        Mat3< float > mat = kamMat * getObjectMatrix();
        for( auto *i = &p->var->getArray(); i && i->set; i = i->next )
        {
            for( auto *j = &i->var->zListe()->getArray(); j->next->next && j->next->next->set; j = j->next )
            {
                Vertex a = mat * *j->var->punkt;
                Vertex b = mat * *j->next->var->punkt;
                Vertex c = mat * *j->next->next->var->punkt;
                zRObj.drawLinie( a, b, 0xFFFFFFFF );
                zRObj.drawLinie( a, c, 0xFFFFFFFF );
                zRObj.drawLinie( b, c, 0xFFFFFFFF );
            }
        }
    }
    */
}

// constant
bool Model2DObject::istPunktInnen( Vertex p, bool ignoreTransparent ) const
{
    if( !rData )
        return 0;
    p -= position;
    if( p < Mat3< float >::scaling( size ) * rData->minP || p > Mat3< float >::scaling( size ) * rData->maxP || !rData->polygons )
        return 0;
    Mat3< float > mat = Mat3< float >::rotation( -rotation ) * Mat3< float >::scaling( 1 / size );
    p = mat * p;
    for( Polygon2D polygon : *rData->polygons )
    {
        if( polygon.transparent && !ignoreTransparent )
            continue;
        bool c = 0;
        for( auto point = polygon.vertex->begin(); point; point++ )
        {
            Vertex a;
            if( point.next() )
                a = point.next();
            else
                a = polygon.vertex->get( 0 );
            Vertex b = point;
            if( ((a.y >= p.y) != (b.y >= p.y)) && (p.x <= (b.x - a.x) * (p.y - a.y) / (float)(b.y - a.y) + a.x) )
                c = !c;
        }
        if( c )
            return 1;
    }
    return 0;
}

bool Model2DObject::istLinieInnen( Vertex a, Vertex b, bool ignoreTransparent ) const
{
    if( !rData || !rData->polygons )
        return 0;
    int pAnz = rData->polygons->getEintragAnzahl();
    for( int p = 0; p < pAnz; p++ )
    {
        if( rData->polygons->get( p ).transparent && !ignoreTransparent )
            continue;
        Mat3< float > mat = Mat3< float >::rotation( rotation ) * Mat3< float >::scaling( size );
        int anz = rData->polygons->get( p ).vertex->getEintragAnzahl();
        int j = anz - 1;
        for( int i = 0; i < anz; i++ )
        {
            Punkt va = mat * rData->polygons->get( p ).vertex->get( i );
            Punkt vb = mat * rData->polygons->get( p ).vertex->get( j );
            if( (Punkt)a == position + va && (Punkt)b == position + vb )
                return 1;
            if( (Punkt)a == position + vb && (Punkt)b == position + va )
                return 1;
            j = i;
        }
        Vertex len = b - a;
        Vertex speed( len.x > 0 ? 1 : -1.f, len.y > 0 ? 1 : -1.f );
        int mLen = 0;
        if( fabs( len.x ) > fabs( len.y ) )
        {
            mLen = (int)fabs( len.x );
            speed.y = len.y / (float)fabs( len.x );
        }
        else
        {
            mLen = (int)fabs( len.y );
            speed.x = len.x / (float)fabs( len.y );
        }
        int i = 1;
        bool inside = 1;
        for( Vertex vp = speed + a; (Punkt)vp != (Punkt)(b - speed) && inside && i < mLen - 1; vp += speed, i++ )
            inside &= istPunktInnen( vp, ignoreTransparent );
        if( inside )
            return 1;
    }
    return 0;
}

bool Model2DObject::istModelInnen( const Object2D* zObj, Vertex* sp, bool end, bool ignoreTransparent ) const
{
    if( !end )
    {
        if( !getBoundingBox().collidesWith( zObj->getBoundingBox() ) )
            return 0;
    }
    Mat3< float > mat = getObjectMatrix();
    for( Polygon2D polygon : *rData->polygons )
    {
        if( polygon.transparent && !ignoreTransparent )
            continue;
        int anz = polygon.vertex->getEintragAnzahl();
        for( int i = 0; i < anz; i++ )
        {
            if( zObj->istPunktInnen( mat * polygon.vertex->get( i ), ignoreTransparent ) )
            {
                if( sp )
                    *sp = mat * polygon.vertex->get( i );
                return 1;
            }
        }
    }
    if( end )
        return 0;
    return zObj->istModelInnen( this, sp, 1, ignoreTransparent );
}

Rect2< float > Model2DObject::getBoundingBox() const
{
    if( rData )
        return Rect2< float >{ (Vertex)rData->minP* size + position, (Vertex)rData->maxP* size + position };
    return Rect2< float >();
}

bool Model2DObject::calcHitPoint( Vertex pos, Vertex dir, Vertex& hitpoint ) const
{
    pos = getObjectPos( pos );
    dir = getObjectDir( dir );
    Vertex ms;
    Vertex hp;
    float rs;
    float dist = INFINITY;
    if( rData )
    {
        for( Polygon2D p : *rData->polygons )
        {
            if( !p.transparent && rData->calcHitPoint( pos, dir, p.name->getText(), hp, ms, rs ) )
            {
                float f = (hp.x - pos.x) / dir.x;
                if( !speed.x )
                    f = (hp.y - pos.y) / dir.y;
                if( (hp - pos).getLengthSq() < dist && f > 0 )
                {
                    hitpoint = getObjectMatrix() * hp;
                    dist = (hp - pos).getLengthSq();
                }
            }
        }
        if( dist < INFINITY )
            return 1;
    }
    return 0;
}

float Model2DObject::getLuftWiederstand() const
{
    if( !rData )
        return 0;
    float angle = speed.angle( Vertex( 1, 0 ) );
    float faktor = -1;
    if( getDrehung() > PI )
        faktor = -faktor;
    if( getDrehung() < -PI )
        faktor = -faktor;
    Mat3< float > m = Mat3< float >::rotation( rotation + faktor * angle ) * Mat3< float >::scaling( size );
    float yMin = INFINITY;
    float yMax = -INFINITY;
    for( Polygon2D p : *rData->polygons )
    {
        if( p.transparent )
            continue;
        for( Vertex point : *p.vertex )
        {
            Vertex v = m * point;
            if( v.y > yMax )
                yMax = v.y;
            if( v.y < yMin )
                yMin = v.y;
        }
    }
    if( yMin != INFINITY )
    {
        return yMax - yMin;
    }
    return 0;
}

float Model2DObject::getMasse() const
{
    if( !rData )
        return 0;
    return abs( rData->getMasse() * size * size );
}

// Gibt die Textur des ersten Polygons zurück
Textur2D* Model2DObject::getTextur() const
{
    return textur->get( 0 );
}

// Gibt die Textur eines Polygons zurück
//  polygonName: Der Name des Polygons
Textur2D* Model2DObject::getTextur( const char* polygonName ) const
{
    int index = 0;
    for( Polygon2D p : *rData->polygons )
    {
        if( p.name->istGleich( polygonName ) )
            return textur->get( index );
        index++;
    }
    return 0;
}

// Gibt die Textur des ersten Polygons ohne erhöhten Reference Counter zurück
Textur2D* Model2DObject::zTextur() const
{
    return textur->z( 0 );
}

// Gibt die Textur eines Polygons ohne erhöhten Reference Counter zurück
//  polygonName: Der Name des Polygons
Textur2D* Model2DObject::zTextur( const char* polygonName ) const
{
    int index = 0;
    for( Polygon2D p : *rData->polygons )
    {
        if( p.name->istGleich( polygonName ) )
            return textur->z( index );
        index++;
    }
    return 0;
}

Model2DData* Model2DObject::getModel() const
{
    return rData ? dynamic_cast<Model2DData*>(rData->getThis()) : 0;
}

Model2DData* Model2DObject::zModel() const
{
    return rData;
}



// Inhalt der Model2D Klasse aus Model2D.h
// Konstruktor
Model2D::Model2D()
    : Zeichnung()
{
    farbe = 0;
    style = 0;
    rData = 0;
    drehung = 0;
    size = 1;
    textur = new RCArray< Textur2D >;
}

// Destruktor
Model2D::~Model2D()
{
    if( rData )
        rData->release();
    textur->release();
}

// nicht constant
void Model2D::setModel( Model2DData* mdl )
{
    if( rData )
        rData->release();
    rData = mdl;
}

void Model2D::setDrehung( float drehung )
{
    this->drehung = drehung;
    while( this->drehung > PI * 2 )
        this->drehung -= (float)PI * 2;
    while( this->drehung < 0 )
        this->drehung += (float)PI * 2;
    rend = 1;
}

void Model2D::addDrehung( float drehung )
{
    this->drehung += drehung;
    while( this->drehung > PI * 2 )
        this->drehung -= (float)PI * 2;
    while( this->drehung < 0 )
        this->drehung += (float)PI * 2;
    rend = 1;
}

void Model2D::setSize( float size )
{
    this->size = size;
    rend = 1;
}

void Model2D::addSize( float size )
{
    this->size += size;
    rend = 1;
}

void Model2D::setTextur( Textur2D* t )
{
    if( rData )
    {
        for( int i = 0; i < rData->polygons->getEintragAnzahl(); i++ )
            textur->set( dynamic_cast<Textur2D*>(t->getThis()), i );
    }
    t->release();
}

void Model2D::setTextur( Textur2D* t, const char* polygonName )
{
    int index = 0;
    for( Polygon2D p : *rData->polygons )
    {
        if( p.name->istGleich( polygonName ) )
            textur->set( dynamic_cast<Textur2D*>(t->getThis()), index );
        index++;
    }
    t->release();
}

void Model2D::setFarbe( int f )
{
    farbe = f;
    rend = 1;
}

bool Model2D::tick( double tickVal )
{
    return Zeichnung::tick( tickVal );
}

void Model2D::render( Bild& zRObj )
{
    if( !rData || hatStyleNicht( Model2D::Style::Sichtbar ) || !rData->polygons )
        return;
    Zeichnung::render( zRObj );
    int num = 0;
    for( auto p : *rData->vListen )
    {
        Mat3< float > mat = Mat3< float >::translation( pos ) * Mat3< float >::rotation( drehung ) * Mat3< float >::scaling( size );
        if( hatStyle( Model2D::Style::Textur ) )
        {
            if( !textur || !textur->z( num ) || !textur->z( num )->zTextur() || !rData->polygons->get( num ).tKordinaten )
            {
                for( auto i : *p )
                {
                    for( auto j = i->zListe()->begin(); j.hasNext() && j.next().hasNext(); j++ )
                    {
                        Vertex a = mat * *j->punkt;
                        Vertex b = mat * *j.next()->punkt;
                        Vertex c = mat * *j.next().next()->punkt;
                        if( hatStyle( Model2D::Style::Alpha ) )
                            zRObj.drawDreieckAlpha( a, b, c, farbe );
                        else
                            zRObj.drawDreieck( a, b, c, farbe );
                    }
                }
            }
            else
            {
                Bild* txt = textur->z( num )->zTextur();
                for( auto i : *p )
                {
                    for( auto j = i->zListe()->begin(); j.hasNext() && j.next().hasNext(); j++ )
                    {
                        Vertex a = mat * *j->punkt;
                        Vertex b = mat * *j.next()->punkt;
                        Vertex c = mat * *j.next().next()->punkt;
                        Punkt ta = (Punkt)Vertex( j->textur->x * (float)(txt->getBreite() - 1), j->textur->y * (float)(txt->getHeight() - 1) );
                        Punkt tb = (Punkt)Vertex( j.next()->textur->x * (float)(txt->getBreite() - 1), j.next()->textur->y * (float)(txt->getHeight() - 1) );
                        Punkt tc = (Punkt)Vertex( j.next().next()->textur->x * (float)(txt->getBreite() - 1), j.next().next()->textur->y * (float)(txt->getHeight() - 1) );
                        if( hatStyle( Model2D::Style::Alpha ) )
                            zRObj.drawDreieckTexturAlpha( a, b, c, ta, tb, tc, *txt );
                        else
                            zRObj.drawDreieckTextur( a, b, c, ta, tb, tc, *txt );
                    }
                }
            }
        }
        if( hatStyle( Model2D::Style::Mesh ) )
        {
            for( auto i : *p )
            {
                for( auto j = i->zListe()->begin(); j.hasNext() && j.next().hasNext(); j++ )
                {
                    Vertex a = mat * *j->punkt;
                    Vertex b = mat * *j.next()->punkt;
                    Vertex c = mat * *j.next().next()->punkt;
                    if( hatStyle( Model2D::Style::Alpha ) )
                    {
                        zRObj.drawLinieAlpha( a, b, farbe );
                        zRObj.drawLinieAlpha( b, c, farbe );
                        zRObj.drawLinieAlpha( c, a, farbe );
                    }
                    else
                    {
                        zRObj.drawLinie( a, b, farbe );
                        zRObj.drawLinie( b, c, farbe );
                        zRObj.drawLinie( c, a, farbe );
                    }
                }
            }
        }
        if( hatStyle( Model2D::Style::Rahmen ) )
        {
            auto beg = rData->polygons->get( num ).vertex->begin();
            if( beg )
            {
                Vertex letzter;
                for( auto e = beg; e && e.hasNext(); e++ )
                {
                    if( hatStyle( Model2D::Style::Alpha ) )
                        zRObj.drawLinieAlpha( mat * e._, mat * e.next()._, farbe );
                    else
                        zRObj.drawLinie( mat * e._, mat * e.next()._, farbe );
                    letzter = e.next();
                }
                if( beg.hasNext() )
                {
                    if( hatStyle( Model2D::Style::Alpha ) )
                        zRObj.drawLinieAlpha( mat * letzter, mat * beg._, farbe );
                    else
                        zRObj.drawLinie( mat * letzter, mat * beg._, farbe );
                }
            }
        }
        num++;
    }
}

// constant
float Model2D::getDrehung() const
{
    return drehung;
}

float Model2D::getSize() const
{
    return size;
}

// Gibt zurück, ob ein Punkt in dem Model enthalten ist
//  p: Der Punkt
bool Model2D::istPunktInnen( int x, int y ) const
{
    return istPunktInnen( Vertex( (float)x, (float)y ) );
}

bool Model2D::istPunktInnen( Vertex p ) const
{
    if( !rData )
        return 0;
    p -= pos;
    if( p < Mat3< float >::scaling( size ) * rData->minP || p > Mat3< float >::scaling( size ) * rData->maxP || !rData->polygons )
        return 0;
    for( Polygon2D polygon : *rData->polygons )
    {
        if( polygon.transparent )
            continue;
        Mat3< float > mat = Mat3< float >::rotation( drehung ) * Mat3< float >::scaling( size );
        int anz = polygon.vertex->getEintragAnzahl();
        bool c = 0;
        int j = anz - 1;
        for( int i = 0; i < anz; i++ )
        {
            Vertex a = mat * polygon.vertex->get( i );
            Vertex b = mat * polygon.vertex->get( j );
            if( ((a.y >= p.y) != (b.y >= p.y)) && (p.x <= (b.x - a.x) * (p.y - a.y) / (float)(b.y - a.y) + a.x) )
                c = !c;
            j = i;
        }
        if( c )
            return 1;
    }
    return 0;
}

bool Model2D::istLinieInnen( Vertex a, Vertex b ) const
{
    if( !rData || !rData->polygons )
        return 0;
    int pAnz = rData->polygons->getEintragAnzahl();
    for( int p = 0; p < pAnz; p++ )
    {
        if( rData->polygons->get( p ).transparent )
            continue;
        Mat3< float > mat = Mat3< float >::rotation( drehung ) * Mat3< float >::scaling( size );
        int anz = rData->polygons->get( p ).vertex->getEintragAnzahl();
        int j = anz - 1;
        for( int i = 0; i < anz; i++ )
        {
            Punkt va = mat * rData->polygons->get( p ).vertex->get( i );
            Punkt vb = mat * rData->polygons->get( p ).vertex->get( j );
            if( (Punkt)a == pos + va && (Punkt)b == pos + vb )
                return 1;
            if( (Punkt)a == pos + vb && (Punkt)b == pos + va )
                return 1;
            j = i;
        }
        Vertex len = b - a;
        Vertex speed( len.x > 0 ? 1 : -1.f, len.y > 0 ? 1 : -1.f );
        int mLen = 0;
        if( fabs( len.x ) > fabs( len.y ) )
        {
            mLen = (int)fabs( len.x );
            speed.y = len.y / (float)fabs( len.x );
        }
        else
        {
            mLen = (int)fabs( len.y );
            speed.x = len.x / (float)fabs( len.y );
        }
        int i = 1;
        bool inside = 1;
        for( Vertex vp = speed + a; (Punkt)vp != (Punkt)(b - speed) && inside && i < mLen - 1; vp += speed, i++ )
            inside &= istPunktInnen( vp );
        if( inside )
            return 1;
    }
    return 0;
}

bool Model2D::istModelInnen( const Model2D* zMdl, bool end ) const
{
    if( !end )
    {
        Vertex min = (Vertex)rData->minP * size + pos;
        Vertex max = (Vertex)rData->maxP * size + pos;
        Vertex min2 = (Vertex)zMdl->zModel()->minP * zMdl->getSize() + zMdl->getPosition();
        Vertex max2 = (Vertex)zMdl->zModel()->maxP * zMdl->getSize() + zMdl->getPosition();
        if( max.x < min2.x || min.x > max2.x || max.y < min2.y || min.y > max2.y )
            return 0;
    }
    Mat3< float > mat = Mat3< float >::translation( pos ) * Mat3< float >::rotation( drehung ) * Mat3< float >::scaling( size );
    for( Polygon2D polygon : *rData->polygons )
    {
        if( polygon.transparent )
            continue;
        int anz = polygon.vertex->getEintragAnzahl();
        for( int i = 0; i < anz; i++ )
        {
            if( zMdl->istPunktInnen( mat * polygon.vertex->get( i ) ) )
                return 1;
        }
    }
    if( end )
        return 0;
    return zMdl->istModelInnen( this, 1 );
}

Model2DData* Model2D::getModel() const
{
    return rData ? dynamic_cast<Model2DData*>(rData->getThis()) : 0;
}

Model2DData* Model2D::zModel() const
{
    return rData;
}