 //////////////////////////////////////////////////////////////////////////////
//
// PocketFrog - The Game Library for Pocket PC Devices
// Copyright 2002  Thierry Tremblay
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation.  Thierry Tremblay makes no representations
// about the suitability of this software for any purpose.
// It is provided "as is" without express or implied warranty.
//
//////////////////////////////////////////////////////////////////////////////

#include "blit.h"
#include "resource.h"

using namespace Frog;



//////////////////////////////////////////////////////////////////////////////
//
// _Module : This is needed to use ATL Windows
//
//////////////////////////////////////////////////////////////////////////////

CComModule _Module;



//////////////////////////////////////////////////////////////////////////////
//
// BlitSample
//
//////////////////////////////////////////////////////////////////////////////

BlitSample::BlitSample()
{

    m_config.szWindowName = _T( "Blit Sample" );
    m_config.orientation  = ORIENTATION_ROTATE90CCW;
}


typedef unsigned __int16 word;
typedef unsigned __int32 dword;

#define fixedshift 16


/*
benchmark results - fps when all the way zoomed in and pocketfrog logo is gone

Axim x50v

DEBUG mode
13 fps - blitStretchClipped section 1
36 fps - blitStretchClipped section 2
36/37 fps - blitStretchClippedFixed

RELEASE mode optimized for speed EVC 3.0
41 fps - blitStretchClippedFixed

*/

void blitStretchClipped(
				 word *dst, int dw, int dh,			// dest surface
				 word *src, int sw, int sh,			// source surface
				 int x, int y, int w, int h,		// position and size of scaling
				 int mx, int my,
				 float dx, float dy,
				 float sx, float cy)
{
	for(int j=y; j<my; j++)
	{
		// current x
		float cx = sx;

		// line y offsets
		int dstline = j * dw;
		int srcline = (int)cy * sw;

		// section1
		//for(int i=x; i<mx; i++)
		//{
		//	*(dst + i + dstline) = *(src + (int)cx + srcline);
		//	cx += dx;
		//}

		// section 2
		// fixed point
		word *dstp = dst + x + dstline;
		word *srcp = src + srcline;
		#define fs 16
		dword fcx = (dword)(cx * (1<<fs));
		dword fdx = (dword)(dx * (1<<fs));
		for(int i=x; i<mx; i++)
		{
			*dstp = *(srcp + (fcx>>fs));
			fcx += fdx;
			dstp++;
		}

		cy += dy;
	}
}


void blitStretchClippedFixed(
				 word *dst, int dw, int dh,			// dest surface
				 word *src, int sw, int sh,			// source surface
				 int x, int y, int w, int h,		// position and size of scaling
				 int mx, int my,
				 dword fdx, dword fdy,
				 dword fsx, dword fcy)
{
	word *cur_dstp = dst + x + y * dw;
	word *cur_dstpend = cur_dstp + (my - y) * dw;
	int scaledw = mx - x;

	while(cur_dstp < cur_dstpend)
	{
		word *dstp = cur_dstp;
		word *srcp = src + (fcy>>fs) * sw;
		word *dstpend = dstp + scaledw;
		dword fcx = fsx;

		while(dstp < dstpend)
		{
			*dstp = *(srcp + (fcx>>fs));
			dstp++;
			fcx += fdx;
		}

		cur_dstp += dw;
		fcy += fdy;
	}
}




void blitStretch(word *dst, int dw, int dh,			// dest surface
				 word *src, int sw, int sh,			// source surface
				 int x, int y, int w, int h,		// position and size of scaling
				 int x1, int y1, int x2, int y2)	// clipping values of dst
{
	// max x and y
	int mx = x+w;
	int my = y+h;

	// scaling factors
	float dx = (float)sw / (float)w;
	float dy = (float)sh / (float)h;

	// starting x
	float sx = 0;

	// current y
	float cy = 0;

	// clip the scaled bitmap to our clipping coords
	if(x < x1)
	{
		sx = ((float)(x1-x))*dx;
		x = x1;
	}
	if(mx > x2)
	{		
		mx = x2;
	}
	if(y < y1)
	{
		cy = ((float)(y1-y))*dy;
		y = 0;
	}
	if(my > y2)
	{
		my = y2;
	}

	//blitStretchClipped(dst, dw, dh, src, sw, sh, x, y, w, h, mx, my, dx, dy, sx, cy);

	dword fsx = (dword)(sx * (1<<fs));
	dword fcy = (dword)(cy * (1<<fs));
	dword fdx = (dword)(dx * (1<<fs));
	dword fdy = (dword)(dy * (1<<fs));
	blitStretchClippedFixed(dst, dw, dh, src, sw, sh, x, y, w, h, mx, my, fdx, fdy, fsx, fcy);
}





bool BlitSample::GameInit()
{

		// Initialize random seed
    srand( PocketPC::GetTickCount() );
    
    Display* display = GetDisplay();
    
    m_images[0] = LoadImage( display, IDB_BITMAP1 );
    m_images[1] = LoadImage( display, IDB_BITMAP2 );
    m_images[2] = LoadImage( display, IDB_BITMAP3 );
    m_images[3] = LoadImage( display, IDB_BITMAP4 );
    m_background = LoadImage( display, IDR_IMAGE1 );
    
    for (int i = 0; i < 4; ++i)
    {
        // Set color mask
        m_images[i]->SetColorMask( Color( 255, 0, 255) );
        
        // Set initial position
        m_position[i].Set( 0, 0, m_images[i]->GetWidth(), m_images[i]->GetHeight() );
        
        int x = rand() % (display->GetWidth() - m_images[i]->GetWidth());
        int y = rand() % (display->GetHeight() - m_images[i]->GetHeight());
        
        m_position[i].Move( x, y );
    }
    
    
    m_numbers = LoadImage( display, IDB_NUMBERS );
    m_FPSCounter = 0;

    m_dragImage = -1;   // Not dragging any image

    return true;

}



