/*
 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 *%                                                                        %
 *%     Copyright (C) 1994 by WATCOM International Inc.                    %
 *%     All rights reserved.                                               %
 *%                                                                        %
 *%     Permission is granted to anyone to use this example program for    %
 *%     any purpose on any computer system, subject to the following       %
 *%     restrictions:                                                      %
 *%                                                                        %
 *%     1. This example is provided on an "as is" basis, without warranty. %
 *%        You indemnify, hold harmless and defend WATCOM from and against %
 *%        any claims or lawsuits, including attorney's, that arise or     %
 *%        result from the use or distribution of this example, or any     %
 *%        modification thereof.                                           %
 *%                                                                        %
 *%     2. You may not remove, alter or suppress this notice from this     %
 *%        example program or any modification thereof.                    %
 *%                                                                        %
 *%                                                                        %
 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 */

#define INCL_PM

#include <os2.h>
#include <string.h>
#include "drawmenu.h"
#include "button.h"
#include "room.h"
#include "threed.h"

static HWND hwnd;

static HAB Main_hab;

static HMQ Main_hmq;

static three_d_handle hThree_d;

static RECTL Draw_area;

#ifdef DRAW_ALL_AT_ONCE
static HDC Hdc;
static HPS Mem_dc;
static HBITMAP Draw_bitmap = NULLHANDLE;
static HBITMAP Old_bitmap = NULLHANDLE;
#endif


static int Button_height;

static int Auto_run = 0;

static void add_wrap( three_d_attr attr, int add, int minimum, int maximum )
{
    int         start;

    start = three_d_get( hThree_d, attr );
    start += add;
    while( start >= maximum ) {
        start -= maximum - minimum;
    }
    while( start < minimum ) {
        start += maximum - minimum;
    }
    three_d_set( hThree_d, attr, start );
}

static void add_range( three_d_attr attr, int add, int minimum, int maximum )
{
    int         start;

    start = three_d_get( hThree_d, attr );
    start += add;
    if( start >= maximum ) {
        three_d_set( hThree_d, attr, maximum );
    } else if( start <= minimum ) {
        three_d_set( hThree_d, attr, minimum );
    } else {
        three_d_set( hThree_d, attr, start );
    }
}

void draw_box(
    float       x1,
    float       x2,
    float       y1,
    float       y2,
    float       z1,
    float       z2,
    unsigned    red,
    unsigned    green,
    unsigned    blue )
{
    three_d_box( hThree_d, x1, x2, y1, y2, z1, z2, red, green, blue );
}

void new_display( void )
{
#ifdef DRAW_ALL_AT_ONCE
    GpiSetBitmap( Mem_dc, Old_bitmap );
    GpiDeleteBitmap( Draw_bitmap );
    Draw_bitmap = NULLHANDLE;
    GpiDestroyPS( Mem_dc );
    DevCloseDC( Hdc );
#endif
}

