 
/*
 * Userspace program that communicates with the viewtube device driver
 * through ioctls
 *
 * Original by
 * Stephen A. Edwards
 * Columbia University
 *
 * Adapted by Benjamin Allison (bja2142)
 */

#include <stdio.h>
#include <linux/types.h>
#include "viewtube_kmod.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <ncurses.h>
#include <stdlib.h>
#include <pthread.h>

#define WINDOW_DIR_UP    0
#define WINDOW_DIR_LEFT  1
#define WINDOW_DIR_RIGHT 2
#define WINDOW_DIR_DOWN  3

#define WINDOW_Y_MOVE_INTERVAL 10
#define WINDOW_X_MOVE_INTERVAL 15

#define GRAPHIC_WIDTH 1500
#define GRAPHIC_HEIGHT 1814

#define WINDOW_WIDTH  500
#define WINDOW_HEIGHT 425

#define RIGHT_WALL (GRAPHIC_WIDTH - WINDOW_WIDTH -1)
#define VERTICAL_FLOOR (GRAPHIC_HEIGHT - WINDOW_HEIGHT -1)

int viewtube_fd;

viewtube_background_window_position_t vla;

static const viewtube_color_t colors[] = {
    { 0xff, 0x00, 0x00 }, /* Red */
    { 0x00, 0x77, 0x00 }, /* Green */
    { 0x00, 0x00, 0xff }, /* Blue */
    { 0xff, 0xff, 0x00 }, /* Yellow */
    { 0x00, 0xff, 0xff }, /* Cyan */
    { 0xff, 0x00, 0xff }, /* Magenta */
    { 0x80, 0x80, 0x80 }, /* Gray */
    { 0x00, 0x00, 0x00 }, /* Black */
    { 0xff, 0xff, 0xff }  /* White */
  };

  viewtube_sprite_t sprite_letter_one = {
                                          .type = SPRITE_TYPE_MONO,
                                          .jump = 0,
                                          .table_index = 4,
                                          .new_x = 150,
                                          .new_y = 450,
                                          .obj = {.mono = {
                                            .sprite_num = 7,
                                            .red = 0xff,
                                            .green = 0xff,
                                            .blue = 0xff
                                          }}
                                        };
  viewtube_sprite_t sprite_letter_two = {
                                          .type = SPRITE_TYPE_MONO,
                                          .jump = 0,
                                          .table_index = 5,
                                          .new_x = 158,
                                          .new_y = 450,
                                          .obj = {.mono = {
                                            .sprite_num = 8,
                                            .red = 0xff,
                                            .green = 0xff,
                                            .blue = 0xff
                                          }}
                                        };
  viewtube_sprite_t sprite_letter_three = {
                                          .type = SPRITE_TYPE_MONO,
                                          .jump = 0,
                                          .table_index = 3,
                                          .new_x = 166,
                                          .new_y = 450,
                                          .obj = {.mono = {
                                            .sprite_num = 4,
                                            .red = 0xff,
                                            .green = 0xff,
                                            .blue = 0xff
                                          }}
                                        };


  viewtube_sprite_t sprite_car_one = {
                                          .type = SPRITE_TYPE_COLOR,
                                          .jump = 0,
                                          .table_index = 0,
                                          .new_x = 0,
                                          .new_y = 100,
                                          .obj = {.color = {
                                            .sprite_num = 0,
                                            .width = 17,
                                            .height = 26
                                          }}
                                        };
  viewtube_sprite_t sprite_car_two = {
                                          .type = SPRITE_TYPE_COLOR,
                                          .jump = 0,
                                          .table_index = 1,
                                          .new_x = 300,
                                          .new_y = 400,
                                          .obj = {.color = {
                                            .sprite_num = 0,
                                            .width = 17,
                                            .height = 26
                                          }}
                                        };
  viewtube_sprite_t sprite_car_three = {
                                          .type = SPRITE_TYPE_COLOR,
                                          .jump = 0,
                                          .table_index = 2,
                                          .new_x = 200,
                                          .new_y = 400,
                                          .obj = {.color = {
                                            .sprite_num = 0,
                                            .width = 17,
                                            .height = 26
                                          }}
                                        };
  viewtube_sprite_t sprite_car_four = {
                                          .type = SPRITE_TYPE_COLOR,
                                          .jump = 0,
                                          .table_index = 3,
                                          .new_x = 500,
                                          .new_y = 0,
                                          .obj = {.color = {
                                            .sprite_num = 0,
                                            .width = 17,
                                            .height = 26
                                          }}
                                        };

