/* main.c
   Peter's Tetris (hmm...)
   By Peter Seidler <seidler@phys.au.dk> */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <curses.h>
#include <term.h>
#include "petris.h"
#include "main.h"
#include "game.h"
#include "highscore.h"

/* Placement and size of different elements on the screen. */
COORDS coords;

/* Initialize struct with coordinates for different elements.
   The function's argument is a string of the form [width]x[height]. */
void init_coords(char *size)
{
	char *endptr;	
	short totw;	/* Total width. */
	
	/* "Constants" */
	WELL_Y = 2;
	STAT_Y = 2;
	STAT_WIDTH = 18;
	
	/* Dependant on constants */
	WELL_WIDTH = strtol(size, &endptr, 10);
	if (endptr == size || *endptr != 'x' || WELL_WIDTH < BLOCK_DOTS) {
		endwin();
		fprintf(stderr, "Petris: Bad [width]x[height] format.\n");
		exit(1);
	}
	
	WELL_HEIGHT = strtol(endptr + 1, 0, 10);
	if (WELL_HEIGHT < BLOCK_DOTS) {
		endwin();
		fprintf(stderr, "Petris: Bad [width]x[height] format.\n");
		exit(1);
	}

	totw = 2 * WELL_WIDTH + 2 + STAT_WIDTH + 2;

	/* This is only a basic check of the terminal size. If the status
	   window is taller than the well, or if some of the other windows
	   used during the game are bigger than the space occupied by
	   the well or status display things will go wrong. */
	if (totw > COLS || WELL_Y + WELL_HEIGHT >= LINES) {
		endwin();
		fprintf(stderr, "Petris: Terminal too small. Aborting.\n");
		exit(1);
	}
	
	/* Place everything in the center of the screen. */
	WELL_X = COLS / 2 - totw / 2 + 1;	/* +1 because of frame. */
	STAT_X = WELL_X + 2 * WELL_WIDTH + 3;
}

void parse_args(int argc, char **argv)
{
	/* If arg. 2 is there assume it's a well size. If not use default size. */
	if (argc >= 2)
		init_coords(*(argv + 1));
	else
		init_coords("10x20");
}

/* Show or hide cursor. */
void cursor_vis(int vis)
{
	char *cmd;
	
	if (vis == 1)
		cmd = tigetstr("cnorm");
	else
		cmd = tigetstr("civis");
	
	if (cmd == (char *)-1) {
		fprintf(stderr, "Warning! Your termianl does not support cursor on / off\n");
		sleep(3);
		return;
	}
		
	putp(cmd);
}

void init_colors()
{
	if (!has_colors()) {
		endwin();
		fprintf(stderr, "Petris: Your terminal doesn't support colors. Aborting \n");
		cursor_vis(1);
		exit(1);
	}
	
	/* We assume this goes ok - we should check, really. */
	start_color();
	
	/* 1 - 16 is for blocks (we just don't have 16 blocks yet) */ 
	init_pair(1, COLOR_BLACK, COLOR_RED);
	init_pair(2, COLOR_BLACK, COLOR_YELLOW);
	init_pair(3, COLOR_BLACK, COLOR_GREEN);
	init_pair(4, COLOR_BLACK, COLOR_CYAN);
	init_pair(5, COLOR_BLACK, COLOR_MAGENTA);
	init_pair(6, COLOR_BLACK, COLOR_BLUE);
	init_pair(7, COLOR_BLACK, COLOR_WHITE);
	
	/* 17 - ? is for other things */
	init_pair(COLOR_POINTS, COLOR_RED, COLOR_BLACK);
	init_pair(COLOR_LINES, COLOR_YELLOW, COLOR_BLACK);
	init_pair(COLOR_LEVEL, COLOR_GREEN, COLOR_BLACK);
	init_pair(COLOR_MSG, COLOR_YELLOW, COLOR_BLACK);
}

/* Redraw static parts of the screen. */
void update_screen()
{
	/* Remember: Each dot in well is two chars wide! */
	
	clear();

	/* Draw frame around well window. */
	attrset(COLOR_PAIR(0));
	move(WELL_Y, WELL_X - 1);
	vline(ACS_VLINE, WELL_HEIGHT);
	move(WELL_Y, WELL_X + (2 * WELL_WIDTH));
	vline(ACS_VLINE, WELL_HEIGHT);
	move(WELL_Y + WELL_HEIGHT, WELL_X - 1);
	addch(ACS_LLCORNER);
	hline(ACS_HLINE, 2 * WELL_WIDTH);
	move(WELL_Y + WELL_HEIGHT, WELL_X + (2 * WELL_WIDTH));
	addch(ACS_LRCORNER);

	/* This is for the status stuff. */
	move(STAT_Y, STAT_X);
	addch(ACS_ULCORNER);
	hline(ACS_HLINE, STAT_WIDTH - 2);
	move(STAT_Y, STAT_X + STAT_WIDTH - 1);
	addch(ACS_URCORNER);
	move(STAT_Y + 1, STAT_X);
	vline(ACS_VLINE, BLOCK_DOTS + 7);
	move(STAT_Y + 1, STAT_X + STAT_WIDTH - 1);
	vline(ACS_VLINE, BLOCK_DOTS + 7);
	move(STAT_Y + BLOCK_DOTS + 8, STAT_X);
	addch(ACS_LLCORNER);
	hline(ACS_HLINE, STAT_WIDTH - 2);
	move(STAT_Y + BLOCK_DOTS + 8, STAT_X + STAT_WIDTH - 1);
	addch(ACS_LRCORNER);
	move(STAT_Y + BLOCK_DOTS + 2, STAT_X);
	addch(ACS_LTEE);
	hline(ACS_HLINE, STAT_WIDTH - 2);
	move(STAT_Y + BLOCK_DOTS + 2, STAT_X + STAT_WIDTH - 1);
	addch(ACS_RTEE);

	refresh();
}