static void draw_stuff( HWND hwnd )
{
    HPS                 win_dc;
    RECTL               paint;
    RECTL               intersect;
#ifdef DRAW_ALL_AT_ONCE
    int                 old_top;
    int                 width, height;
    SIZEL               sizl = { 0, 0 };
    BITMAPINFOHEADER2   bmih;
    LONG                formats[24];
    POINTL              pts[3];
    LONG                old_cursor;
    LONG                hour_glass_cur;
    RECTL               interior;
    DEVOPENSTRUC        dop = { 0L, "DISPLAY", NULL, 0L, 
                                0L, 0L, 0L, 0L, 0L };
#endif

    
    win_dc = WinBeginPaint( hwnd, 0, &paint );
    GpiCreateLogColorTable( win_dc, 0L, LCOLF_RGB, 0L, 0L, NULL );
#ifdef DRAW_ALL_AT_ONCE
    old_top = paint.yBottom;
    paint.yBottom = Draw_area.yTop;
#endif
    WinFillRect( win_dc, &paint, SYSCLR_WINDOW );
#ifdef DRAW_ALL_AT_ONCE
    paint.yBottom = old_top;
#endif
    if( WinIntersectRect( Main_hab, &intersect, &paint, &Draw_area ) ) {
    
#ifdef DRAW_ALL_AT_ONCE
        width = Draw_area.xRight - Draw_area.xLeft;
        height = Draw_area.yTop - Draw_area.yBottom;
        interior.xLeft = 0;
        interior.yBottom = 0;
        interior.xRight = width;
        interior.yTop = height;
        if( Draw_bitmap == NULLHANDLE ) {
            Hdc = DevOpenDC( Main_hab, OD_MEMORY, "*", 5L,
                                            (PDEVOPENDATA)&dop, NULLHANDLE ); 
            Mem_dc = GpiCreatePS( Main_hab, Hdc, &sizl, PU_PELS | GPIA_ASSOC );
            memset( &bmih, 0, sizeof( BITMAPINFOHEADER2 ) );
            GpiQueryDeviceBitmapFormats( Mem_dc, 24L, formats );
            bmih.cbFix = sizeof( BITMAPINFOHEADER2 );
            bmih.cx = width;
            bmih.cy = height;
            bmih.cPlanes = (USHORT) formats[0];
            bmih.cBitCount = (USHORT) formats[1];
            Draw_bitmap = GpiCreateBitmap( Mem_dc, &bmih, 0L, NULL, NULL );
            Old_bitmap = GpiSetBitmap( Mem_dc, Draw_bitmap );
            GpiCreateLogColorTable( Mem_dc, 0, LCOLF_RGB, 0, 0, NULL );
            WinFillRect( Mem_dc, &interior, SYSCLR_WINDOW );
            
            hour_glass_cur = WinQuerySysPointer( HWND_DESKTOP, SPTR_WAIT, FALSE );
            old_cursor = WinQueryPointer( HWND_DESKTOP );
            WinSetPointer( HWND_DESKTOP, hour_glass_cur );
            
            hThree_d = three_d_begin( Mem_dc, &interior );
            draw_room();
            three_d_draw( hThree_d );
            three_d_end( hThree_d );
            
            WinSetPointer( HWND_DESKTOP, old_cursor );
        }
        
        pts[0].x = Draw_area.xLeft;
        pts[0].y = Draw_area.yBottom;
        pts[1].x = Draw_area.xLeft + width;
        pts[1].y = Draw_area.yBottom + height;
        pts[2].x = 0;
        pts[2].y = 0;
        GpiBitBlt( win_dc, Mem_dc, 3, pts, ROP_SRCCOPY, BBO_IGNORE );
#else
        hThree_d = three_d_begin( win_dc, &Draw_area );
        draw_room();
        three_d_draw( hThree_d );
        three_d_end( hThree_d );
#endif

    }
    WinEndPaint( win_dc );
}

static void make_buttons( HWND hwnd )
{
    int     offset;
    int     max_height;
    int     btn_width;
    int     btn_height;
    static int buttons[] = {
        IDM_ROTATE_UP,
        IDM_MOVE_IN,
        IDM_ROTATE_LEFT,
        IDM_ROTATE_RIGHT,
        IDM_MOVE_OUT,
        IDM_ROTATE_DOWN,
        IDM_MORE_BRIGHTNESS,
        IDM_LESS_BRIGHTNESS,
        0 };
    int index;

    Draw_area.yTop -= 2;
    offset = Draw_area.xLeft + 2;
    max_height= 0;
    for( index = 0; buttons[index]; ++ index, offset += btn_width - 1 ) {
        #if 0
        if( index == 0 ) {
             add_button( hwnd, Draw_area.yTop, offset, buttons[index], NULL, &btn_height );
        } else
        #endif
        add_button( hwnd, Draw_area.yTop, offset, buttons[index], &btn_width, &btn_height );
        if( max_height < btn_height ) max_height = btn_height;
    }
    // Remove buttons from drawing area
    Draw_area.yTop -= max_height + 2;
    Button_height = max_height + 4;
}