WINDOW * curses_window;

unsigned short ushort_min(unsigned short left, unsigned short right)
{
  if (left > right) return right;
  return left;
}

unsigned short ushort_max(unsigned short left, unsigned short right)
{
  if (left > right) return left;
  return right;
}

unsigned short decrease(unsigned short original, unsigned short interval, unsigned short bound)
{
  if ( (unsigned short) (original - interval) > original)
  {
    return bound;
  }
  return ushort_max(original - interval,bound);
}

unsigned short increase(unsigned short original, unsigned short interval, unsigned short bound)
{
  if ( (unsigned short) (original + interval) < original)
  {
    return bound;
  }
  return ushort_min(original + interval,bound);
}


/* Set x,y position of window into background*/
void set_viewtube_background_position(unsigned short x, unsigned short y)
{
  printw("set_viewtube_background_position(%d,%d)\n",x,y);
  vla.coordinate_x = x;
  vla.bounce = 1;
  if (ioctl(viewtube_fd, VIEWTUBE_SET_BACKGROUND_POS_X, &vla)) {
      printw("ioctl(VIEWTUBE_SET_BACKGROUND_POS_X) failed\n");
      return;
  }
  vla.coordinate_y = y;
  if (ioctl(viewtube_fd, VIEWTUBE_SET_BACKGROUND_POS_Y, &vla)) {
      printw("ioctl(VIEWTUBE_SET_BACKGROUND_POS_Y) failed\n");
      return;
  }
}

void set_viewtube_sprite_data(viewtube_sprite_t * sprite)
{
  printw("set_viewtube_sprite_data()\n");
  if (ioctl(viewtube_fd, VIEWTUBE_UPDATE_SPRITE_AT_INDEX, sprite)) {
      printw("ioctl(VIEWTUBE_UPDATE_SPRITE_AT_INDEX) failed\n");
      return;
  }
  usleep(1000);
}

void set_viewtube_sprite_pos(viewtube_sprite_t * sprite)
{
  printw("set_viewtube_sprite_pos()\n");
  if (ioctl(viewtube_fd, VIEWTUBE_MOVE_SPRITE_TO_NEW_POS, sprite)) {
      printw("ioctl(VIEWTUBE_MOVE_SPRITE_TO_NEW_POS) failed\n");
      return;
  }
  usleep(1000);
}

void poll_background_position()
{
  if (ioctl(viewtube_fd, VIEWTUBE_GET_BACKGROUND_POS, &vla)) {
      printw("ioctl(VGA_BALL_GET_POS) failed\n");
      return;
  }
  printw("cur x/y:(%d,%d)\n",vla.coordinate_x,vla.coordinate_y);
}

/* Set x,y position of window into background*/
void move_viewtube_background_position_up(unsigned char direction, unsigned short interval)
{
  printw("move_viewtube_background_position(%02x)\n",direction);

  poll_background_position();
  vla.bounce = 0;
  if(WINDOW_DIR_UP == direction)
  {
    vla.coordinate_y = decrease(vla.coordinate_y, interval, 0);
    if (ioctl(viewtube_fd, VIEWTUBE_SET_BACKGROUND_POS_Y, &vla)) {
        printw("ioctl(VIEWTUBE_SET_BACKGROUND_POS_Y) failed\n");
        return;
    }
  } 
  else if (WINDOW_DIR_DOWN == direction)
  {
    vla.coordinate_y = increase(vla.coordinate_y, interval, VERTICAL_FLOOR);
    if (ioctl(viewtube_fd, VIEWTUBE_SET_BACKGROUND_POS_Y, &vla)) {
        printw("ioctl(VIEWTUBE_SET_BACKGROUND_POS_Y) failed\n");
        return;
    }
  }
  else if (WINDOW_DIR_LEFT == direction)
  {
    vla.coordinate_x = decrease(vla.coordinate_x, interval, 0);
    if (ioctl(viewtube_fd, VIEWTUBE_SET_BACKGROUND_POS_X, &vla)) {
        printw("ioctl(VIEWTUBE_SET_BACKGROUND_POS_X) failed\n");
        return;
    }
  }
  else if (WINDOW_DIR_RIGHT == direction)
  {
    vla.coordinate_x = increase(vla.coordinate_x, interval, RIGHT_WALL);
    
    if (ioctl(viewtube_fd, VIEWTUBE_SET_BACKGROUND_POS_X, &vla)) {
        printw("ioctl(VIEWTUBE_SET_BACKGROUND_POS_X) failed\n");
        return;
    }
  }
  printw("new x/y:(%d,%d)\n",vla.coordinate_x,vla.coordinate_y);
}


