///////////////////////////////////////////////////////////
//                                                       //
// PC MAGAZIN - Demo Projekt                             //
//                                                       //
// Dies ist die Grafikbibliothek, die Funktionen         //
// bereitstellt, um Windowsfenster für Demoeffekte zu    //
// nutzen                                                //
//                                                       //
// Carsten Dachsbacher                                   //
// Nils Pipenbrinck                                      //
//                                                       //
///////////////////////////////////////////////////////////

#include "demo.h"

// Globale Tabellen und Variablen

// 16 Bit Farb Tabellen
int     Rtab[256];
int     Gtab[256];
int     Btab[256];
int     Fenster_Modus = FENSTER;    // Darstellungsmodus

// Statische Variablen für die Grafiklibrary und das
// Window-Handling
HWND         DemoHWND;      // Handle des Fensters
static HDC          DemoHDC;       // Devicecontext
static BITMAPINFO  *bitmapinfo;
volatile BOOL       DemoRunning;   // Status des Demothread
unsigned long ThreadID;
HANDLE ThreadHandle;

// Zeitpunkt des Demo-Starts
static volatile unsigned long   DemoStartTime;

// Gibt den Farbcode für ein RGB-Tripel zurück
unsigned short ColorCode(int r, int g, int b)
{
  r = ( r > 255 ) ? 255 : ( r < 0 ) ? 0 : r;
  g = ( g > 255 ) ? 255 : ( g < 0 ) ? 0 : g;
  b = ( b > 255 ) ? 255 : ( b < 0 ) ? 0 : b;
  return (unsigned short)( ( ( r >> 3 ) << 11 ) |
                           ( ( g >> 2 ) << 5 )  |
                             ( b >> 3 ) );
}

static BOOL InitGraphic( void )
{
    // Vorbereiten eines BitmapInfoHeaders für
    // alle Grafikausgaben.
    int bisize = sizeof( BITMAPINFOHEADER );
    bitmapinfo = (BITMAPINFO *)malloc( bisize + 12 );
    ZeroMemory( &bitmapinfo->bmiHeader, bisize );

    // BitmapInfoHeader eines normalen 16 Bit Bitmaps
    // wie wir es brauchen erzeugen
    bitmapinfo->bmiHeader.biSize        = bisize;
    bitmapinfo->bmiHeader.biWidth       = SCREEN_X;
    bitmapinfo->bmiHeader.biHeight      = -SCREEN_Y;
    bitmapinfo->bmiHeader.biPlanes      = 1;
    bitmapinfo->bmiHeader.biBitCount    = 16;
    bitmapinfo->bmiHeader.biCompression = BI_BITFIELDS;
    // Farb-Felder des 16-Bit Bitmaps setzen.
    *((long*) bitmapinfo->bmiColors +0) = 0xF800;
    *((long*) bitmapinfo->bmiColors +1) = 0x07E0;
    *((long*) bitmapinfo->bmiColors +2) = 0x001F;

    // Berechnen der 16 Bit Farbtabelle
    for ( int i = 0; i < 256; i++ )
    {
        Rtab[ i ] = ColorCode( i, 0, 0 );
        Gtab[ i ] = ColorCode( 0, i, 0 );
        Btab[ i ] = ColorCode( 0, 0, i );
    }
    return 1;
}


void CloseGraphic( void )
{
    free( bitmapinfo );
}


void BlitGraphic( void *buf )
{
    if ( !DemoHDC ) return;

    switch ( Fenster_Modus )
    {
    case FENSTER:
      SetDIBitsToDevice( DemoHDC, 0, 0, SCREEN_X, SCREEN_Y,
                         0, 0, 0, SCREEN_Y, buf, bitmapinfo,
                         DIB_RGB_COLORS );
      break;
    default:
      RECT r;
      GetClientRect( DemoHWND, &r );
      StretchDIBits( DemoHDC, 0, 0, r.right, r.bottom, 0, 0,
                     SCREEN_X, SCREEN_Y, buf, bitmapinfo,
                     DIB_RGB_COLORS, SRCCOPY );
      break;
    }
}

unsigned long GetDemoTime( void )
{
    return GetTickCount() - DemoStartTime;
}


long CALLBACK WindowProc( HWND hWnd, UINT message,
                          WPARAM wParam, LPARAM lParam )
{
    // Dies ist die Message Funktion des Demo-Fensters.
    // Da das Fenster selbst sogut wie keine Funktionalität
    // haben muß ist diese Funktion sehr kurz
    switch (message)
    {
        case WM_DESTROY:
        case WM_KEYDOWN:
			int ecode;
			GetExitCodeThread( ThreadHandle, (LPDWORD)&ecode );
			TerminateThread( ThreadHandle, ecode );
			demoquit();
            DemoRunning = 0;
            ReleaseDC( DemoHWND, DemoHDC );
            DemoHDC = 0;
            PostQuitMessage( 0 );
            break;
    }
    return DefWindowProc( hWnd, message, wParam, lParam );
}