MRESULT EXPENTRY main_proc(
    HWND                hwnd,
    ULONG               msg,
    MPARAM              mp1,
    MPARAM              mp2
) {
    if( msg == WM_CREATE ) {
    
        PCREATESTRUCT   pcreate;
        
        pcreate = ( CREATESTRUCT * ) mp2;
        Draw_area.xRight = pcreate->x + pcreate->cx;
        Draw_area.yTop = pcreate->y + pcreate->cy;
        Draw_area.yBottom = pcreate->y;
        Draw_area.xLeft = pcreate->x;
//      WinQueryWindowRect( hwnd, &Draw_area );
        
    } else if( msg == WM_COMMAND || ( msg == WM_CONTROL &&
                                SHORT2FROMMP( mp1 ) != BN_PAINT ) ) {
                                
        switch( SHORT1FROMMP( mp1 ) ) {
        
        case IDM_EXIT:
            new_display();
            WinPostMsg( hwnd, WM_QUIT, 0L, 0L );
            return( NULL );
            
        case IDM_ROTATE_LEFT:
            new_display();
            add_wrap( THREE_D_HORZ_ANGLE, -15, 0, 360 );
            break;
        case IDM_ROTATE_RIGHT:
            new_display();
            add_wrap( THREE_D_HORZ_ANGLE, 15, 0, 360 );
            break;
        case IDM_ROTATE_UP:
            new_display();
            add_range( THREE_D_VERT_ANGLE, 5, -90, 90 );
            break;
        case IDM_ROTATE_DOWN:
            new_display();
            add_range( THREE_D_VERT_ANGLE, -5, -90, 90 );
            break;
        case IDM_MOVE_IN:
            new_display();
            add_range(THREE_D_ZOOM, 10, 5, 200 );
            break;
        case IDM_MOVE_OUT:
            new_display();
            add_range( THREE_D_ZOOM, -10, 5, 200 );
            break;
        case IDM_MORE_PERSPECTIVE:
            new_display();
            add_range( THREE_D_PERSPECTIVE, 10, 5, 200 );
            break;
        case IDM_LESS_PERSPECTIVE:
            new_display();
            add_range( THREE_D_PERSPECTIVE, -10, 5, 200 );
            break;
        case IDM_MORE_CONTRAST:
            new_display();
            add_range( THREE_D_CONTRAST, 10, 0, 100 );
            break;
        case IDM_LESS_CONTRAST:
            new_display();
            add_range( THREE_D_CONTRAST, -10, 0, 100 );
            break;
        case IDM_MORE_BRIGHTNESS:
            new_display();
            add_range( THREE_D_BRIGHTNESS, 10, 0, 100 );
            break;
        case IDM_LESS_BRIGHTNESS:
            new_display();
            add_range( THREE_D_BRIGHTNESS, -10, 0, 100 );
            break;
        case IDM_LIGHT_HORZ_LEFT:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_HORZ, 1 );
            break;
        case IDM_LIGHT_HORZ_MIDDLE:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_HORZ, 2 );
            break;
        case IDM_LIGHT_HORZ_RIGHT:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_HORZ, 3 );
            break;
        case IDM_LIGHT_VERT_TOP:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_VERT, 1 );
            break;
        case IDM_LIGHT_VERT_MIDDLE:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_VERT, 2 );
            break;
        case IDM_LIGHT_VERT_BOTTOM:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_VERT, 3 );
            break;
        case IDM_LIGHT_DEPTH_FRONT:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_DEPTH, 1 );
            break;
        case IDM_LIGHT_DEPTH_MIDDLE:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_DEPTH, 2 );
            break;
        case IDM_LIGHT_DEPTH_BACK:
            new_display();
            three_d_set( hThree_d, THREE_D_LIGHT_DEPTH, 3 );
            break;
        default:
            ;
        }
        WinInvalidateRect( hwnd, &Draw_area,
                        SHORT1FROMMP( mp1 ) >= IDM_FIRST_REQUIRING_CLEAR );
        
    } else if( msg == WM_DESTROY ) {
        WinPostMsg( hwnd, WM_QUIT, 0L, 0L );
        return( 0 );
 
    } else if( msg == WM_PAINT ) {       
        draw_stuff( hwnd );
        if( Auto_run ) {
            if( Auto_run == 5 ) {
                WinPostMsg( hwnd, WM_QUIT, 0L, 0L );
            } else {
                long count;
                int will_be_one;
                will_be_one = 1;
                for(count = 0; count < 1000000; ++ count ) will_be_one ^= 1;
                    
                Auto_run += will_be_one;
                WinPostMsg( hwnd, WM_COMMAND,
                        MPFROMSHORT( IDM_ROTATE_LEFT ), 0L );
            }
        }
        return( 0 );
        
    } else if( msg == WM_SIZE ) {
        WinQueryWindowRect( hwnd, &Draw_area );
        Draw_area.yTop -= Button_height;
        WinInvalidateRect( hwnd, NULL, TRUE );
        return( 0 );
 
    } else if( msg == WM_MEASUREITEM ) {
        return( (MRESULT) measure_button( hwnd, mp1, mp2 ) );
        
    } else if( msg == WM_CONTROL && SHORT2FROMMP( mp1 ) == BN_PAINT ) {
        draw_button( mp1, mp2 );
        return( (MRESULT) TRUE );
    
    }
    
    return( (MRESULT) WinDefWindowProc( hwnd, msg, mp1, mp2 ) );
}
            
