/*
 Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <IO/Output/Video/GLWindow.h>
#include <IO/Output/Video/GLView.h>
#import <lib/Preferences.h>

extern const char * getString( NSString * characters );

@interface MyView : NSView {

    GLWindow * mOwner;
}

- (void)drawRect:(NSRect)aRect;
- (void) setOwner:(GLWindow*) owner;
- (BOOL)isOpaque;
- (void) mouseDown:(NSEvent *)theEvent;
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent;

@end // interface MyView

@implementation MyView

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
    return YES;
}

- (void) mouseDown:(NSEvent *)theEvent
{
    [ super mouseDown:theEvent ];
    
    //[[ self window ] setInitialFirstResponder:self ];
}

- (BOOL)isOpaque
{
    return NO;
}

- (void)drawRect:(NSRect)aRect
{
    //puts("MyView.drawRect");

    [ super drawRect:aRect ];
    
    [ mOwner updateContent ];
}

- (void) setOwner:(GLWindow*) owner
{
    mOwner = owner;
}

@end // implementation MyView

@implementation GLWindow

+ (id) alloc
{
    id _id = [ super alloc ];
    
    if( _id != nil ) {
        [ _id initMembers ];
    }
    return _id;
}

- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
{
    id _id = [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ];

    //puts("initWithContentRect");

    [ self registerForDraggedTypes: [NSArray arrayWithObject: NSFilenamesPboardType]];

    return _id;
}

- (void) move:(float) x y:(float) y
{
    NSRect r = [ self frame ];
    
    r.origin.x = x;
    r.origin.y = y;
    
    [ self setFrame:r display:true ];
    
    [ self posChanged: x y:y ];
}

- (void) resize:(float) w h:(float) h
{
    NSRect r = [ self frame ];
    
    r.size.width  = w;
    r.size.height = h;
    
    [ self setFrame:r display:true ];

    [ self sizeChanged: w height:h ];    
}

- (GLView*) glView
{
    return glView;
}
              
- (void) updateContent
{
}

- (void) updateGLView
{
}

- (void) draw:( const unsigned char *) buffer
{
    int ntsc;
    static float time = 0.0;
    static long ticksPrev = 0;
    //static float xRot = 0.0, yRot = 0.0, zRot = 0.0;
    float rx, ry, rz;
    float alpha;

    short width;
    short height;

    if( mNeedUpdate ) {

        [ self updateGLMovieForGrow:mNewWidth height:mNewHeight ];

        mNeedUpdate = false;
    }


    // get viewport size --------------
    // for text

    width  = (short) grectWin.size.width;
    height = (short) grectWin.size.height;

    // update texture -----------------
	
    //glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 360*gScale, 288*gScale, GL_BGRA, GL_UNSIGNED_BYTE, buffer );
    glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, gMovieWidth, gMovieHeight, GL_BGRA, GL_UNSIGNED_BYTE, buffer );
		
	 
    // move frame ---------------------
	
    if (ticksPrev)
        time = ((float) (TickCount () - ticksPrev)) / 60.0;
    else
        time = 0.0;
    
    ticksPrev = TickCount ();

    // draw polygon	-------------------
	
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

    
    ntsc = ( gMovieHeight == 240 );
    
    if( ntsc ) {
        rx = 0.295+0.050;
        ry = 0.523;
    }
    else {
        rx = 0.295;
        ry = 0.43;
    }
    
    rz = 1.4;
    
    glTranslatef (rx, ry, -rz);
    	
    alpha = 1.0; // fabs (cos (f)); //

    glBegin(GL_QUADS);
    glTexCoord3f( 0.0, 0.0, 0.0 ); glColor4f( 1.0, 1.0, 1.0, alpha); glVertex3f( -1, -1, 0 );
    glTexCoord3f( 0.0, 1.0, 0.0 ); glColor4f( 1.0, 1.0, 1.0, alpha); glVertex3f( -1,  1, 0 );
    glTexCoord3f( 1.0, 1.0, 0.0 ); glColor4f( 1.0, 1.0, 1.0, alpha); glVertex3f(  1,  1, 0 );
    glTexCoord3f( 1.0, 0.0, 0.0 ); glColor4f( 1.0, 1.0, 1.0, alpha); glVertex3f(  1, -1, 0 );
    glEnd();

    // draw text ----------------------
	
    {
        // set to per pixel orthographic context for 2D/text drawing
        GLint matrixMode;
	glDisable(GL_TEXTURE_2D);
	glGetIntegerv (GL_MATRIX_MODE, &matrixMode);
	
	glMatrixMode (GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity ();
	glMatrixMode (GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity ();
	glScalef (2.0 / width, -2.0 / height, 1.0);
	glTranslatef (-width / 2.0, -height / 2.0, 0.0);
	glColor3f (1.0, 1.0, 1.0);

#if 0
        if( mDrawInfo ) {
            
            StringList::iterator iter( mStringList.begin() );
    
            while( iter != mStringList.end() ) {
    
                String * s = *iter;
        
                if( s->isVisible() ) {

                    glRasterPos3f( s->getX(), s->getY(), 0); 
                
                    DrawCStringGL( (char*) s->getString().c_str(), gFontList);

                }
                iter++;
            }
        }
#endif

	glPopMatrix(); // GL_MODELVIEW
	glMatrixMode (GL_PROJECTION);
        glPopMatrix();
	glMatrixMode (matrixMode);
	glEnable(GL_TEXTURE_2D);
    }

    [[ glView openGLContext] flushBuffer];

}

- (int) setupGL:(NSRect) windowPos movieSize:(NSSize) movieSize;
{
    gMovieSettings.wWindowWidth  = windowPos.size.width;
    gMovieSettings.wWindowHeight = windowPos.size.height;

    gMovieRect = NSMakeRect( 0, 0, movieSize.width, movieSize.height  );
    grectWin   = windowPos;
    
    if( glView == nil ) {
    
        [ self create:windowPos ];
    }

    
    // Setup OpenGL for movie texturing ---------------------

	
    [ self initGL:grectWin.size.width  height:grectWin.size.width ];

    // Setup texturing and movie scaling  -------------------

    gMovieWidth  = (short) gMovieRect.size.width;
    gMovieHeight = (short) gMovieRect.size.height;

    if (gMovieSettings.wTextureSize == 0) // set to power of two larger than movie
    {
	short shift = 0; 
	long value; 
	if (gMovieWidth > gMovieHeight)
            value = gMovieWidth;
	else
            value = gMovieHeight;

	while (value){
            value = value >> 1;
            shift++;
        }
        value = 1;
        
        while (shift)
        {
            value = value << 1;
            shift--;
        }
        gMovieSettings.wTextureSize = (short) value;
    }

    gvTextureOffset = 0; gvTextureEnd = gMovieHeight; ghTextureOffset = 0; ghTextureEnd = gMovieWidth;
    
    if (gMovieWidth > gMovieHeight)
    {
	gfTextureScale = (float) gMovieWidth / (float) (gMovieSettings.wTextureSize);
	gUsedTextureHeight = (short) (gMovieHeight / gfTextureScale);
	gUsedTextureWidth = (short) (gMovieWidth / gfTextureScale);	
	gvTextureOffset = (short) ((gMovieSettings.wTextureSize - gUsedTextureHeight) / 2);
	ghTextureOffset = 0;
    }
    else // harder case needs to be inset left and right
    {
	gfTextureScale = (float) gMovieHeight / (float) gMovieSettings.wTextureSize;
	gUsedTextureHeight = (short) (gMovieHeight / gfTextureScale);
	gUsedTextureWidth = (short) (gMovieWidth / gfTextureScale);	
	ghTextureOffset = (short) ((gMovieSettings.wTextureSize - gUsedTextureWidth) / 2);
	gvTextureOffset = 0;
    }
    gvTextureEnd = (short) (gUsedTextureHeight + gvTextureOffset);
    ghTextureEnd = (short) (gUsedTextureWidth + ghTextureOffset);

    [ self updateGLMovieForGrow:grectWin.size.width  height:grectWin.size.height ];
    
    [ self draw: gpTexture ];
    
    return noErr;
}

- (void) posChanged:(float) x y:(float) y
{
}

- (void) sizeChanged:(float) windowWidth height:(float) windowHeight
{

    float glViewWidth  = windowWidth  - ( mLeftBorder + mRightBorder ) ;
    float glViewHeight = windowHeight - ( mTopBorder + mBottomBorder );

    float x = mLeftBorder;
    float y = mBottomBorder;


    NSRect mainViewBounds  = NSMakeRect( 0, 0, windowWidth, windowHeight );
    NSRect glViewBounds    = NSMakeRect( x, y, glViewWidth, glViewHeight );

    if( mFullScreen ) {
        glViewBounds = mainViewBounds;
    }
    
    //puts("GLWindow.sizeChanged");
    
    [ mainView setFrame: mainViewBounds  ];
    [ glView   setFrame: glViewBounds    ];

    mNewWidth   = [ glView frame ].size.width;
    mNewHeight  = [ glView frame ].size.height;
    mNeedUpdate = true;

    [ super display ];

    [ self draw: gpTexture ];

    [ self flushWindow ];    
}

- (void) display
{
    int w = [ self frame ].size.width;
    int h = [ self frame ].size.height;
    
    [ self sizeChanged:w height:h ];
}

-(bool) create:(NSRect) windowPos
{
UInt32 RendererSelection; // mod philipp here
    //printf("GLWindow.create xywh(%d,%d,%d,%d)\n", (int) windowPos.origin.x, (int) windowPos.origin.y,
    //                                              (int) windowPos.size.width, (int) windowPos.size.height );
    if( glView == nil ) {

        int flags = NSBorderlessWindowMask;
    
        NSRect frame = [ NSWindow contentRectForFrameRect: windowPos styleMask: flags ];
        NSRect innerRect;
    
        [ self initWithContentRect: frame styleMask: flags backing: NSBackingStoreBuffered defer: NO ];

        mainView = [ MyView alloc ];
    
        [ ((MyView*)mainView ) setOwner:self ];
    
        innerRect  = NSMakeRect( 0, 0, windowPos.size.width, windowPos.size.height );

        [ mainView initWithFrame: innerRect ];
        
        [ self setContentView: mainView ];
        
        //[ self setAcceptsMouseMovedEvents: true ];
        
        [ mainView display ];
        
        [ self setHasShadow:false ];
        
        // OpenGL view
        
        glView = [ GLView alloc ];
            // mod philipp here
        {
            int OpenGLRenderer  = GetPrivateProfileInt( "Window", "OpenGL", 0 );
    
            switch (OpenGLRenderer) {
                case 1:
                    RendererSelection = kCGLRendererGenericID;
                    break;
                case 2:
                    RendererSelection = kCGLRendererAppleSWID;
                    break;
                case 3:
                    RendererSelection = kCGLRendererATIRage128ID;
                    break;
                case 4:
                    RendererSelection = kCGLRendererATIRageProID;
                    break;
                case 5:
                    RendererSelection = kCGLRendererATIRadeonID;
                    break;
                case 6:
                    RendererSelection = kCGLRendererGeForce2MXID;
                    break;
                case 7:
                    RendererSelection = kCGLRendererGeForce3ID;
                    break;
                case 8:
                    RendererSelection = kCGLRendererMesa3DFXID;
                    break;
                default:
                    RendererSelection = 0;
                    break;
            } 
            printf("Selected Renderer no: %lx\n",RendererSelection);
        }

        [ glView initWithFrame: innerRect RendererSelection:RendererSelection];
             
        [ mainView addSubview: glView ];
        
        [[ glView openGLContext] setView:glView ];
        
        [ self setResizeIncrements: NSMakeSize(1.0,1.0) ];
        
        [ self  setHasShadow:false ];

        [ self  setAutodisplay:false ];

        //[ self setInitialFirstResponder:glView ];
    }
    return true;
}

- (NSSize) windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
{
    return proposedFrameSize;
}
        
- (void) toggleInfo
{
    mDrawInfo = ( ! mDrawInfo );
}

- (BOOL)canBecomeKeyWindow
{
    return YES;
}

- (BOOL)canBecomeMainWindow
{
    return YES;
}

- (BOOL) acceptsFirstResponder
{
    return YES;
}

-(int) getRightBorder
{
    return mRightBorder;
}

-(int) getLeftBorder
{
    return mLeftBorder;
}

- (void) setRightBorder:( int ) pixels
{
    mRightBorder = pixels;
}

- (void) setLeftBorder:( int ) pixels
{
    mLeftBorder = pixels;
}

-(int) getTopBorder
{
    return mTopBorder;
}

-(int) getBottomBorder
{
    return mBottomBorder;
}

- (void) setTopBorder:( int ) pixels
{
    mTopBorder = pixels;
}

- (void) setBottomBorder:( int ) pixels
{
    mBottomBorder = pixels;
}
    
- (void) updateGLMovieForGrow:(short) width height:(short) height
{
    gMovieSettings.wWindowWidth = width;
    gMovieSettings.wWindowHeight = height;
    
    glViewport (0, 0, width, height);

    //SetFrustum (width, height, 2.0, 1.0, 100.0);

    [ self setFrustum:width height:height  focalLen:2.0 minDepth:1.0 maxDepth:100.0 ];
}

- (void) initGL:(short) width height:(short) height
{
    // set up GL params

    [ self updateGLMovieForGrow:width  height:height ];

    if( gpTexture == 0 ) {

        long sizeTexture;
    
        glEnable(GL_TEXTURE_2D);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        // allocated texture buffer
	
        sizeTexture = 3 * gMovieSettings.wTextureSize * gMovieSettings.wTextureSize; // size of texture in bytes
	
        gpTexture = (GLubyte *) NewPtrClear (sizeTexture);
    
        if (!gpTexture) {
            puts( "failed to allocat texture buffer" );
            return;
        }
    }
    
    gRowStride = gUsedTextureWidth * gMovieSettings.wOffScreenDepth / 8; 
		
        // set up initial black texture	
    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, gMovieSettings.wTextureSize, gMovieSettings.wTextureSize, 0, 
                    GL_RGBA, GL_UNSIGNED_BYTE, gpTexture );

}

- (void) setFrustum:(short) width height:(short) height focalLen:(float) focalLen minDepth:(float) minDepth maxDepth:(float) maxDepth
{
    float  heightOverWidth = (float) height / (float) width; // 0.75 intially;
    float f = minDepth / focalLen;
    float h = heightOverWidth * f;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //glFrustum( -f, f, h, -h, minDepth, maxDepth);
    glFrustum( -f, f, h, -h, minDepth, maxDepth);
}


- (void) initMembers
{
    mTopBorder = mLeftBorder = mRightBorder = mBottomBorder = 0;
    
    mNeedUpdate = false;

    gMovieSettings.wWindowWidth      = 360;
    gMovieSettings.wWindowHeight     = 288;
    gMovieSettings.wTextureSize      = 512; 
    gMovieSettings.wOffScreenDepth   = 32;
    gMovieSettings.fFullScreen       = false;
    gMovieSettings.fDirectTexturing  = true;
    gMovieSettings.fVBLSync          = true;

    gUsedTextureHeight = 0;
    gUsedTextureWidth = 0;
    
    gfTextureScale = (float) 1.0;
    gvTextureOffset = 0, gvTextureEnd = 0, ghTextureOffset = 0, ghTextureEnd = 0;

    // Movie
    gMovieRect = NSMakeRect( 0, 0, 360*2, 288*2);

    // GL stuff
    
    gfHasPackedPixels = false;
    gFontList = 0;
    
    gRowStride = 0;
    mDrawInfo   = false;

    glView   = 0;
    mainView = 0;


    mFullScreen = FALSE;

}

- (BOOL) isFullScreen
{
    return mFullScreen;
}

- (void) toggleFullScreen
{
    float w,h;
        
    if( ! mFullScreen ) {

        NSSize screen = [[ NSScreen mainScreen ] frame ].size;

        mSavedBounds = [ self frame ];        
        mSavedLevel  = [ self level ];    
        
        w = screen.width;
        h = screen.height;

        mFullScreen = true;

        [ self setFrame:NSMakeRect( 0, 0, w, h ) display:true ];
        
        [ self setLevel: kCGOverlayWindowLevel ];    

    }
    else {

        w = mSavedBounds.size.width;
        h = mSavedBounds.size.height;
        
        mFullScreen = false;

        [ self setFrame:mSavedBounds display:TRUE ];
        [ self setLevel:mSavedLevel ];    

        // hack , window isn't drawn 
        
        [ self sizeChanged: w-1 height:h-1 ];
    }

    [ self sizeChanged: w height:h ];
    [ self flushWindow ];
}

- (void) handleDrop:(NSString*) fileName
{
}

- (unsigned int) draggingEntered:(id <NSDraggingInfo>)sender
{
    //puts("draggingEntered");
    return NSDragOperationCopy;
}

- (BOOL) prepareForDragOperation:(id <NSDraggingInfo>)sender
{
    //puts("prepareForDragOperation");
    return YES;
}


- (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
{
    //puts("performDragOperation");
    // Mike Ferris sez that you should do the drag operation in conclude!
    // That way you don't have to wait for time-outs when debugging the
    // drop code.
    return YES;
}

- (void) concludeDragOperation:(id <NSDraggingInfo>)sender
{
    unsigned i;
    NSPasteboard * pboard           = [ sender draggingPasteboard ];
    NSArray	 * draggedFileNames = [ pboard propertyListForType: NSFilenamesPboardType ];

    //puts("concludeDragOperation");
    
    for( i = 0; i < [ draggedFileNames count ]; i++ ) {
    
        NSString * s = [ draggedFileNames objectAtIndex:i ];
        
        //printf( "item(%d) is %s\n", i, [ s cString ] );
        
        [ self handleDrop:s ];
    }
    //draggedFileNames = [ draggedFileNames fileNamesWithBasePath: @"" ];
    //draggedFileNames = [draggedFileNames arrayByRemovingObjectsInArray: files];
    //[ files addObjectsFromArray: draggedFileNames ];
    //[ self updateView: self ];
}

@end // implemetation GLWindow
