#include <Globals.h>
#include <Fenster.h>
#include <MausEreignis.h>
#include <TastaturEreignis.h>
#include <Bildschirm.h>
#include <RenderThread.h>
#include <Zeichnung3D.h>
#include <Model3D.h>
#include <Bild.h>
#include <Zeichnung.h>
#include <Mat4.h>
#include <time.h>
#include <Render3D.h>
#include <Welt3D.h>
#include <Kam3D.h>
#include <iostream>
#include <Mat4.h>
#include <Textur.h>
#include <DXBuffer.h>
#include <Cube.h>
#include <Textur.h>
#include <TexturList.h>
#include <Klient.h>
#include <Animation3D.h>
#include <FrameworkMath.h>

using namespace Framework;

void FClose( void *p, void *zF )
{
    StopNachrichtenSchleife( ( (WFenster*)zF )->getFensterHandle() );
}

int kAnz = 10;
Knochen **knochen;

Model3D *createKnochenTestModel()
{
    Model3DData *model = new Model3DData();
    float stdSize = 50;
    float left, right, top, bottom;
    // Calculate the screen coordinates of the left side of the bitmap.
    left = (float)( ( stdSize / 2.0 ) * -1 );
    // Calculate the screen coordinates of the right side of the bitmap.
    right = left + (float)stdSize;
    // Calculate the screen coordinates of the top of the bitmap.
    top = (float)( stdSize / 2.0 );
    // Calculate the screen coordinates of the bottom of the bitmap.
    bottom = top - (float)stdSize;
    float front = -stdSize / 2;
    float back = front + stdSize;

    Vertex3D *vertecies = new Vertex3D[ kAnz * 4 ];
    for( int k = 0; k < kAnz; k++ )
    {
        for( int i = k * 4; i < 4 + k * 4; i++ )
            vertecies[ i ].knochenId = k;
        if( k == 0 )
        {
            vertecies[ 0 + k * 4 ].pos = Vec3<float >( left, top, 0 );
            vertecies[ 0 + k * 4 ].tPos = Vec2< float >( 0.f, 0.f );
            vertecies[ 1 + k * 4 ].pos = Vec3<float >( right, top, 0 );
            vertecies[ 1 + k * 4 ].tPos = Vec2< float >( 1.f, 0.f );
            vertecies[ 2 + k * 4 ].pos = Vec3<float >( left, bottom, 0 );
            vertecies[ 2 + k * 4 ].tPos = Vec2< float >( 0.f, 1.f );
            vertecies[ 3 + k * 4 ].pos = Vec3<float >( right, bottom, 0 );
            vertecies[ 3 + k * 4 ].tPos = Vec2< float >( 1.f, 1.f );
        }
        else
        {
            vertecies[ 0 + k * 4 ].pos = Vec3<float >( left, top, 100 );
            vertecies[ 0 + k * 4 ].tPos = Vec2< float >( 0.f, 0.f );
            vertecies[ 1 + k * 4 ].pos = Vec3<float >( right, top, 100 );
            vertecies[ 1 + k * 4 ].tPos = Vec2< float >( 1.f, 0.f );
            vertecies[ 2 + k * 4 ].pos = Vec3<float >( left, bottom, 100 );
            vertecies[ 2 + k * 4 ].tPos = Vec2< float >( 0.f, 1.f );
            vertecies[ 3 + k * 4 ].pos = Vec3<float >( right, bottom, 100 );
            vertecies[ 3 + k * 4 ].tPos = Vec2< float >( 1.f, 1.f );
        }
    }
    model->setVertecies( vertecies, kAnz * 4 );

    for( int k = 0; k < kAnz; k++ )
    {
        if( k == 0 || k == kAnz - 1 )
        {
            // front side
            Polygon3D *p = new Polygon3D();
            p->indexAnz = 6;
            p->indexList = new int[ p->indexAnz ];
            p->indexBuffer->setLength( p->indexAnz * 4 );
            p->indexBuffer->setData( p->indexList );
            p->indexList[ 0 ] = 0 + k * 4;
            p->indexList[ 1 ] = 3 + k * 4;
            p->indexList[ 2 ] = 2 + k * 4;
            p->indexList[ 3 ] = 0 + k * 4;
            p->indexList[ 4 ] = 1 + k * 4;
            p->indexList[ 5 ] = 3 + k * 4;
            model->addPolygon( p );
        }
        if( k != kAnz - 1 )
        {
            // right side
            Polygon3D *p = new Polygon3D();
            p->indexAnz = 6;
            p->indexList = new int[ p->indexAnz ];
            p->indexBuffer->setLength( p->indexAnz * 4 );
            p->indexBuffer->setData( p->indexList );
            p->indexList[ 0 ] = 1 + k * 4;
            p->indexList[ 1 ] = 7 + k * 4;
            p->indexList[ 2 ] = 3 + k * 4;
            p->indexList[ 3 ] = 1 + k * 4;
            p->indexList[ 4 ] = 5 + k * 4;
            p->indexList[ 5 ] = 7 + k * 4;
            model->addPolygon( p );
            // left side
            p = new Polygon3D();
            p->indexAnz = 6;
            p->indexList = new int[ p->indexAnz ];
            p->indexBuffer->setLength( p->indexAnz * 4 );
            p->indexBuffer->setData( p->indexList );
            p->indexList[ 0 ] = 0 + k * 4;
            p->indexList[ 1 ] = 2 + k * 4;
            p->indexList[ 2 ] = 6 + k * 4;
            p->indexList[ 3 ] = 0 + k * 4;
            p->indexList[ 4 ] = 6 + k * 4;
            p->indexList[ 5 ] = 4 + k * 4;
            model->addPolygon( p );
            // top side
            p = new Polygon3D();
            p->indexAnz = 6;
            p->indexList = new int[ p->indexAnz ];
            p->indexBuffer->setLength( p->indexAnz * 4 );
            p->indexBuffer->setData( p->indexList );
            p->indexList[ 0 ] = 4 + k * 4;
            p->indexList[ 1 ] = 1 + k * 4;
            p->indexList[ 2 ] = 0 + k * 4;
            p->indexList[ 3 ] = 4 + k * 4;
            p->indexList[ 4 ] = 5 + k * 4;
            p->indexList[ 5 ] = 1 + k * 4;
            model->addPolygon( p );
            // down side
            p = new Polygon3D();
            p->indexAnz = 6;
            p->indexList = new int[ p->indexAnz ];
            p->indexBuffer->setLength( p->indexAnz * 4 );
            p->indexBuffer->setData( p->indexList );
            p->indexList[ 0 ] = 6 + k * 4;
            p->indexList[ 1 ] = 2 + k * 4;
            p->indexList[ 2 ] = 3 + k * 4;
            p->indexList[ 3 ] = 6 + k * 4;
            p->indexList[ 4 ] = 3 + k * 4;
            p->indexList[ 5 ] = 7 + k * 4;
            model->addPolygon( p );
        }
    }

    Model3DTextur *textur = new Model3DTextur();
    for( int k = 0; k < kAnz; k++ )
    {
        Textur *t = new Textur();
        Bild *tb = new Bild();
        tb->neuBild( 100, 100, 0xFF000000 | (rand() % 0xFFFFFF) );
        t->setBildZ( tb );
        textur->setPolygonTextur( k * 4, t->getThis() );
        textur->setPolygonTextur( k * 4 + 1, t->getThis() );
        textur->setPolygonTextur( k * 4 + 2, t->getThis() );
        textur->setPolygonTextur( k * 4 + 3, t );
    }
    
    Model3D *m3d = new Model3D();
    m3d->setModelDaten( model );
    m3d->setModelTextur( textur );
    
    Skelett *s = new Skelett();
    knochen = new Knochen*[ kAnz ];
    for( int k = 0; k < kAnz; k++ )
    {
        Knochen *kn = new Knochen( k );
        if( k != 0 )
            kn->setPosition( Vec3<float>( 0, 0, 100 ) );
        s->addKnochen( kn, k - 1 );
        knochen[ k ] = kn;
    }
    m3d->setSkelettZ( s );

    Animation3D *animation = new Animation3D();
    animation->addKeyFrame( 2, 20, Vec3<float>( 0, 0, 100 ), Vec3<float>( 2 * 3.14, 2 * 3.14, 2 * 3.14 ) );
    animation->addKeyFrame( 2, 0, Vec3<float>( 0, 0, 100 ), Vec3<float>( 0, 0, 0 ) );
    animation->addKeyFrame( 4, 20, Vec3<float>( 0, 0, 100 ), Vec3<float>( 2 * 3.14, 2 * 3.14, 2 * 3.14 ) );
    animation->addKeyFrame( 4, 0, Vec3<float>( 0, 0, 100 ), Vec3<float>( 0, 0, 0 ) );
    animation->addKeyFrame( 6, 20, Vec3<float>( 0, 0, 100 ), Vec3<float>( 2 * 3.14, 2 * 3.14, 2 * 3.14 ) );
    animation->addKeyFrame( 6, 0, Vec3<float>( 0, 0, 100 ), Vec3<float>( 0, 0, 0 ) );
    animation->addKeyFrame( 8, 20, Vec3<float>( 0, 0, 100 ), Vec3<float>( 2 * 3.14, 2 * 3.14, 2 * 3.14 ) );
    animation->addKeyFrame( 8, 0, Vec3<float>( 0, 0, 100 ), Vec3<float>( 0, 0, 0 ) );
    m3d->addAnimation( animation, 5 );

    return m3d;
}