static BOOL init_instance( int show )
{
    HWND        frame_hwnd;
    ULONG       flags = FCF_SIZEBORDER | FCF_TITLEBAR
                | FCF_SYSMENU | FCF_MENU | FCF_MINMAX | FCF_SHELLPOSITION;

    frame_hwnd = WinCreateStdWindow( HWND_DESKTOP, WS_VISIBLE,
                &flags, "DrawDemo", "Draw a Picture", WS_VISIBLE,
                0, 200, &hwnd );
        
    /* If window could not be created, return "failure" */

    if( hwnd == 0 || frame_hwnd == 0 ) {
        return (FALSE);
    }

    /* Make the window visible; update its client area; and return "success" */

    WinSetWindowPos( frame_hwnd, HWND_TOP, 50, 50, 200, 200, show );
    
    make_buttons( hwnd );
        
    WinShowWindow( frame_hwnd, TRUE );
    WinUpdateWindow( frame_hwnd );
    return( TRUE );
}

BOOL init_app( HAB hab )
{
    /* Register the window class and return success/failure code. */
    return( WinRegisterClass( hab, "DrawDemo", (PFNWP) main_proc, NULL, 0 ) );
}

int main(
/*******/
    void
) {
    QMSG                qmsg;
    
    Main_hab = WinInitialize( 0 );
    if( !Main_hab ) {
        return( FALSE );
    }
    
    Main_hmq = WinCreateMsgQueue( Main_hab, 0 );
    if( !Main_hmq ) {
        return( FALSE );
    }

    if( !init_app( Main_hab ) ) {
        return( FALSE );    
    }

    /* Perform initializations that apply to a specific instance */

    three_d_init();
    define_room();
    if (!init_instance( SWP_SHOW | SWP_ACTIVATE ) ) {
        finish_room();
        three_d_fini();
        return( FALSE );
    }

    /* Acquire and dispatch messages until a WM_QUIT message is received. */

    while( WinGetMsg( Main_hab, &qmsg, NULL, NULL, NULL) ) {
        WinDispatchMsg( Main_hab, &qmsg );
    }
    finish_room();
    three_d_fini();
    
    if( Main_hmq ) {
      WinDestroyMsgQueue( Main_hmq );
    }
    if( Main_hab ) {
      WinTerminate( Main_hab );
    }
    
    DosExit( EXIT_PROCESS, 0);
    
    return( SHORT1FROMMP( qmsg.mp1 ) );
}
