/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <sys/mount.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/klog.h>
#include <fcntl.h>
#include <errno.h>
#include <stdarg.h>

#define tryexeclp(file, arg, ...) ( { \
	int pid; \
	\
	if ( (pid = fork()) == 0 ) { \
		execlp(file, arg, __VA_ARGS__); \
		perror(file); \
		exit(1); \
	} \
	\
	trywait(pid); \
} )

#define PICKUP_TYPES	0
#define RUNMODE		1
#define DOGNAME		2
#define CATNAME		3
#define HORSENAME	4
#define PETTYPE		5
#define BOULDER		6
#define FRUIT		7
#define MENUSTYLE	8
#define MSG_WINDOW	9
#define SUPPRESS_ALERT	10

void trygets(char *s, int len){ /*{{{*/
	s[0]=0;
	if (fgets(s, len, stdin) == NULL) {
		if (ferror(stdin)) perror("fgets");
		else printf("EOF\n");

		sleep(1);
		execl("/linuxrc", "/linuxrc", NULL);
		printf("\nCan't start /linuxrc!! Life sucks.\n\n");
		fflush(stdout);
		exit(0);
	}
	/* guarantee a 0 termination and remove the trailing newline */
	s[len-1]=0;
	if (strlen(s) > 0) s[strlen(s)-1]=0;
}/*}}}*/
void trywait(pid){/*{{{*/
	if (pid < 0) perror("fork");
	else waitpid(pid, NULL, 0);
}/*}}}*/
void edit_nethackrc(){/*{{{*/
	char *booloptions[39] = {
		"autodig", "autopickup", "autoquiver", "checkpoint",
		"cmdassist", "color", "confirm", "DECgraphics", "eight_bit_tty",
		"extmenu", "fixinv", "help", "hilite_pet", "IBMgraphics",
		"ignintr", "lit_corridor", "lootabc", "mail", "menucolors",
		"perm_invent", "null", "prayconfirm", "pushweapon",
		"rest_on_space", "showexp", "showrace", "showscore", "silent",
		"sortpack", "sound", "sparkle", "standout", "time",
		"timed_delay", "tombstone", "toptenwin", "travel",
		"use_inverse", "verbose"
	};
	char *moreoptions[11] = {
		"pickup_types", "runmode", "dogname", "catname",
		"horsename", "pettype", "boulder", "fruit", "menustyle",
		"msg_window", "suppress_alert"
	};
	typedef struct s_menucolors {
		char *regex;
		char *color;
		void *next;
	} s_menucolors;
	s_menucolors *menucolors;
	char *values[50];
	char *buf, *line;
	int x, nethackrc;
	int yesno;
	int exit_edit;
	char text[100];
	int input;

	menucolors=malloc(sizeof(s_menucolors));
	menucolors->regex = malloc(80);
	menucolors->color = malloc(80);
	menucolors->next = 0x0;
	memset(menucolors->regex, 0x0, 80);
	memset(menucolors->color, 0x0, 80);
	exit_edit=0;
	for (x=0;x<50;x++){
		values[x] = malloc(50);
		memset(values[x], 0x0, 50);
	}
	nethackrc = open("/nethackrc", O_RDONLY);
	line=malloc(128);
	while (nethackrc != -1){
		memset(line, 0x0, 128);
		buf=line-1;
		do {
			buf++;
			if (read(nethackrc, buf, 1) == 0){
				close(nethackrc);
				nethackrc=-1;
				memset(buf, '\n', 1);
			}
		} while (strncmp(buf, "\n", 1) != 0);
		memset(buf, 0x0, 1);
		buf=strstr(line, "#");
		if (buf != NULL)
			memset(buf, 0x0, 1);
		if (strlen(line)<6) continue;
		if (strstr(line, "OPTIONS") == line){
			buf=strstr(line, "=");
			buf++;
			if (strstr(buf, "no") == buf){
				yesno=1;
				buf+=2;
			} else
				yesno=0;
			for (x=0; x<39; x++){
				if (strncmp(booloptions[x], buf, strlen(booloptions[x]))==0){
					sprintf(values[x], "%s", (yesno == 1 ? "no" : ""));
					x=40;
				}
			}
			for (x=0;x<11;x++){
				if (strncmp(moreoptions[x], buf, strlen(moreoptions[x]))==0){
					buf=strstr(buf, ":");
					buf++;
					sprintf(values[39+x], "%s", buf);
					x=11;
				}
			}
		} else if (strstr(line, "MENUCOLOR") == line) {
			s_menucolors *mcbuf;

			if (strlen(menucolors->regex) == 0)
				mcbuf = menucolors;
			else {
				mcbuf = menucolors;
				while (mcbuf->next)
					mcbuf=mcbuf->next;
				mcbuf->next=malloc(sizeof(s_menucolors));
				mcbuf=mcbuf->next;
				mcbuf->regex = malloc(80);
				mcbuf->color = malloc(80);
				mcbuf->next = 0x0;
				memset(mcbuf->regex, 0x0, 80);
				memset(mcbuf->color, 0x0, 80);
			}
			buf=strstr(line, "=\"");
			buf+=2;
			sprintf(mcbuf->regex, "%s", buf);
			memset(strstr(mcbuf->regex, "\""), 0x0, 1);
			buf=strstr(buf, "\"=");
			buf+=2;
			sprintf(mcbuf->color, "%s", buf);
		} else {
			printf("Mangled line in nethackrc:\n%s\n", line);
		}
	}
	free(line);
	
	while(exit_edit==0){
		for (x=0;x<15;x++){
			int y, z;
			char *output;

			output=malloc(80);
			for (y=x*4;y<x*4+4 && y<50;y++){
				memset(output, 0x0, 80);
				if (y<39)
					sprintf(output, "%2i. %s%s", y+1, values[y], booloptions[y]);
				else
					sprintf(output, "%2i. %s:%s", y+1, moreoptions[y-39], values[y]);
				printf(output);
				for(z=strlen(output); z<20; z++)
					printf(" ");
			}
			printf("\n");
			free(output);
		}

		printf("Which value do you want to change (0=done)? ");
		fflush(stdout);
		trygets(text, 80);
		input=atoi(text);
		input--;
		if (input == -1){
			exit_edit = 1;
		} else if (input > -1 && input < 39){
			if (strlen(values[input]) == 2)
				memset(values[input], 0x0, 50);
			else
				strcat(values[input], "no");
		} else if (input > 38 && input < 50){
			switch (input-39){
				case PICKUP_TYPES:
					printf("Please enter a string containing one or more of these characters:\n");
					printf("$\")[%?+!=/(*`0_\n");
					break;
				case RUNMODE:
					printf("Please enter crawl, walk, run or teleport\n");
					break;
				case DOGNAME:
				case CATNAME:
				case HORSENAME:
					printf("Please enter the name of your %s:\n", (input-39 == DOGNAME ? "dog" : (input-39 == CATNAME ? "cat" : "horse")));
					break;
				case PETTYPE:
					printf("Please enter which pet you want to start with\n");
					printf("if your class has multiple pets or none for no pet:\n");
					break;
				case BOULDER:
					printf("Please enter the character you want to represent boulders\n");
					break;
				case FRUIT:
					printf("What do you want to call fruits:\n");
					break;
				case MENUSTYLE:
					printf("Please enter your desired menustyle:\n");
					printf("traditional, combination, partial or full\n");
					break;
				case MSG_WINDOW:
					printf("Please enter your desired message history style:\n");
					printf("single, combination, full or reversed");
					break;
				case SUPPRESS_ALERT:
					printf("Please enter the versionnumber whose error-messages you want\n");
					printf("to deactivate. For example 3.3.1\n");
					break;
			}
			printf("> ");
			fflush(stdout);
			trygets(values[input], 50);
		} else
			printf("Unknown option: %s\n", text);
	}
	exit_edit=0;
	while (exit_edit==0){
		s_menucolors *mcbuf;
		
		x=0;
		mcbuf = menucolors;
		while (mcbuf->next)
			mcbuf=mcbuf->next;
		if (strlen(mcbuf->regex) != 0){
			mcbuf->next=malloc(sizeof(s_menucolors));
			mcbuf=mcbuf->next;
			mcbuf->regex=malloc(80);
			mcbuf->color=malloc(80);
			memset(mcbuf->regex, 0x0, 80);
			memset(mcbuf->color, 0x0, 80);
		}
		mcbuf = menucolors;
		while (mcbuf){
			x++;
			if (strlen(mcbuf->regex) == 0){
				printf("%2i. Add new line\n", x);
			} else {
				printf("%2i. %s = %s\n", x, mcbuf->regex, mcbuf->color);
			}
			mcbuf=mcbuf->next;
		}
		printf("Please enter the number you want to change or 0 when done\n> ");
		fflush(stdout);
		trygets(text, 80);
		input=atoi(text);
		if (input > x || input < 0){
			printf("ERROR! %i is out of range (1-%i)\n", input, x);
			continue;
		}
		if (input == 0){
			exit_edit=1;
			continue;
		}
		mcbuf=menucolors;
		for (x=1; x<input; x++)
			mcbuf=mcbuf->next;
		printf("Please enter the text you want to color.\nExample: cursed, holy, weapon in hand\nOld value: %s\nLeave blank to not change the value.\nEnter 'none' to delete this entry\n> ", mcbuf->regex);
		fflush(stdout);
		trygets(text, 80);
		if (strlen(text) > 0){
			if (strstr(text, "none") == text && strlen(text) == 4){
				s_menucolors *mcbuf2;
				mcbuf2=menucolors;
				for (x=2; x<input; x++)
					mcbuf2=mcbuf2->next;
				mcbuf2->next=mcbuf->next;
				free(mcbuf->regex);
				free(mcbuf->color);
				free(mcbuf);
				continue;
			}
		}
		sprintf(mcbuf->regex, "%s", text);
		printf("Please enter the color combination you want to use.\nExample:red, blue, green&bold, yellow&underline\nOld value: %s\nLeave blank to not change the value.\n> ", mcbuf->color);
		fflush(stdout);
		trygets(text, 80);
		if (strlen(text) > 0)
			sprintf(mcbuf->color, "%s", text);
	}
	nethackrc=open("/nethackrc", O_WRONLY|O_TRUNC);
	write(nethackrc, "# Example nethackrc file\n", 25);
	write(nethackrc, "# Please note that for Nethack Linux to be able to parse this file, there must\n", 79);
	write(nethackrc, "# be one and only one option per line.\n", 39);
	write(nethackrc, "# Comments begin with a hash\n", 29);
	for (x=0;x<50;x++){
		if (x < 39)
			sprintf(text, "OPTIONS=%s%s\n", (strlen(values[x]) == 2 ? "no" : ""), booloptions[x]);
		else
			sprintf(text, "OPTIONS=%s:%s\n", moreoptions[x-39], values[x]);
		write(nethackrc, text, strlen(text));
		free(values[x]);
	}
	{
		s_menucolors *mcbuf;
		while (menucolors){
			mcbuf=menucolors->next;
			if (strlen(menucolors->regex) != 0){
				sprintf(text, "MENUCOLOR=\"%s\"=%s\n", menucolors->regex, menucolors->color);
				write(nethackrc, text, strlen(text));
			}
			free(menucolors->regex);
			free(menucolors->color);
			menucolors=mcbuf;
		}
	}
	close(nethackrc);
}/*}}}*/
int main(){/*{{{*/
	int exit_linuxrc=0;
	char text[100];
	int input;

	if ( mount("none", "/usr", "ramfs", 0, NULL) && errno != EBUSY )
		perror("Can't mount ramfs on /usr");

	chdir("/usr");
	printf("uncompressing nethack\n");
	tryexeclp("/tar", "tar", "--use-compress-program=/bzip2", "-xf", "/nethack.tar.bz2", NULL);

	while (exit_linuxrc == 0){
		printf("\n\
     1. Play nethack\n\
     2. Edit nethackrc\n\
     3. Show highscore\n\
     \n\
     0. Exit\n\
\n\
What do you want to do? ");
		fflush(stdout);

		trygets(text, 100);
		input=atoi(text);
		
		switch (input) {
			case 0:
			  exit_linuxrc=1;
			  break;

			case 1:
			  tryexeclp("/nethack", "nethack", NULL);
			  break;

			case 2:
			  edit_nethackrc();
			  break;

			case 3:
			  tryexeclp("/nethack", "nethack", "-s", NULL);
			  break;

			default:
			  printf("No such option present!");
		}
	}

	sync();
	printf("The dungeon walls collapse.\n");
	fflush(stdout);
	while(1)
		sleep(1);

	return 0;
}/*}}}*/
