/*

greenbar, Version 0.1

Copyright (c) 2013 All Rights Reserved, Egan Ford (egan@sense.net)

THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

License:
	*  Do what you like, remember to credit all sources when using.

Description:

Features:

Compile:
	OS/X:
		gcc -O3 -o greenbar -framework Cocoa greenbar.c playsound.m 

Notes:

Not yet done:

Thinking about:

Bugs:
	*  Probably

*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define SND
#define COL 132

const char *usagetext="\n\
usage:  greenbar [-1] [-b] [-u] [-s bps] [-l usec] [-s usec]\n\
\n\
";

const char *civis="\033[?25l";
const char *setwidth="\033[8;;146t";
const char *white="\033[47m";
const char *cyan="\033[46m";
const char *green="\033[42m";
//const char *setgreen="\033]4;2;#B6C8B3\007";
//const char *setwhite="\033]4;7;#DCDCCD\007";
const char *setgreen="\033]4;2;#D6E8D3\007";
const char *setwhite="\033]4;7;#ECECDD\007";
const char *hole="| O |                                                                                                                                        | O |";
const char *hole2="\033(0x ` x                                                                                                                                        x ` x\033(B";
const char *line="|   |                                                                                                                                        |   |";

static char run = 1;
static char stalled = 0;
static char not_stalled = 1;
static unsigned int dir = 1;
char oneliner = 0;
int wheel_us = 50000 / 47;
int carriage_us = 250000 / COL;

void inthandler() {
	run = 0;
}

static void alarm_handler(int x) {
	if (not_stalled)
		return;
    stalled = 1;
}

void printline(unsigned char *line, int len, int col, int speed, int bidir);
void newline(int col);
int getch();
int playsound(char *soundfile);

int main(int argc, char **argv)
{
	int i;
	unsigned char c, line[COL];
	unsigned int col = 0, buf = 0;
	int ch, speed = 1, bidir = 0, upper = 0;
	struct winsize w;
	struct sigaction act;

	opterr = 1;
	while ((ch = getopt(argc, argv, "l:c:u1bs:")) != -1)
		switch (ch) {
			case 'u':
				upper = 1;
				break;
			case '1':
				oneliner = 1;
				break;
			case 'b':
				bidir = 1;
				break;
			case 's':
				speed = atoi(optarg);
				break;
			case 'l':
				wheel_us = atoi(optarg) / 47;
				break;
			case 'c':
				carriage_us = atoi(optarg) / COL;
				break;
			default:
				break;
		}

	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
	signal(SIGINT, inthandler);

	printf("\033[%d;0H", w.ws_row - 1);	/* move cursor to last row */
	printf("%s", civis);			/* no cursor */
	printf("%s", setwidth);			/* set xterm to COLUMNS=146; */
	printf("%s", setgreen);			/* green is the new green */
	printf("%s", setwhite);			/* white is the new white */

	newline(col);

	setvbuf(stdout, NULL, _IONBF, 0);
	act.sa_handler = &alarm_handler;
	sigaction(SIGALRM, &act, NULL);

	while (run) {
		ualarm(100000, 0);
		not_stalled = 0;

		if (stalled && buf) {		/* flush buffer, and continue */
			not_stalled = 1;
			printline(line, col-buf, col, speed, 0);
			buf = 0;
			stalled = 0;
			dir = 1;
		}

		c = getch();

		if (feof(stdin)) {
			if (buf)
				printline(line, col-buf, col, speed, bidir);
			break;
		}

		if (c > 126) {			/* is 8-bit */
			usleep(1000);		/* be nice */
			continue;
		}

		if (c == '\r')
			continue;

		if (upper)
			c = toupper(c);

		if (c == '\t') {
			i = 8 - (col % 8);
			col += i;
			while (i--)
				line[buf++] = ' ';
		} else {
			line[buf++] = c;
			col++;
		}

		if (col == COL) {
			not_stalled = 1;
			printline(line, col-buf, col, speed, bidir);
			buf = col = 0;
			continue;
		}
/*
		if (c == '\r') {
			not_stalled = 1;
			printline(line, col-buf, col, speed, bidir);
			buf = col = 0;
		}
*/
		if (c == '\n') {
			not_stalled = 1;
			printline(line, col-buf, col, speed, bidir);
			buf = col = 0;
		}
	}

	printf("\n\n\033[!p\033[?3;4l\033[4l\033>");	/* clean up xterm */

	return 0;
}

