/*****************************************************************************
 * Microchip Graphics Library Demo Application
 * This program shows how to use the Graphics Primitives Layer.
 *****************************************************************************
 * FileName:        MainDemo.c
 * Dependencies:    MainDemo.h
 * Processor:       PIC24, PIC32
 * Compiler:       	MPLAB C30, MPLAB C32
 * Linker:          MPLAB LINK30, MPLAB LINK32
 * Company:         Microchip Technology Incorporated
 *
 * Software License Agreement
 *
 * Copyright ?2008 Microchip Technology Inc.  All rights reserved.
 * Microchip licenses to you the right to use, modify, copy and distribute
 * Software only when embedded on a Microchip microcontroller or digital
 * signal controller, which is integrated into your product or third party
 * product (pursuant to the sublicense terms in the accompanying license
 * agreement).  
 *
 * You should refer to the license agreement accompanying this Software
 * for additional information regarding your rights and obligations.
 *
 * SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS?WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY
 * OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR
 * PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR
 * OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION,
 * BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT
 * DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL,
 * INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
 * COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY
 * CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
 * OR OTHER SIMILAR COSTS.
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Anton Alkhimenok		30/05/07	...
 *****************************************************************************/

/*
******************************************************************************
* Version 120309
* (1) for ssd1982.c, GetReg(), GetPixel() for 8-bit only verified. 
* No code for 16-bit accessing modified yet.
* (2) for MainDemo.c, add #define PIC32_STARTER_KIT to differentiate
* between PIC32MX Starter Kit and PIC24 Eval Board C2 with PIC32MX
* onboard when no crystal soldered.
* Programmer: John Leung
* Date: 12-03-2009
******************************************************************************
*/
/*
****************************** Breakout GAME *****************************
* Description : Destroy the Wall
* Use : button 0 & 1 for Left
*       button 2 & 3 for Right
* Programmer: Fabrice Muller
* Date: 14-03-2009
* HW info : PIC24 Eval Rev C , CPU : PIC32 PIM at 72Mhz
******************************************************************************
*/
/*
********************* Breakout GAME , Bugs & improvment **********************
* Programmer: Fabrice Muller
* Date: 16-03-2009
* The srand() function is now used with a 32bits timer to get real random number
* Randomize the BallX position at start
* Randomize the Dx Ball direction before start
*/

#include "BreakOut.h"
// comment the following line if your are using Internal Fast Osc for PIC24 Eval BoardC2
// #define PIC32_STARTER_KIT

// Configuration bits
#ifdef __PIC32MX
#pragma config FPLLODIV = DIV_1, FPLLMUL = MUL_18, FWDTEN = OFF, FCKSM = CSECME, FPBDIV = DIV_1, FPLLIDIV = DIV_2
#pragma config OSCIOFNC = ON, FSOSCEN = OFF, IESO = OFF
#pragma config CP = OFF, BWP = OFF, PWP = OFF

#ifdef PIC32_STARTER_KIT
// PIC32MX Starter Kit
#pragma config FNOSC = PRIPLL, POSCMOD = XT
#else
// PIC24 Eval board RevC2 with PIC32MX360F512L onboard, Internal Fast Osc for 8MHz*18= 72MHz
#pragma config FNOSC = FRCPLL, POSCMOD = OFF, ICESEL = ICS_PGx2
#endif

#else
_CONFIG2(FNOSC_PRIPLL & POSCMOD_XT) // Primary XT OSC with PLL
_CONFIG1(JTAGEN_OFF & FWDTEN_OFF)   // JTAG off, watchdog timer off
#endif

//Define TickRate for the 32 bits Timer (Timer45)
#define TICK_RATE 72000000

///////////////////////////////// FONTS ///////////////////////////////////////
extern const FONT_FLASH Font25;
extern const FONT_FLASH Font35;

// Bricks array
short Brick[17][10];                  	// 10 lines of 17 bricks 
// Racket position
short Racket,OldRacket;                 // Position de depart de la Racket
short RackRight;                       	// Pixel droit de la Racket
short RackSize = 40;                  	// Taille de la Racket
// Ball position
short BallX;                         	// Position de la balle en X
short BallY;                         	// Position de la balle en Y
short OldBallX;                      	// Position de la balle sauvegard?
short OldBallY;                      	// Position de la balle sauvegard?
// Ball direction
signed short Dx,Dy;                     // X & Y Ball direction
// Shooted Brick coordinates
short   ShootX;
short   ShootY;
// Variables diverses
char  Number[5];                     	// Pour Afficher un score (si necessaire)
short Score = 0;                      	// ...
short GMx,GMy;							//Max X and Max Y from the Screen

// Declaration des fonctions