int _select = 0;
int _achse = 0;

bool taste( void *p, void *o, TastaturEreignis t )
{
    Vec3<float> r(0, 0, 0);
    if( _achse == 0 )
        r.x = 0.001;
    if( _achse == 1 )
        r.y = 0.001;
    if( _achse == 2 )
        r.z = 0.001;
    if( t.id == TE_Press )
    {
        switch( t.taste )
        {
        case '0':
            _select = 0;
            break;
        case '1':
            _select = 1;
            break;
        case '2':
            _select = 2;
            break;
        case '3':
            _select = 3;
            break;
        case '4':
            _select = 4;
            break;
        case '5':
            _select = 5;
            break;
        case '6':
            _select = 6;
            break;
        case '7':
            _select = 7;
            break;
        case '8':
            _select = 8;
            break;
        case '9':
            _select = 9;
            break;
        case 'x':
            _achse = 0;
            break;
        case 'y':
            _achse = 1;
            break;
        case 'z':
            _achse = 2;
            break;
        case '+':
            knochen[ _select ]->setDrehung( knochen[ _select ]->getDrehung() + r );
            break;
        case '-':
            knochen[ _select ]->setDrehung( knochen[ _select ]->getDrehung() - r );
            break;
        }
    }
    return false;
}

int main()
{
    initFramework();

    Welt3D *welt = new Welt3D();
    Kam3D *kamera = new Kam3D();
    kamera->setPosition( Vec3< float >( 0, 75, 0 ) );
    kamera->setWelt( welt );
    kamera->setBildschirmSize( 1600, 900 );

    WFenster *f = new WFenster();
    WNDCLASS fc = F_Normal( 0 );
    fc.lpszClassName = "Test";
    f->erstellen( WS_OVERLAPPEDWINDOW, fc );
    f->setSize( 1600, 900 );
    f->setVSchließAktion( FClose );
    f->setMausAktion( _ret1ME );
    f->setTastaturAktion( taste );
    f->setAnzeigeModus( 1 );

    Bildschirm3D *bs = new Bildschirm3D( f->getThis() );
    f->setBildschirm( bs->getThis() );
    bs->setFill( 1 );
    bs->setFillFarbe( 0 );
    //bs->setTestRend( 0 );
    bs->addKamera( kamera );

    Model3D *sat = createKnochenTestModel();
    welt->addZeichnung( sat );
    /*
    int anzX = 100;
    int anzZ = 100;

    for( int i = 0; i < 100; i++ )
    {
        Bild *b = new Bild();
        b->neuBild( 100, 100, 0xFF000000 | ( ( rand() % 0xFF ) << 16 ) | ( ( rand() % 0xFF ) << 8 ) | ( rand() % 0xFF ) );
        b->fillCircle( 50, 50, 15, 0 );
        Textur *t = new Textur();
        t->setBildZ( b );
        zTexturRegister()->addTextur( t, Text( i ) );
    }

    Cube **test = new Cube*[ anzX * anzZ ];
    for( int i = 0; i < anzX; i++ )
    {
        for( int j = 0; j < anzZ; j++ )
        {
            test[ i + j * anzZ ] = new Cube( 100 );
            test[ i + j * anzZ ]->setPosition( i * 100.f - 100 * ( anzX / 2 ), 0, j * 100.f - 100 * ( anzZ / 2 ) );
            test[ i + j * anzZ ]->setTextur( rand() % 100 );
            test[ i + j * anzZ ]->setAlpha( 1 );
            test[ i + j * anzZ ]->setDrehung( Vec3< float >( ( rand() % 10 ) / 10.f * 6.28f, ( rand() % 10 ) / 10.f * 6.28f, ( rand() % 10 ) / 10.f * 6.28f ) );
            welt->addZeichnung( test[ i + j * anzZ ] );
        }
    }*/

    ZeichnungHintergrund *zh = new ZeichnungHintergrund();
    zh->setStyle( ZeichnungHintergrund::Style::Sichtbar | ZeichnungHintergrund::Style::Hintergrund | ZeichnungHintergrund::Style::Buffered );
    zh->setHintergrundFarbe( 0 );
    zh->setAlphaFeldFarbe( 0xA0FF0000 );
    zh->setAlphaFeldStrength( 5 );
    zh->setSize( 1580, 860 );
    zh->setPosition( 10, 10 );
    bs->addMember( zh );

    RenderTh *r = new RenderTh();
    r->setBildschirm( bs->getThis() );
    r->setMaxFps( 60 );
    r->beginn();

    StartNachrichtenSchleife();

    r->beenden();
    r->release();
    /*
    for( int i = 0; i < anzX * anzZ; i++ )
        test[ i ]->release();
    delete[] test;*/
    delete zh;

    f->setBildschirm( 0 );
    bs->release();
    f->release();
    welt->release();
    kamera->release();

    releaseFramework();
    return 0;
}