// Erzeugen des Fensters und anderer Windows spezifischer
// Initialisierungen
static BOOL InitDemoWindow(int nCmdShow, HINSTANCE instance)
{
    // zunächst wird eine eigene Window-Klasse für
    // unsere Ausgabe erzeugt.
    WNDCLASS        wc;
    RECT            r;
    unsigned long   style;
    unsigned long   styleex;

    wc.style         = CS_BYTEALIGNCLIENT;
    wc.lpfnWndProc   = WindowProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = instance;
    wc.hIcon         =
        LoadIcon  (instance, MAKEINTRESOURCE(IDI_DEMO_ICON));
    wc.hCursor       =
        LoadCursor(instance, MAKEINTRESOURCE(IDC_BLANK_CURSOR));
    wc.hbrBackground = GetStockObject (BLACK_BRUSH);
    wc.lpszMenuName  = 0;
    wc.lpszClassName = "demo";
    RegisterClass( &wc );

    r.top   = CW_USEDEFAULT;
    r.left  = CW_USEDEFAULT;
    r.right = SCREEN_X;
    r.bottom= SCREEN_Y +
              GetSystemMetrics( SM_CYCAPTION ) -
              GetSystemMetrics( SM_CYBORDER );

    switch ( Fenster_Modus )
    {
    case FENSTER:
        style     = WS_OVERLAPPED | WS_SYSMENU;
        styleex   = 0;
        break;
    case SKALIERBAR:
        style     = WS_OVERLAPPED | WS_MINIMIZEBOX |
                    WS_SYSMENU | WS_SIZEBOX |
                    WS_MAXIMIZEBOX;
        styleex   = 0;
        r.right   += 2 * GetSystemMetrics( SM_CXSIZEFRAME );
        r.bottom  += 2 * GetSystemMetrics( SM_CYSIZEFRAME );
        break;
    case VOLLBILD:
        r.top    = 0;
        r.left   = 0;
        r.right  = GetSystemMetrics( SM_CXSCREEN );
        r.bottom = GetSystemMetrics( SM_CYSCREEN );
        style    = WS_POPUP;
        styleex  = WS_EX_TOPMOST;
        break;
    }

    // jetzt wird das Fenster für das Demo
    // selbst erzeugt.
    DemoHWND =
        CreateWindowEx( styleex, "demo", "PC Magazin Demo",
                        style, r.left, r.top, r.right,
                        r.bottom, 0, 0, instance, 0);

    if ( !DemoHWND ) return 0;

    ShowWindow( DemoHWND, nCmdShow );  // Fenster anzeigen
    DemoHDC = GetDC( DemoHWND );       // Device Context

    return 1;
}


// Start-Funktionen für den Demo-Thread
#ifdef __WATCOMC__

// Demo-Thread Entry für Watcom C/C++
void DemoThreadStartFunction (void *parm)
{
    // Vermeide Compiler-Warnungen
    parm = parm;
    // jetzt geht's los:
    DemoRunning = 1;
    DemoStartTime = GetTickCount();
    demomain();
    DemoRunning = 0;
    PostMessage( DemoHWND, WM_CLOSE, 0, 0 );
    _endthread();
}

#else

// Demo-Thread Entry für Visual C++ und Borland C++
unsigned long CALLBACK DemoThreadStartFunction( void * )
{
    DemoRunning = 1;
    DemoStartTime = GetTickCount();
    demomain();
    DemoRunning = 0;
    PostMessage( DemoHWND, WM_CLOSE, 0, 0 );
    return 0;
}

#endif

// Hauptprogramm
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst,
                   LPSTR lpCmdLine, int nCmdShow)
{
    MSG             message;

    // Vermeinde Compiler-Warnungen
    hPrevInst = hPrevInst;
    lpCmdLine = lpCmdLine;

    
    // optional, wenn das Demo in das Verzeichnis des demo.exe wechseln soll
    /*
    // In das Verzeichniss des Executables wechseln
    char        *AppFileName = new char[256];
    char        *backslash;

    GetModuleFileName (hInstance, AppFileName, 256);
    backslash = strrchr (AppFileName, '\\');
    if (backslash)
    {
      *backslash = 0;                    // Beende Filenamen am letzen Backslash-Zeichen
      strcat (AppFileName, "\\");        // Füge ein Backslash an das Ende des Pfades
      SetCurrentDirectory (AppFileName); // Und als Verzeichniss setzen.
    }
    delete AppFileName;
    */
    // Tabellen und Strukturen der Bibliothek initialisieren
    if ( !InitGraphic() ) return 0;

    // Hier darf das Demo sich erst einmal initialisieren
    if ( !demoinit() ) return 0;

    // Fenster Erzeugen und Zeigen
    if ( !InitDemoWindow( nCmdShow, hInstance ) ) return 0;

    // Jetzt kann nichts mehr schiefgehen
    // der Haupt-Thread des Demos kann gestartet werden

#ifdef __WATCOMC__
    ThreadID = _beginthread( DemoThreadStartFunction,
                             0x100000, NULL );
#else
    ThreadHandle = CreateThread(   0, 4096, DemoThreadStartFunction,
								   0, 0, &ThreadID);
#endif

    // Program in der Window Hauptschleife ausführen,
    // bis das Fenster geschlossen wird.
    while ( GetMessage( &message, 0, 0, 0 ) )
    {
        Sleep (40);
        TranslateMessage( &message );
        DispatchMessage( &message );
    }
    // Um den Demo-Thread braucht man sich nicht zu kümmern
    // Sollte er noch laufen wird er automatisch beendet
    return message.wParam;
}