void KasBrick(void);
void ShowWall(void);
void TestBrick(void);
void RacketTest(void);
void GameOverTxt(void);
void Win_Party(void);


// Predefined Colors (just for memo)
// BRIGHTBLUE,BRIGHTGREEN,BRIGHTCYAN,BRIGHTRED,BRIGHTMAGENTA,BRIGHTYELLOW,
// BLUE,GREEN,CYAN,RED,MAGENTA,BROWN,LIGHTGRAY,DARKGRAY,LIGHTBLUE,LIGHTGREEN,
// LIGHTCYAN,LIGHTRED,LIGHTMAGENTA,YELLOW,WHITE

int main(void){
	short width, height;

	#ifdef __PIC32MX__
    	INTEnableSystemMultiVectoredInt();
    	SYSTEMConfigPerformance(GetSystemClock());
	#endif

	/////////////////////////////////////////////////////////////////////////////
  InitGraph();

	// Set buttons pins as inputs
	PORTSetPinsDigitalIn(IOPORT_A, BIT_14);	//Button 0	Left
	PORTSetPinsDigitalIn(IOPORT_A, BIT_15);	//Button 1	Left
	PORTSetPinsDigitalIn(IOPORT_D, BIT_8);	//Button 2	Right
	PORTSetPinsDigitalIn(IOPORT_D, BIT_9);	//Button 3	Right

	//********************************************
	// Clear Screen
											
	SetColor(BLACK);
	ClearDevice();

	//~~~~~~~~~~~~~     Configurer a 32 bits Timer    ~~~~~~~~~~~~~
	OpenTimer45(T45_ON | T45_PS_1_64 | T45_32BIT_MODE_ON | T45_SOURCE_INT,TICK_RATE);
	// Ticks for 72Mhz
	// 1 sec = 1125000 ticks 
	// 1 mS  =    1125 ticks
	// 1 µS  =   1.125 ticks


    // Init the Timer
    WriteTimer45(0x0);

	GMx = GetMaxX();
	GMy = GetMaxY();

  	SetFont((void*)&Font25);
  	SetColor(BRIGHTGREEN);
  	width  = GetTextWidth("SupraBotics",(void*)&Font25);
  	height = GetTextHeight((void*)&Font25);

  	OutTextXY( (GMx-width)>>1,
               (GMy-height)>>1,
                "SupraBotics");

  	DelayMs(2000);
	SetColor(BLACK);
	ClearDevice();

  	SetColor(BRIGHTBLUE);
  	width = GetTextWidth("Press a button to start",(void*)&Font25);
  	height = GetTextHeight((void*)&Font25);

  	OutTextXY( (GMx-width)>>1,
               (GMy-height)>>1,
                "Press a button to start");
	
	//Wait up a button is pressed
	while(PORTReadBits(IOPORT_A, BIT_15) && PORTReadBits(IOPORT_D, BIT_8) &&
      	  PORTReadBits(IOPORT_D, BIT_9)  && PORTReadBits(IOPORT_A, BIT_14))
      		{continue;}

	SetColor(BLACK);
	ClearDevice();

	//Value for the random generator
	srand(ReadTimer45());
	
    while(1)
	{
		DelayMs(1000);
		//Launch the Game
   		KasBrick();
    } 
}

void KasBrick(void)
{
   short   value1 = 0;

   // Show the wall
   ShowWall();

   // Init the Racket Position
   Racket = 0;

   // Init the Ball Position
   BallX = (rand() % (GMx - 40)) + 20; ;
   BallY = 200 ;
   OldBallX = BallX ;
   OldBallY = BallY ;

   //Make sure Dx is never 0
   Dx = 0;
   while (Dx == 0)	
   	{Dx = (rand() % 5) - 2;}

   Dy = -2;
   // main routine
   do
     {
      // Read Buttons 
	  if (!PORTReadBits(IOPORT_A, BIT_15) | !PORTReadBits(IOPORT_A, BIT_14)) Racket = Racket - 6;
	  if (!PORTReadBits(IOPORT_D, BIT_8)  | !PORTReadBits(IOPORT_D, BIT_9))  Racket = Racket + 6;
      // Clip the Racket in screen
      if (Racket > (GMx - RackSize)) Racket = (GMx-RackSize);
	  if (Racket < 1) Racket = 1;
      RackRight = Racket + RackSize;
      // Delete Racket from old position
      // and show it to new position
      if (Racket != OldRacket)
       {
		 SetColor(BLACK);
         Bar(OldRacket,220,OldRacket + RackSize,225);
		 SetColor(BRIGHTBLUE);
         Bar(Racket,220,RackRight,225);
       }
	  OldRacket = Racket;
      OldBallX = BallX ;
      OldBallY = BallY ;
      BallX += Dx;
      BallY += Dy;
      // Ball go outside the top of screen ?
      if (BallY < 2) Dy = abs(Dy);
      // The Ball come at Racket level in Y ?
      // Test if we touch the Ball with the Racket
      if (BallY >= 218) RacketTest();
      // Ball touch the right side of the screen ?
      if (BallX > 317) Dx = -Dx;
      // Ball touch the left side of the screen ?
      if (BallX < 2) Dx = abs(Dx);
      ShootY = (short)(BallY / 12);
	  SetColor(BLACK);
      Bar(OldBallX - 2,OldBallY - 2,OldBallX + 2,OldBallY + 2);
	  SetColor(BRIGHTRED);
      Bar(BallX - 2,BallY - 2,BallX + 2,BallY + 2);
      // The Ball shoot a Brick ?
      if ((ShootY >= 0) && (ShootY <= 9))
         {
            ShootX = (short)(BallX / 18);
            if (ShootX <=17)
               // Test if we shoot a Brick
               TestBrick();
         }
      DelayMs(20);
     }
   while (TRUE);

}

