/*
 * FragRank 0.7 by Phil "Crazy" Spencer Released under the GPL GNU Public License
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LOGPATH "/home/spencep/.quakeforge/qw/frag_%04d.log" 
//#define LOGPATH "/home/spencep/quake/philsqw/frag_%d.log"
/* If using quakeforge change the filename to frag_%04d.log 
 * Regular quake world server is frag_%d.log
 */
#define TITLE "FragRank by CrazySpence"
#define HEADLINE "Philtopia.com Killers"
#define HCOLOR "#840d05"
#define NUMCOLOR "#840d05"
#define NAMECOLOR "#2911af"
#define FRAGCOLOR "#0a5b07"
#define FRAGGEDCOLOR "#0a5b07"
#define SUICIDECOLOR "#0a5b07"
#define SCORECOLOR "#2911af"
#define FCOLOR "#840d05"

struct player {
	char *name;
	double frags;
	double fragged;
	double dumbass; //suicide variable
	double score; // frags divided by fragged Score
	struct player *next; //pointer to next allocated struct
};

char *charreplace(char *string, char match, char *change);//character replacement function
int isplayer(char *search); //check if player exists
void displayplayers(int max); //format html and display output
void *findplayernum(char *name); //search for existing player
void *sortplayers(); //sort players for display by score

struct player *head,*prev, *current;
int numplayers = 0;
double totalfrags = 0;
double totalfragged = 0;
double totalscore = 0;
double totaldumbass = 0;

int main() {
	FILE *file;
	char buf[512];
	char backup[512];
	char *s;
	char killer[255];
	char killed[255];
	char path[255];
	int filenum = 0;
	int i;
    
    sprintf(path,LOGPATH,filenum);
	while ((file = fopen(path,"r")) != NULL) {
		/* Why is this a file opening while loop? let me tell you!
		 * Quake world will not log all frags to just one file, if you
		 * restart qwsv it logs the frags to a new file but fortunatley
		 * they are all frag_#.log so we just loop through until a file
		 * is not there, not the nicest way to do this but it does the job
		 * quickly actually it might be the nicest...I never looked for
		 * another way but there probably is a more sane way *shrugs*
		 */
		while (fgets(buf,512,file) != NULL) {
            strcpy(backup,charreplace(charreplace(buf,'<',"&lt;"),'>',"&gt;")); //replace < and >'s with &lt; and &gt; to prevent tom's from executing code within fragstats
			
            s = strtok(backup,"\\");
			strcpy(killer,s);
			if (numplayers != 0) {
				if ((isplayer(s)) != 1) {
					//new player found in killer, add to linked list
					current = (struct player *) malloc(sizeof(struct player));
					prev->next = current;
					current->next = NULL;
					current->name = strdup(s);
					prev = current;
                    numplayers++;
       			}
			} else {
				//add first player
				current = (struct player *) malloc(sizeof(struct player));
				head = current;
				current->next = NULL;
				current->name = strdup(s);
				numplayers++;
   	            prev = current;
       	    }
			s = strtok(NULL,"\\");
			strcpy(killed,s);
            if ((isplayer(s)) != 1) {
				//new player found in killed field, add to linked list
				current = (struct player *) malloc(sizeof(struct player));
				prev->next = current;
				current->next = NULL;
				current->name = strdup(s);
				numplayers++;
   	            prev = current;
       		}
			if (strcmp(killer,killed) == 0) {
	            current = (struct player *) findplayernum(killer);
				current->frags--;
				current->dumbass++;
				totaldumbass++;
				totalfrags--;
			} else {
	            current = (struct player *) findplayernum(killer);
				current->frags++; //increase killers frags
				current = (struct player *) findplayernum(killed);
				current->fragged++; //increase killed fragged
				totalfrags++;
				totalfragged++;
			}
		}
	    fclose(file);
		filenum++;
		sprintf(path,LOGPATH,filenum); //setup path for next file
	}
	current = head;
    while (current != NULL) {//add the scores
		if (current->fragged == 0)
			current->score = current->frags; //quick fix to avoid divide by 0 error
		else
			current->score = current->frags / current->fragged;
	    current = current->next;
	}
	if (totalfragged == 0)
		totalscore = totalfrags; //also to avoid divide by zero error
	else
		totalscore = totalfrags / totalfragged;
	displayplayers(numplayers);
	return 0;
}

int isplayer(char *search) {
	current = head;
	while (current != NULL) {
		if (strcasecmp(search,current->name) == 0) {
			return 1;
		}
		current = current->next;
	}
	return 0;
}