int get_level(int level)
{
	WINDOW *win;
	int ch = 0;
	
	/* Place window in the middle of the screen. */
	win = newwin(12, 16, LINES / 2 - 6, COLS / 2 - 8);
	box(win, 0, 0);
	
	wattrset(win, COLOR_PAIR(COLOR_MSG));
	mvwprintw(win, 2, 2, "Choose level");
	mvwprintw(win, 3, 2, "(0 - %d)", NO_LEVELS - 1);	
	mvwprintw(win, 5, 2, "Up arrow");
	mvwprintw(win, 7, 2, "Down arrow");
	mvwprintw(win, 9, 2, "Space for OK");
	wattrset(win, COLOR_PAIR(COLOR_LEVEL));
	mvwprintw(win, 6, 4, "%d ", level);	
	
	update_screen();
	wrefresh(win);

	nodelay(stdscr, FALSE);
	
	while(ch != ' ' && ch != KEY_ENTER) {
		ch = getch();
		switch (ch) {
		case KEY_UP:
			level = (level + 1 > NO_LEVELS - 1)? 0 : level + 1;
			mvwprintw(win, 6, 4, "%d ", level);
			wrefresh(win);
			break;
		case KEY_DOWN:
			level = (level - 1 < 0)? NO_LEVELS - 1: level - 1;
			mvwprintw(win, 6, 4, "%d ", level);
			wrefresh(win);
			break;
		}
	}
	
	nodelay(stdscr, TRUE);
	delwin(win);
	   
	return level;
}

/* If use_hs != 0 check for new highscore. */
int show_score(POINTS points, int use_hs)
{
	WINDOW *win;
	int ranking;	/* Highscore ranking. */
	int ch;
	int ret = 1;

	/* Place window in the middle of the screen. */
	win = newwin(20, 28, LINES / 2 - 10, COLS / 2 - 14);
	box(win, 0, 0);
	
	wattrset(win, COLOR_PAIR(COLOR_MSG) | A_BOLD);
	mvwprintw(win, 2, 6, "*** GAME OVER ***");
	wattrset(win, COLOR_PAIR(COLOR_MSG));
	mvwprintw(win, 4, 2, "You scored:");
	wattrset(win, COLOR_PAIR(COLOR_POINTS));
	mvwprintw(win, 6, 5, "Points: %d", points.points);	
	wattrset(win, COLOR_PAIR(COLOR_LINES));
	mvwprintw(win, 7, 5, "Lines:  %d", points.lines);
	wattrset(win, COLOR_PAIR(COLOR_LEVEL));
	mvwprintw(win, 8, 5, "Level:  %d", points.level);
	
	wattrset(win, COLOR_PAIR(COLOR_POINTS) | A_BOLD);
	if (use_hs) {
		ranking = check_highscores(points.points);
		if (ranking) {
			mvwprintw(win, 10, 2, "Congratulations!");
			wattrset(win, COLOR_PAIR(COLOR_MSG));
			mvwprintw(win, 12, 5, "You're on the high-");
			mvwprintw(win, 13, 5, "score list as no. %d", ranking);
		} else
			mvwprintw(win, 11, 5, "Practice some more!");
	} else {
		mvwprintw(win, 10, 2, "Highscore disabled!");
	}
	
	wattrset(win, COLOR_PAIR(COLOR_MSG));
	mvwprintw(win, 15, 2, "Press q to quit, h to");
	mvwprintw(win, 16, 2, "view highscores or any");
	mvwprintw(win, 17, 2, "other key to play again.");
	
	update_screen();
	wrefresh(win);
	
	if (ch != ERR)
		while (getch() != ERR);

	nodelay(stdscr, FALSE);

	ch = getch();
	if (ch == 'q' || ch == 'Q')
		ret = 0;
	else if (ch == 'h' || ch == 'H')
		ret = view_highscores();
		
	nodelay(stdscr, TRUE);
	delwin(win);
		
	return ret;
}

/* Displays a message in the top left corner for a couple
 * of seconds. Used for displaying "global" messages like
 * errors */
void message(char *msg)
{
	attrset(COLOR_PAIR(COLOR_MSG) | A_BLINK);
	mvprintw(0,0, msg);
	refresh();
	sleep(3);
}

int main(int argc, char **argv)
{
	int play;
	int level = 0;
	POINTS points;
	int use_highscore = 1;
	
	/* Initialize screen */
	if (0 == initscr()) {
		fprintf(stderr, "Petris: Error initializing ncurses.\n");
		exit(1);
	}

	parse_args(argc, argv);
	
	/* We only use highscore list if the size of the well is
	   standard 10x20. */
	if (WELL_WIDTH != 10 || WELL_HEIGHT != 20)
		use_highscore = 0;
	
	/* Turn off cursor. */
	cursor_vis(0);

	/* Setup keyboard. We'd like to get each and every character, but
	   not to display them on the terminal. */
	keypad(stdscr, TRUE);
	nodelay(stdscr, TRUE);
	noecho();
	cbreak();
	
	init_colors();

	/* Set random seed. */
	srandom(time( (time_t *) 0 ));

	/* This is where the game begins! */
	while (play) {
		level = get_level(level);
		points = play_game(level);
		play = show_score(points, use_highscore);
	}

	cursor_vis(1);

	endwin();	

	exit(0);
}