// Show the Wall
void ShowWall(void)
 {
   short  x;
   short  y;

   for (x=0;x<17;x++)
      for (y=0;y<10;y++)
         {
	  	  SetColor(BRIGHTGREEN);
          Bar(x * 18, y * 12, (x*18)+16, (y*12)+10);
          Brick[x][y] = 1;
         }
 }

// Test if we Shoot a Brick
void TestBrick(void)
 {
     // If they is a Brick here ...
     if (Brick[ShootX][ShootY] == 1)
        {
          Brick[ShootX][ShootY] = 0;
          // Erase the Brick
		  SetColor(BLACK);
          Bar(ShootX * 18,ShootY * 12,(ShootX * 18) + 16,(ShootY * 12) + 10);
          // We update the Score ...
          Score += 10;
		  snprintf(Number,4,"%u",Score);
          Bar(0,120,62,150);
		  SetColor(BRIGHTGREEN);
		  OutTextXY(0,120,Number);
          // Is the whole Wall destroyed ?? (Score Max = 1700)
          if (Score >= 1700) Win_Party();
          // Verify the Y Ball direction and invert it
          if (Dy > 0)
            Dy = -Dy;
          else
            Dy = abs(Dy);
        }
 }

// Test if the Ball go behind the Racket
void RacketTest(void)
 {
   // Ball at right from the racket ?
   if (BallX < Racket)
      GameOverTxt();
   // Ball at left from the racket ?
   if (BallX > RackRight)
      GameOverTxt();
   // Ball touch the Racket , we invert Y direction
   Dy = -Dy;
   // We test where the Ball touch the racket
   // Left side from the Racket (bord)
   if ((BallX >= Racket) && (BallX <= (Racket + (unsigned short)(RackSize/4))))
      // If the Ball come from Left we invert X direction
      if (Dx > 0)
        Dx = -3;
   // If the Ball come from left (1/4 from Racket center)
   if ((BallX >= (Racket + (unsigned short)(RackSize/4))) && (BallX <= (Racket + (unsigned short)(RackSize/2))))
      // If the Ball come from Left we invert X direction
      if (Dx > 0)
        Dx = -2;

   // Sur la partie Droite de la Racket bord)
   if ((BallX >= (Racket + (unsigned short)(RackSize / 2)+(unsigned short)(RackSize / 4))) && (BallX <= RackRight))
      // If the Ball come from Right we invert X direction
      if (Dx < 0)
        Dx = 3;
    // If the Ball come from Right (1/4 after the Racket center)
   if ((BallX >= (Racket + (unsigned short)(RackSize / 2))) && ((BallX <= RackRight)-(unsigned short)(RackSize / 4)))
      // If the Ball come from Right we invert X direction
      if (Dx < 0)
        Dx = 2;
}

// End of the Game
void GameOverTxt(void)
 {
	// The Ball is passed behind the Racket
	short width, height;
    SetFont((void*)&Font25);
    SetColor(BRIGHTRED);
    width = GetTextWidth("GAME OVER",(void*)&Font25);
    height = GetTextHeight((void*)&Font25);

    OutTextXY( (GMx-width)>>1,
               (GMy-height)>>1,
                "GAME OVER");

    while(1){continue;}
 }

void Win_Party(void)
 {
   // You win the Party ;)
	short width, height;

    SetFont((void*)&Font25);
    SetColor(BRIGHTYELLOW);
    width = GetTextWidth("YOU WIN",(void*)&Font25);
    height = GetTextHeight((void*)&Font25);

    OutTextXY( (GMx-width)>>1,
               (GMy-height)>>1,
                "YOU WIN");

    while(1){continue;}
 }