void displayplayers(int max) {
	/*
	 * This is the fancy HTML part of the code stfu erik!
	*/
	int i;
        struct player *players;
    
	players = sortplayers();
 	printf("Content-type: text/html\n\n");
	printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
	printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
	printf("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n<title>%s</title>\n</head>\n<body style=\"background-color:black;color:white;margin-left:auto;margin-right:auto;width:20em\">\n",TITLE);
	printf("<h2 style=\"text-align:center;\">%s</h2>\n",HEADLINE);
	printf("<table>\n");
	printf("<tr style=\"background-color:%s\">\n",HCOLOR);
	printf("<th>#</th>\n");
	printf("<th>Name</th>\n");
	printf("<th>Frags</th>\n");
	printf("<th>Fragged</th>\n");
	printf("<th>Suicide</th>\n");
	printf("<th>Score</th>\n");
	printf("</tr>\n");
	for (i = 0 ; i < max ; i++) {
		printf("<tr>\n");
		printf("<td style=\"background-color:%s;\">%d</td>\n",NUMCOLOR,i+1);
		printf("<td style=\"background-color:%s;\">%s</td>\n",NAMECOLOR,players[i].name);
		printf("<td style=\"background-color:%s;\">%.0f</td>\n",FRAGCOLOR,players[i].frags);
		printf("<td style=\"background-color:%s;\">%.0f</td>\n",FRAGGEDCOLOR,players[i].fragged);
		printf("<td style=\"background-color:%s;\">%.0f</td>\n",SUICIDECOLOR,players[i].dumbass);
		printf("<td style=\"background-color:%s;\">%.1f</td>\n",SCORECOLOR,players[i].score);
		printf("</tr>\n");
	}
	printf("<tr style=\"background-color:%s;\">\n",FCOLOR);
        printf("<td></td>\n");
	printf("<td>Total:</td>\n");
	printf("<td>%.0f</td>\n",totalfrags);
	printf("<td>%.0f</td>\n",totalfragged);
	printf("<td>%.0f</td>\n",totaldumbass);
	printf("<td>%.1f</td>\n",totalscore);
	printf("</tr>\n");
        printf("</table>\n");
        printf("<div style=\"text-align:center;\">\n<p>\n<a href=\"http://validator.w3.org/check?uri=referer\">\n<img style=\"border: 0px;\" src=\"http://www.w3.org/Icons/valid-xhtml10-blue\" alt=\"Valid XHTML 1.0 Strict\" height=\"31\" width=\"88\"/>\n</a>\n</p>\n</div>\n");
	printf("</body>\n</html>\n");
	free(players); //free the sorted players struct from memory
}

void *sortplayers() {
	int top;
	int big;
	int i;
    struct player temp, *players;
    
    players = (struct player *) malloc(numplayers * sizeof(struct player));
	current = head;
	i = 0;
	while (current != NULL) {
		players[i] = *current;
		current = current->next;
		i++;	
	}
	 
	//selection sort to sort players by scores
	for (top = (numplayers - 1) ; top >= 1 ; top--) {
		big = 0;
		for (i = 1 ; i <= top ; i++) {
			if (players[i].score < players[big].score)
				big = i;
			temp = players[top];
			players[top] = players[big];
			players[big] = temp;
		}
	}
	return players;
}

void *findplayernum(char *name) {
	int i = 0;
	current = head;
    while (current != NULL) {
		if (strcasecmp(current->name,name) == 0)
			return current;
		current = current->next;
	}
	return NULL; //this wont ever happen unless I break the code heh

}

char *charreplace(char *string, char match, char *change) {
    /* I made this function so i could replace < and >'s with &lt; and &gt;
     * however I do not care for the way i implemented it but it does work for
     * what is needed
     */
     int i,k,l;
     int count = 0;
     char *replaced;
    
      i = 0;
      while (string[i] != '\0') {
           if (string[i] == match)
           count++; //count occrences of match             
           i++;
      }
      if (count == 0)
           return string; //no match, return original string
      replaced = (char *) malloc(strlen(string) + (count * strlen(change))); //prepare new string
      i = 0;
      l = 0;
      while (string[i] != '\0') {
           if (string[i] == match) {
               k = 0;
               while (change[k] != '\0') {
                    replaced[l] = change[k];
                    k++;
                    l++;
                }     
           }  else {
                replaced[l] = string[i];          
                     l++;
           }
           i++;
      }
            return replaced; //send back modified string
}