void setup_curses(void)
{
  curses_window = initscr();
  keypad(curses_window,1);
  noecho();

}

void update_display(void)
{
  refresh();
}

void cleanup(void)
{
  echo();
  endwin();
}

void menu(void)
{
  printw("Use WASD or arrows to move. Backspace to exit\n");
}

int main()
{

  int i;
  static const char filename[] = "/dev/viewtube";





  if ( (viewtube_fd = open(filename, O_RDWR)) == -1) {
    fprintf(stderr, "could not open %s\n", filename);
    return -1;
  }
  setup_curses();
  atexit(cleanup);
  
  printf("window setup\n");
  update_display();
  int ch;

  while( (ch = getch()) != KEY_BACKSPACE)
  {
    clear();
    curs_set(0);
    menu();
    switch(ch)
    {
      case KEY_UP:
        if(vla.coordinate_y > 0)
        {
          move_viewtube_background_position_up(WINDOW_DIR_UP,1);
        }
        break;
      case 'w':
        if(vla.coordinate_y > 0)
        {
          move_viewtube_background_position_up(WINDOW_DIR_UP,WINDOW_Y_MOVE_INTERVAL);
        }
        break;
      case KEY_LEFT:
        if(vla.coordinate_x > 0)
        {
          move_viewtube_background_position_up(WINDOW_DIR_LEFT,1);
        }
        break;
      case 'a':
        if(vla.coordinate_x > 0)
        {
          move_viewtube_background_position_up(WINDOW_DIR_LEFT,WINDOW_X_MOVE_INTERVAL);
        }
        break;
      case KEY_DOWN:
        if(vla.coordinate_y < VERTICAL_FLOOR)
        {
          move_viewtube_background_position_up(WINDOW_DIR_DOWN,1);
        }
        break;
      case 's':
        if(vla.coordinate_y < VERTICAL_FLOOR)
        {
          move_viewtube_background_position_up(WINDOW_DIR_DOWN,WINDOW_Y_MOVE_INTERVAL);
        }
        break;
      case KEY_RIGHT:
        if(vla.coordinate_x < RIGHT_WALL)
        {
          move_viewtube_background_position_up(WINDOW_DIR_RIGHT,1);
        }
        break;
      case 'd':
        if(vla.coordinate_x < RIGHT_WALL)
        {
          move_viewtube_background_position_up(WINDOW_DIR_RIGHT,WINDOW_X_MOVE_INTERVAL);
        }
        break;
      case 't':
        sprite_car_one.new_x ^= 100;
        set_viewtube_sprite_pos(&sprite_car_one);
        
        break;
      case 'u':
        sprite_car_two.new_y ^= 400;
        set_viewtube_sprite_pos(&sprite_car_two);
        
        break;
      case 'l':
        set_viewtube_sprite_pos(&sprite_letter_three);
        break;
      case 'b':
        sprite_letter_one.obj.mono.sprite_num = 1;
        sprite_letter_two.obj.mono.sprite_num = 50;
        sprite_letter_three.obj.mono.sprite_num = 30;
        set_viewtube_sprite_data(&sprite_letter_three);
        set_viewtube_sprite_data(&sprite_letter_two);
        set_viewtube_sprite_data(&sprite_letter_one);
        break;
      case 'm':
        sprite_letter_three.new_x = (sprite_letter_three.new_x + 20) % 400;
        sprite_letter_two.new_x = (sprite_letter_two.new_x + 20) % 400;
        sprite_letter_one.new_x  = (sprite_letter_one.new_x + 20 )% 400;
        set_viewtube_sprite_pos(&sprite_letter_three);
        set_viewtube_sprite_pos(&sprite_letter_two);
        set_viewtube_sprite_pos(&sprite_letter_one);
        break;
      case 'r':
	    set_viewtube_background_position(RIGHT_WALL-1,VERTICAL_FLOOR -1); 
	    break;
      case 'z':
	    set_viewtube_background_position(0,0);
	    break;
      case 'p':
	    poll_background_position();
	    break;
      default:
        break;
    }
    usleep(100000);
    update_display();
    usleep(10000);
  } 

  return 0;
}