void printline(unsigned char *line, int len, int col, int speed, int bidir)
{
	int i;
	char c, d;
	static unsigned int charcount = 0;
	static unsigned int lastcol = 0;
	static char lastc = '!';

	dir = (dir == 0);
	if (bidir && dir) {
		if (abs(lastcol - col) < lastcol) {
			if (line[col-1] == '\r' || line[col-1] == '\n')
				/* for now forget working with CR */
				col--;										/* ignore LF and CR */
			printf("\033[%dC", col-1); /* move cursor forward */
			for (i = col-1; i >= 0; i--) {
				c = line[i];
				write(1, &c, 1);	/* print it */
				printf("\010\010");	/* backspace twice */
#ifdef SND
				if (c == ' ') {
					usleep(carriage_us / speed);
					playsound("conf/dwchar0.aiff");
				} else {
					if (charcount++ % speed == 0) {
						d = abs(c-lastc);
						if (d > 47)
							d = 94 - d;
						usleep(wheel_us * d >
							carriage_us ?
							wheel_us * d :
							carriage_us);
						playsound("conf/dwchar0.aiff");
						usleep(19500);
						//playsound("conf/dwchar1.aiff");
						//usleep(40000);
						lastc = c;
					}
				}
#endif
			}
			newline(0);
			lastcol = col;
			return;
		}
		else {
			#ifdef SND
			if (col > 0 && col <= COL * .2) {
				playsound("conf/dwcr20.aiff");
				usleep(90000);
			}
			else if (col > COL * .2 && col <= COL * .4) {
				playsound("conf/dwcr40.aiff");
				usleep(200000);
			}
			else if (col > COL * .4 && col <= COL * .6) {
				playsound("conf/dwcr60.aiff");
				usleep(290000);
			}
			else if (col > COL * .6 && col <= COL * .8) {
				playsound("conf/dwcr80.aiff");
				usleep(390000);
			}
			else {
				playsound("conf/dwcr.aiff");
				usleep(490000);
			}
			#endif
			dir = 0;	/* forward */
		}
	}

	for (i = 0; i < col-len; i++) {
		c = line[i];

		/* control codes */

		if (c == '\r') {
			#ifdef SND
			if (col > 0 && col <= COL * .2) {
				playsound("conf/dwcr20.aiff");
				usleep(90000);
			}
			else if (col > COL * .2 && col <= COL * .4) {
				playsound("conf/dwcr40.aiff");
				usleep(200000);
			}
			else if (col > COL * .4 && col <= COL * .6) {
				playsound("conf/dwcr60.aiff");
				usleep(290000);
			}
			else if (col > COL * .6 && col <= COL * .8) {
				playsound("conf/dwcr80.aiff");
				usleep(390000);
			}
			else {
				playsound("conf/dwcr.aiff");
				usleep(490000);
			}
			#endif
			printf("\r\033[7C");	/* \r and move cursor forward */
			continue;
		}

		if (c == '\n') {
			if (bidir)
				newline(0);
			else
				newline(col);
			continue;
		}

		if (c != ' ')
			write(1, &c, 1);	/* print it */
		else
			printf("\033[1C");

#ifdef SND
		if (c == ' ') {
			usleep(carriage_us / speed);
			playsound("conf/dwchar0.aiff");
		}
		else {
			if (charcount++ % speed == 0) {
				d = abs(c-lastc);
				if (d > 47)
					d = 94 - d;
				usleep(wheel_us * d > carriage_us ?
					wheel_us * d : carriage_us);
				playsound("conf/dwchar0.aiff");
				usleep(19500);
				//playsound("conf/dwchar1.aiff");
				//usleep(40000);
				lastc = c;
			}
		}
#endif
	}
	if (col == COL) {
		if (bidir)
			newline(0);
		else
			newline(col);
	}
	lastcol = col;
}

void newline(int col)
{
	static unsigned char color=0, lines=0;

#ifdef SND
	if (col) {
		if (col > 0 && col <= COL * .2) {
			playsound("conf/dwcr20.aiff");
			usleep(90000);
		}
		else if (col > COL * .2 && col <= COL * .4) {
			playsound("conf/dwcr40.aiff");
			usleep(200000);
		}
		else if (col > COL * .4 && col <= COL * .6) {
			playsound("conf/dwcr60.aiff");
			usleep(290000);
		}
		else if (col > COL * .6 && col <= COL * .8) {
			playsound("conf/dwcr80.aiff");
			usleep(390000);
		}
		else {
			playsound("conf/dwcr.aiff");
			usleep(490000);
		}
	}
	playsound("conf/dwnl.aiff");
#endif

	printf("\r\n");

	if(lines == 0 || oneliner) {
		color = (color == 0);
		if (color)
			printf("%s",green);	/* print green */
		else
			printf("%s",white);	/* print white */
	}

	if (lines == 1)
		printf("%s\r",hole2);		/* print hole line */
	else
		printf("%s\r",line);		/* print reg line */
	printf("\033[7C");			/* move cursor forward */

	lines++;
	if (lines == 3)
		lines = 0;

#ifdef SND
	usleep(100000);
#endif
}

int getch() {
	struct termios oldt, newt;
	int ch;

	tcgetattr(STDIN_FILENO, &oldt);
	newt = oldt;
	newt.c_lflag &= ~(ICANON | ECHO);
	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
	ch = getchar();
	tcsetattr(STDIN_FILENO, TCSANOW, &oldt);

	return ch;
}
