#include <math.h>
#include <io.h>
#include <system.h>
#include <stdio.h>
#include "sky.h"

//#define lookupLength 3480
//#define RAD 0.0018055130193045
//#define HALF_RAD 0.00090275650965225

#define lookupLength 4096
#define RAD 0.0015339807878856
#define HALF_RAD 0.0007669903939428

//#define lookupLength 3072
//#define RAD 0.0020453077171808
//#define HALF_RAD 0.0010226538585904


#define screenWidth 640
#define halfScreenWidth 320
#define screenHeight 480
#define texWidth 64
#define texHeight 64
#define mapWidth 32
#define mapHeight 32
#define extensionFactor 6
#define loopbackFactor 4

#define RED_8BIT 0xE0 //red
#define GREEN_8BIT 0x1C //green
#define BLUE_8BIT 0x03 //blue
#define YELLOW_8BIT 0xFC //yellow
#define WHITE_8BIT 0xFF //white
#define VIOLET_8BIT 0xE2 //violet

#define CTRL_WRITE_HIGH 0
#define CTRL_WRITE_LOW 0

#define posShift 22




#define IOWR_RAM_DATA(base, offset, data) \
IOWR_32DIRECT(base, (offset*4), data)

#define IORD_RAM_DATA(base, offset) \
IORD_32DIRECT(base, (offset*4) )

#define IOSKYWR_RAM_DATA(base, offset, data) \
  IOWR_16DIRECT(base, (offset) * 2, data)

char worldMap[mapWidth][mapHeight]=
{
 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
 9,0,0,7,0,8,0,8,0,0,8,7,0,2,5,2,6,2,5,2,0,6,0,0,0,0,0,0,0,0,0,9,
 9,2,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,6,0,9,
 9,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,9,
 9,2,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,9,
 9,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,6,6,0,6,7,0,0,0,5,0,0,0,0,9,
 9,8,8,8,0,8,8,8,8,8,8,4,4,4,4,4,4,6,0,0,0,0,0,0,0,0,0,0,0,7,0,9,
 9,6,7,7,0,7,7,7,7,0,8,0,8,0,8,0,8,4,0,4,0,6,7,0,0,0,0,0,0,0,0,9,
 9,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,0,0,0,0,0,0,0,0,8,0,8,0,0,9,
 9,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,9,
 9,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,6,0,6,0,0,0,0,0,0,0,0,0,9,
 9,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,4,6,0,6,6,0,0,0,0,0,0,0,0,9,
 9,2,7,7,0,7,7,7,7,8,8,4,0,6,8,4,8,3,3,3,0,3,6,0,0,0,0,0,0,0,0,9,
 9,6,2,2,0,2,2,2,2,4,6,4,0,0,6,0,6,3,0,0,0,0,0,0,0,0,0,0,0,0,0,9,
 9,6,0,0,0,0,0,2,2,4,0,0,0,0,0,0,4,3,0,0,0,0,6,0,0,0,0,0,0,0,0,9,
 9,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,0,3,2,0,4,3,0,0,9,
 9,7,0,0,0,0,0,0,1,4,4,0,4,4,6,0,6,3,3,0,0,0,0,0,0,0,0,0,0,0,0,9,
 9,0,0,0,0,0,0,0,2,2,2,0,2,2,2,6,6,0,0,5,0,5,0,0,0,0,0,0,0,0,0,9,
 9,7,0,0,0,0,0,2,2,2,0,0,0,2,2,3,5,0,5,0,0,0,5,0,0,0,0,0,0,0,0,9,
 9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,9,
 9,7,0,0,0,0,0,0,3,4,2,1,1,2,0,4,4,3,1,2,2,0,0,0,0,0,0,0,0,0,0,9,
 9,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,7,2,7,2,2,2,0,7,0,8,8,0,5,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,7,2,7,2,2,2,0,7,0,8,8,0,5,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,0,0,0,0,2,2,0,7,0,8,8,0,5,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,0,2,0,0,0,0,0,7,0,8,8,0,3,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,0,2,0,2,2,2,0,2,0,8,8,0,5,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,0,2,0,0,0,1,0,2,0,3,3,0,5,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,0,2,7,2,0,2,0,1,0,8,8,0,3,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,2,0,2,7,1,0,1,0,1,0,8,8,0,5,0,4,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,2,0,0,0,2,7,2,1,2,0,7,0,0,0,0,5,0,0,0,7,0,0,0,0,0,0,0,0,0,0,9,
 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9
};


