/*      
  _____     ___ ____ 
   ____|   |    ____|      PSX2 OpenSource Project
  |     ___|   |____       (c) 2008 jMk0Nm 
------------------------------------------------------------------------
draw.c

*/

#include "mcboot.h"

GSGLOBAL  *gsGlobal;
GSFONT    *gsFont;
GSTEXTURE TexSkin, TexIcon[15];
int				SCREEN_WIDTH	= 640;
int				SCREEN_HEIGHT = 448;
int       SCREEN_X			= 632;
int       SCREEN_Y			= 50;

int updateScr_1;     //flags screen updates for drawScr()
int updateScr_2;     //used for anti-flicker delay in drawScr()
u64 updateScr_t = 0; //exit time of last drawScr()

int Menu_start_x   = SCREEN_MARGIN + LINE_THICKNESS + FONT_WIDTH;
int Menu_message_y = SCREEN_MARGIN + FONT_HEIGHT;
int Menu_start_y   = 56;

//--------------------------------------------------------------
int log( int Value ){
	int r = 0;
	Value--;
	while( Value > 0 )
	{
		Value = Value >> 1;
		r++;
	}
	return r;
}
//--------------------------------------------------------------
int *CreateCoeffInt( int nLen, int nNewLen, int bShrink ) {

 int nSum    = 0;
 int nSum2   = 0;
 int *pRes   = (int*) malloc( 2 * nLen * sizeof(int) );
 int *pCoeff = pRes;
 int nNorm   = (bShrink) ? (nNewLen << 12) / nLen : 0x1000;
 int nDenom  = (bShrink) ? nLen : nNewLen;
 int i;

 memset( pRes, 0, 2 * nLen * sizeof(int) );

 for( i = 0; i < nLen; i++, pCoeff += 2 ) {

  nSum2 = nSum + nNewLen;

  if(nSum2 > nLen) {

   *pCoeff = ((nLen - nSum) << 12) / nDenom;
   pCoeff[1] = ((nSum2 - nLen) << 12) / nDenom;
   nSum2 -= nLen;

  } else {

   *pCoeff = nNorm;

   if(nSum2 == nLen) {
    pCoeff[1] = -1;
    nSum2 = 0;

   } /* end if */

  } /* end else */

  nSum = nSum2;

 } /* end for */

 return pRes;

} /* CreateCoeffInt */
//--------------------------------------------------------------
int ShrinkData( u8 *pInBuff, u16 wWidth, u16 wHeight, u8 *pOutBuff, u16 wNewWidth, u16 wNewHeight ) {

 u8 *pLine    = pInBuff, *pPix;
 u8 *pOutLine = pOutBuff;
 u32 dwInLn   = ( 3 * wWidth + 3 ) & ~3;
 u32 dwOutLn  = ( 3 * wNewWidth + 3 ) & ~3;

 int  x, y, i, ii;
 int  bCrossRow, bCrossCol;
 int *pRowCoeff = CreateCoeffInt( wWidth, wNewWidth, 1 );
 int *pColCoeff = CreateCoeffInt( wHeight, wNewHeight, 1 );

 int *pXCoeff, *pYCoeff = pColCoeff;
 u32  dwBuffLn = 3 * wNewWidth * sizeof( u32 );
 u32 *pdwBuff = ( u32* ) malloc( 6 * wNewWidth * sizeof( u32 ) );
 u32 *pdwCurrLn = pdwBuff;  
 u32 *pdwNextLn = pdwBuff + 3 * wNewWidth;
 u32 *pdwCurrPix;
 u32  dwTmp, *pdwNextPix;

 memset( pdwBuff, 0, 2 * dwBuffLn );

 y = 0;
 
 while( y < wNewHeight ) {

  pPix = pLine;
  pLine += dwInLn;

  pdwCurrPix = pdwCurrLn;
  pdwNextPix = pdwNextLn;

  x = 0;
  pXCoeff = pRowCoeff;
  bCrossRow = pYCoeff[ 1 ] > 0;
  
  while( x < wNewWidth ) {
  
   dwTmp = *pXCoeff * *pYCoeff;
   for ( i = 0; i < 3; i++ )
    pdwCurrPix[ i ] += dwTmp * pPix[i];
   
   bCrossCol = pXCoeff[ 1 ] > 0;
   
   if ( bCrossCol ) {

    dwTmp = pXCoeff[ 1 ] * *pYCoeff;
    for ( i = 0, ii = 3; i < 3; i++, ii++ )
     pdwCurrPix[ ii ] += dwTmp * pPix[ i ];

   } /* end if */

   if ( bCrossRow ) {

    dwTmp = *pXCoeff * pYCoeff[ 1 ];
    for( i = 0; i < 3; i++ )
     pdwNextPix[ i ] += dwTmp * pPix[ i ];
    
    if ( bCrossCol ) {

     dwTmp = pXCoeff[ 1 ] * pYCoeff[ 1 ];
     for ( i = 0, ii = 3; i < 3; i++, ii++ )
      pdwNextPix[ ii ] += dwTmp * pPix[ i ];

    } /* end if */

   } /* end if */

   if ( pXCoeff[ 1 ] ) {

    x++;
    pdwCurrPix += 3;
    pdwNextPix += 3;

   } /* end if */
   
   pXCoeff += 2;
   pPix += 3;

  } /* end while */
  
  if ( pYCoeff[ 1 ] ) {

   // set result line
   pdwCurrPix = pdwCurrLn;
   pPix = pOutLine;
   
   for ( i = 3 * wNewWidth; i > 0; i--, pdwCurrPix++, pPix++ )
    *pPix = ((u8*)pdwCurrPix)[3];

   // prepare line buffers
   pdwCurrPix = pdwNextLn;
   pdwNextLn = pdwCurrLn;
   pdwCurrLn = pdwCurrPix;
   
   memset( pdwNextLn, 0, dwBuffLn );
   
   y++;
   pOutLine += dwOutLn;

  } /* end if */
  
  pYCoeff += 2;

 } /* end while */

 free( pRowCoeff );
 free( pColCoeff );
 free( pdwBuff );

 return 1;

} /* end ShrinkData */
//--------------------------------------------------------------
int EnlargeData( u8 *pInBuff, u16 wWidth, u16 wHeight, u8 *pOutBuff, u16 wNewWidth, u16 wNewHeight ) {

 u8 *pLine = pInBuff,
    *pPix  = pLine,
    *pPixOld,
    *pUpPix,
    *pUpPixOld;
 u8 *pOutLine = pOutBuff, *pOutPix;
 u32 dwInLn   = ( 3 * wWidth + 3 ) & ~3;
 u32 dwOutLn  = ( 3 * wNewWidth + 3 ) & ~3;

 int x, y, i;
 int bCrossRow, bCrossCol;
 
 int *pRowCoeff = CreateCoeffInt( wNewWidth, wWidth, 0 );
 int *pColCoeff = CreateCoeffInt( wNewHeight, wHeight, 0 );
 int *pXCoeff, *pYCoeff = pColCoeff;
 
 u32 dwTmp, dwPtTmp[ 3 ];
 
 y = 0;
 
 while( y < wHeight ) {
 
  bCrossRow = pYCoeff[ 1 ] > 0;
  x         = 0;
  pXCoeff   = pRowCoeff;
  pOutPix   = pOutLine;
  pOutLine += dwOutLn;
  pUpPix    = pLine;
  
  if ( pYCoeff[ 1 ] ) {

   y++;
   pLine += dwInLn;
   pPix = pLine;

  } /* end if */
  
  while( x < wWidth ) {

   bCrossCol = pXCoeff[ 1 ] > 0;
   pUpPixOld = pUpPix;
   pPixOld  = pPix;
   
   if( pXCoeff[ 1 ] ) {

    x++;
    pUpPix += 3;
    pPix += 3;

   } /* end if */
   
   dwTmp = *pXCoeff * *pYCoeff;
   
   for ( i = 0; i < 3; i++ )
    dwPtTmp[ i ] = dwTmp * pUpPixOld[ i ];

   if ( bCrossCol ) {

    dwTmp = pXCoeff[ 1 ] * *pYCoeff;
    for ( i = 0; i < 3; i++ )
     dwPtTmp[ i ] += dwTmp * pUpPix[ i ];

   } /* end if */

   if ( bCrossRow ) {

    dwTmp = *pXCoeff * pYCoeff[ 1 ];
    for ( i = 0; i < 3; i++ )
     dwPtTmp[ i ] += dwTmp * pPixOld[ i ];
    
    if ( bCrossCol ) {

     dwTmp = pXCoeff[ 1 ] * pYCoeff[ 1 ];
     for(i = 0; i < 3; i++)
      dwPtTmp[ i ] += dwTmp * pPix[ i ];

    } /* end if */

   } /* end if */
   
   for ( i = 0; i < 3; i++, pOutPix++ )
    *pOutPix = (  ( u8* )( dwPtTmp + i )  )[ 3 ];
   
   pXCoeff += 2;

  } /* end while */
  
  pYCoeff += 2;

 } /* end while */
 
 free( pRowCoeff );
 free( pColCoeff );

 return 1;

} /* end EnlargeData */
//--------------------------------------------------------------
int ScaleBitmap( u8* pInBuff, u16 wWidth, u16 wHeight, u8** pOutBuff, u16 wNewWidth, u16 wNewHeight ) {

 int lRet;

 // check for valid size
 if( wWidth > wNewWidth && wHeight < wNewHeight ) return 0;

 if( wHeight > wNewHeight && wWidth < wNewWidth ) return 0;

 // allocate memory
 *pOutBuff = ( u8* ) memalign(   128, (  ( 3 * wNewWidth + 3 ) & ~3  ) * wNewHeight   );

 if( !*pOutBuff )return 0;

 if( wWidth >= wNewWidth && wHeight >= wNewHeight )
  lRet = ShrinkData( pInBuff, wWidth, wHeight, *pOutBuff, wNewWidth, wNewHeight );
 else
  lRet = EnlargeData( pInBuff, wWidth, wHeight, *pOutBuff, wNewWidth, wNewHeight );

 return lRet;
 
} /* end ScaleBitmap */
//--------------------------------------------------------------
void Setup_GS(int gs_vmode)
{
	// GS Init
	gsGlobal = gsKit_init_global_custom(gs_vmode,
		GS_RENDER_QUEUE_OS_POOLSIZE+GS_RENDER_QUEUE_OS_POOLSIZE/2, //eliminates overflow
		GS_RENDER_QUEUE_PER_POOLSIZE);

	// Clear Screen
	gsKit_clear(gsGlobal, ColorBlack);

	// Screen Position Init
	gsGlobal->StartX = SCREEN_X; 
	gsGlobal->StartY = SCREEN_Y;

	// Buffer Init
	gsGlobal->PrimAAEnable = GS_SETTING_ON;
	gsGlobal->DoubleBuffering = GS_SETTING_OFF;
	gsGlobal->ZBuffering      = GS_SETTING_OFF;

	// Force Interlace and Field mode
	gsGlobal->Interlace = GS_INTERLACED;
	gsGlobal->Field     = GS_FIELD;

	// DMAC Init
	//dmaKit_init(D_CTRL_RELE_OFF,D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
	dmaKit_init(D_CTRL_RELE_OFF,D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC,
          D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
	dmaKit_chan_init(DMA_CHANNEL_GIF);
	dmaKit_chan_init(DMA_CHANNEL_FROMSPR);
	dmaKit_chan_init(DMA_CHANNEL_TOSPR);

	// Screen Init
	gsKit_init_screen(gsGlobal);
	gsKit_mode_switch(gsGlobal, GS_ONESHOT);
	gsKit_clear(gsGlobal, ColorBlack);

	// Font Init
	gsFont = gsKit_init_font(GSKIT_FTYPE_FONTM, NULL);
	gsKit_font_upload(gsGlobal, gsFont);
}
//--------------------------------------------------------------
void Update_Screen_Mode( void )
{
	// Init screen position
	gsGlobal->StartX = SCREEN_X;
	gsGlobal->StartY = SCREEN_Y;

	GS_SET_DISPLAY1(gsGlobal->StartX,		// X position in the display area (in VCK unit
			gsGlobal->StartY,		// Y position in the display area (in Raster u
			gsGlobal->MagX,			// Horizontal Magnification
			gsGlobal->MagY,			// Vertical Magnification
			(gsGlobal->Width * 4) -1,	// Display area width
			(gsGlobal->Height-1));		// Display area height

	GS_SET_DISPLAY2(gsGlobal->StartX,		// X position in the display area (in VCK units)
			gsGlobal->StartY,		// Y position in the display area (in Raster units)
			gsGlobal->MagX,			// Horizontal Magnification
			gsGlobal->MagY,			// Vertical Magnification
			(gsGlobal->Width * 4) -1,	// Display area width
			(gsGlobal->Height-1));		// Display area height
}
//--------------------------------------------------------------
void Load_Skin(void)
{
	jpgData* Jpg;
	u8*      ImgData;

	if( ( Jpg = jpgOpenRAW ( &background, size_background, JPG_WIDTH_FIX ) ) > 0 ){		
		if( ( ImgData = malloc (  Jpg->width * Jpg->height * ( Jpg->bpp / 8 ) ) ) > 0 ){
			if( ( jpgReadImage( Jpg, ImgData ) ) != -1 ){
				if( ( ScaleBitmap( ImgData, Jpg->width, Jpg->height, (void*)&TexSkin.Mem, SCREEN_WIDTH, SCREEN_HEIGHT ) ) != 0 ){
					TexSkin.PSM = GS_PSM_CT24;
					TexSkin.VramClut = 0;
					TexSkin.Clut = NULL;
					TexSkin.Width =  SCREEN_WIDTH;
					TexSkin.Height = SCREEN_HEIGHT;
					TexSkin.Filter = GS_FILTER_NEAREST;
					gsGlobal->CurrentPointer=0x15B000;
					TexSkin.Vram = gsKit_vram_alloc(gsGlobal,
				 	gsKit_texture_size(TexSkin.Width, TexSkin.Height, TexSkin.PSM),
				 		GSKIT_ALLOC_USERBUFFER);
					gsKit_texture_upload(gsGlobal, &TexSkin);
					free(TexSkin.Mem);
				} /* end if */
				jpgClose( Jpg );
			} /* end if */
		} /* end if */
		free(ImgData);
	} /* end if */
}
//--------------------------------------------------------------
void Clear_Screen(void)
{

	gsKit_clear(gsGlobal, ColorBlack);
	gsKit_prim_sprite_texture(gsGlobal,
	 &TexSkin, 0, 0, 0, 0,
	 SCREEN_WIDTH, SCREEN_HEIGHT, TexSkin.Width, TexSkin.Height,
	 0, ColorTrans);
	 
	//gsKit_prim_sprite(gsGlobal, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, ColorTrans);

}
//--------------------------------------------------------------

void Draw_Screen(void)
{
	if(updateScr_2){            //Did we render anything last time
		while(Timer() < updateScr_t+5);  //if so, delay to complete rendering
	}
	gsKit_sync_flip(gsGlobal);  //Await sync and flip buffers
	gsKit_queue_exec(gsGlobal); //Start rendering recent transfers for NEXT time
	updateScr_t = Timer();      //Note the time when the rendering started
	updateScr_2 = updateScr_1;  //Note if this rendering had expected updates
	updateScr_1 = 0;            //Note that we've nothing expected for next time
} //NB: Apparently the GS keeps rendering while we continue with other work

//--------------------------------------------------------------

void Draw_Sprite( u64 Color, int x1, int y1, int x2, int y2 )
{
	gsKit_prim_sprite_texture(gsGlobal, &TexSkin,
		x1, y1, x1, y1, x2, y2, x2, y2, 0, Color);
}
//--------------------------------------------------------------

void Print_XY(const unsigned char *s, int x, int y, u64 colour, float scale, float space)
{
	updateScr_1 = 1;
	
	gsFont->FontM_Spacing = space;
	gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
	gsKit_font_print_scaled(gsGlobal, gsFont, x, y, 0, scale, colour, (char*)s);		
	gsGlobal->PrimAlphaEnable = GS_SETTING_OFF;

}

//--------------------------------------------------------------
// end draw.c
//--------------------------------------------------------------