void BlitSample::GameEnd()
{

    for (int i = 0; i < 4; ++i)
    {
        delete m_images[i];
    }
    
    delete m_background;
    delete m_numbers;

}



void BlitSample::GameLoop()
{
    Display* display = GetDisplay();

    display->GetBackBuffer();

    display->Blit( 0, 0, m_background );
    
#if defined(FROG_HPC)
    display->Blit( 320, 0, m_background );
#endif
    
	Surface::LockInfo img1, dst;
	m_images[1]->Lock(img1);
	display->GetBackBuffer()->Lock(dst);

	int dw = display->GetBackBuffer()->GetWidth();
	int dh = display->GetBackBuffer()->GetHeight();
	int sw = m_images[1]->GetWidth(); 
	int sh = m_images[1]->GetHeight();

	static float zoom = 1.0f;
	static float dz = 0.1f;
	zoom += dz;

	if(zoom < 0.1f || zoom > 10.0f)
		dz = -dz;

	// zoom the image in and out
	int zw = (float)sw * zoom;
	int zh = (float)sh * zoom;

	// center the image
	int zx = (dw - zw)/2;
	int zy = (dh - zh)/2;

	blitStretch(
		dst.pixels, dw, dh, 
		img1.pixels, sw, sh,
		zx, zy, zw, zh,
		0, 0, dw, dh);


	m_images[1]->Unlock();
	display->GetBackBuffer()->Unlock();

    /*for (int i = 0; i < 4; ++i)
    {
        Rect& pos = m_position[i];
        
        // Display image
        display->Blit( pos.left, pos.top, m_images[i] );
        
        if (i != m_dragImage)
        {
            // Move image (horizontal)
            switch (rand() % 3)
            {
            case 0: break;  // Don't move
            case 1: if (pos.left > 0) pos.Translate( -1, 0 ); break;
            case 2: if (pos.right < display->GetWidth()) pos.Translate( 1, 0 ); break;
            }
            
            // Move image (vertical)
            switch (rand() % 3)
            {
            case 0: break;  // Don't move
            case 1: if (pos.top > 0) pos.Translate( 0, -1 ); break;
            case 2: if (pos.bottom < display->GetHeight()) pos.Translate( 0, 1 ); break;
            }
        }
    }*/

    
    // Update FPS
    m_FPSTicks[ m_FPSCounter & 15 ] = PocketPC::GetTickCount();
    if (m_FPSCounter > 15)
    {
        uint32_t totalTime = m_FPSTicks[ m_FPSCounter & 15 ] - m_FPSTicks[ (m_FPSCounter+1) & 15 ];
        if (totalTime == 0) totalTime = 1;
        uint32_t fps  = 16000 / totalTime;
        uint32_t n100 = fps / 100; fps -= n100 * 100;
        uint32_t n10  = fps / 10;  fps -= n10 * 10;
        uint32_t n1   = fps;

        if (n100 > 9) { n100 = 9; n10 = 9; n1 = 9; }
        
        // Display fps
        display->Blit(  0, 0, m_numbers, &Rect( n100 * 8, 0, (n100+1)*8, 11 ) );
        display->Blit(  8, 0, m_numbers, &Rect( n10 * 8, 0, (n10+1)*8, 11 ) );
        display->Blit( 16, 0, m_numbers, &Rect( n1 * 8, 0, (n1+1)*8, 11 ) );
    }
    ++m_FPSCounter;

    // Update screen
    display->Update();

}



void BlitSample::StylusDown( Point stylus )
{


    // Check if the point is on one of the images
    for (int i = 3; i >= 0; --i)
    {
        if (m_position[i].Contains( stylus ))
        {
            m_dragImage  = i;
            m_dragOffset = stylus - Point( m_position[i].left, m_position[i].top );
            break;
        }
    }

}




void BlitSample::StylusUp( Point stylus )
{

		m_dragImage = -1;

}



void BlitSample::StylusMove( Point stylus )
{

		if (m_dragImage == -1)
        return;
    
    Point newPosition ( stylus - m_dragOffset );
    
    const Surface* image = m_images[m_dragImage];
    Rect limit( 0, 0, GetDisplay()->GetWidth() - image->GetWidth(), GetDisplay()->GetHeight() - image->GetHeight() );
    
    if (newPosition.x < limit.left) newPosition.x = limit.left;
    else if (newPosition.x > limit.right) newPosition.x = limit.right;
    
    if (newPosition.y < limit.top) newPosition.y = limit.top;
    else if (newPosition.y > limit.bottom) newPosition.y = limit.bottom;
    
    m_dragOffset = stylus - newPosition;
    
    m_position[m_dragImage].Move( newPosition.x, newPosition.y );

}




//////////////////////////////////////////////////////////////////////////////
//
// WinMain - Entry point
//
//////////////////////////////////////////////////////////////////////////////

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPTSTR, int )
{
    _Module.Init( 0, hInstance );
    
    BlitSample game;
    
    game.Run();
    
    return 0;
}