static void  note_isr (void * context, alt_u32 id){

	int i;
	//if(offset>=14531){
	if(offset>=227327){
		offset=0;
	}
	else{
		offset++;
	}

  	new = IORD_8DIRECT(CFI_FLASH_0_BASE,offset);
  	IOWR_8DIRECT(SOUND_CONTROLLER_0_BASE,0,new);

  	return;

}

//fixed point conversion functions
int doubleToInt(double);


void DrawTexture(unsigned int columnIndex, unsigned int texX, unsigned int rowStart, unsigned int rowEnd, unsigned int side, unsigned int texNum, unsigned int invLineHeight , unsigned int line_minus_h);
void DrawAccelerate(int angle, int posX, int posY, int countstep, int rayDirX, int rayDirY, unsigned int columnIndex);
int absVal(int v);

///UNECESSARY CAN BE IMPLEMENTED WITHOUT THESE TWO LOOKUP
int dirsine[lookupLength];
int dircosine[lookupLength];

/// MEMORY HERE CAN BE CUT DOWN IF NECESSARY
int sine[lookupLength];
int cosine[lookupLength];


int main()
{

	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 0, 0);
	int dir = 0;
	int posX = doubleToInt(21.5), posY = doubleToInt(11.5);  //x and y start position
  int x =0;

  int p, q;


  IOSKYWR_RAM_DATA(SKYGEN_0_BASE, 262143, 0x0000);

  for(p = 0; p < 480; p++){
	  for(q = 0; q < 512; q++)
	  {

		  IOSKYWR_RAM_DATA(SKYGEN_0_BASE, p*512+q, (sky[p*1024+q*2+1]<<8) + (sky[p*1024+q*2]));

	  }
  }

  IOSKYWR_RAM_DATA(SKYGEN_0_BASE, 262143, 0x000F);
  double sine_temp;
  double cosine_temp;

	for(x = 0; x < lookupLength ; x++)
	{
		//calculate ray position and direction
		sine_temp = sin(x*RAD + HALF_RAD);
		cosine_temp = cos(x*RAD + HALF_RAD);

		dirsine[x] = doubleToInt(sin(x*RAD));
		dircosine[x] = doubleToInt(cos(x*RAD));
		sine[x] = doubleToInt(sine_temp);
		cosine[x] = doubleToInt(cosine_temp);
	}


  int angle;
  int fish_angle;
  int move;

  int rayDirX;
  int rayDirY;
  int count_step;

  int forward = 0;
  int backward = 0;
  int left = 0;
  int right = 0;

  unsigned char code = 0;
  int hardwareData = 0;


  alt_irq_register(SOUND_CONTROLLER_0_IRQ ,NULL,(void*) note_isr);

  while(1)
  {

	  code = 0;

	  code = IORD_8DIRECT(DE2_PS2_1_BASE, 1);

	  switch(code)
	  {
	  	  case 'u':
	  		  forward = 1;
	  		  backward = 0;
	  		  break;
	  	  case 'r':
	  		  forward = 0;
	  		  backward = 1;
	  		  break;
	  	  case 't':
	  		  right = 1;
	  		  left = 0;
	  		  break;
	  	  case 'k':
	  		  left = 1;
	  		  right = 0;
	  		  break;
	  	  case 'U':
	  		  forward = 0;
	  		  break;
	  	  case 'R':
	  		  backward = 0;
	  		  break;
	  	  case 'T':
	  		  right = 0;
	  		  break;
	  	  case 'K':
	  		  left = 0;
	  		  break;

	  	  case ')':
	  		forward = 0;
	  		backward = 0;
	  		right = 0;
	  		left = 0;
	  		break;

	  }

     x = 0;
    for(k = -halfScreenWidth; k < halfScreenWidth; k++)
    {
        angle = dir + k;

        if ( angle < 0)
                angle += lookupLength;

        if (angle >= lookupLength)
                angle -= lookupLength;

        fish_angle = k;

        if ( fish_angle < 0)
            fish_angle += lookupLength;

        if (fish_angle >= lookupLength)
             fish_angle -= lookupLength;


      //calculate ray position and direction

      rayDirX = cosine[angle]>>extensionFactor;
      rayDirY = sine[angle]>>extensionFactor;
      count_step = cosine[fish_angle]>>extensionFactor;


     DrawAccelerate(angle, posX, posY, count_step, rayDirX, rayDirY, x);
     IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 0, 0);

     hardwareData = IORD_32DIRECT(NIOSINTERFACE_1_0_BASE, 1);

	 while (!(hardwareData & 1)){
		 hardwareData = IORD_32DIRECT(NIOSINTERFACE_1_0_BASE, 1);
	  }

      IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 0, 0xFFFFFFFF);

      x++;
    }



    //move forward if no wall in front of you
    if (forward == 1)
    {
        move = dircosine[dir]>>4;
        if(worldMap[(posX + move)>>posShift][posY>>posShift] == 0)
                posX += move;

        move = dirsine[dir]>>4;
        if(worldMap[posX>>posShift][(posY+move)>>posShift] == 0)
                posY += move;
    }
    //move backwards if no wall behind you
    if (backward == 1)
    {
        move = dircosine[dir]>>4;
        if(worldMap[(posX - move)>>posShift][posY>>posShift] == 0)
                posX -= move;

        move = dirsine[dir]>>4;
        if(worldMap[posX>>posShift][(posY - move)>>posShift] == 0)
                posY -= move;
    }
    //rotate to the right
    if (right == 1)
    {
      //both camera direction and camera plane must be rotated
       dir += 13;

       if(dir >= lookupLength)
           dir -= lookupLength;
    }
    //rotate to the left
    if (left == 1)
    {
      //both camera direction and camera plane must be rotated
        dir -= 13;

        if (dir <0)
            dir += lookupLength;
    }


  }



    return 0;
}

int absVal(int v)
{
  return v * ((v>0) - (v<0));
}

int intToDouble(int a)
{

    return a>>posShift;

}


int doubleToInt(double a)
{
    return (int)(a*(1<<posShift));
}


void DrawTexture(unsigned int columnIndex, unsigned int texX, unsigned int rowStart, unsigned int rowEnd, unsigned int side, unsigned int texNum, unsigned int invLineHeight , unsigned int line_minus_h)
{

	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 8, (columnIndex  << 22));
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 1, ((texNum & 3) + (texX <<2) + (rowEnd<< 8) + (rowStart<<17)+ ((invLineHeight & 0x3F)<<26)));
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 2, ((invLineHeight & 0x3FFFF) >> 6) + ((line_minus_h & 0x3FFFF) << 12 ) + (side << 31));
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 0, 1);
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 0, 0);

}

void DrawAccelerate(int angle, int posX, int posY, int countstep, int rayDirX, int rayDirY, unsigned int columnIndex)
{

	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 8, (columnIndex << 22));
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 1, posX);
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 2, posY );
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 3, countstep );
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 4, rayDirX);
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 5, rayDirY );
	IOWR_RAM_DATA(NIOSINTERFACE_1_0_BASE, 6, angle & 0x03FF);

}

