#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "jobs.h"
#include "yanling.h"
#include "util.h"

#ifdef UNIX
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#else
#include <windows.h>
void wperror();
typedef HANDLE *pid_t;
#endif

int doexit(int argc, char* const  argv[]){
  removeJobs();
  if (joblist) {/* there are still jobs forked by this shell */
    printf("jobs in the back ground, kill them before exit.\n");
    jobs(0, NULL);
    return 1;
  }
  else exit(0);
}

#define NUMOFSIGNALS 63

const char* signals[NUMOFSIGNALS] = {
  "SIGHUP",  "SIGINT",  "SIGQUIT",  "SIGILL",
  "SIGTRAP",  "SIGABRT",  "SIGBUS",  "SIGFPE",
  "SIGKILL",  "SIGUSR1",	 "SIGSEGV",   "SIGUSR2",
  "SIGPIPE",	"SIGALRM",	"SIGTERM",	"", "SIGCHLD",
  "SIGCONT",	"SIGSTOP",	"SIGTSTP",	"SIGTTIN",
  "SIGTTOU",	"SIGURG",	"SIGXCPU",	"SIGXFSZ",
  "SIGVTALRM",	"SIGPROF",	"SIGWINCH",	"SIGIO",
  "SIGPWR",	"SIGSYS",	"SIGRTMIN",	"SIGRTMIN+1",
  "SIGRTMIN+2",	"SIGRTMIN+3",	"SIGRTMIN+4",	"SIGRTMIN+5",
  "SIGRTMIN+6",	"SIGRTMIN+7",	"SIGRTMIN+8",	"SIGRTMIN+9",
  "SIGRTMIN+10",	"SIGRTMIN+11",	"SIGRTMIN+12",	"SIGRTMIN+13",
  "SIGRTMIN+14",	"SIGRTMIN+15",	"SIGRTMAX-15",	"SIGRTMAX-14",
  "SIGRTMAX-13",	"SIGRTMAX-12",	"SIGRTMAX-11",	"SIGRTMAX-10",
  "SIGRTMAX-9",	"SIGRTMAX-8",	"SIGRTMAX-7",	"SIGRTMAX-6",
  "SIGRTMAX-5",	"SIGRTMAX-4",	"SIGRTMAX-3",	"SIGRTMAX-2",
  "SIGRTMAX-1",	"SIGRTMAX"
};


pid_t pidFromJobid(int jobid){
  struct job *start=joblist;
  while ((start != NULL) && (start->jobid != jobid)){
    start = start->nextjob;
  }
  if (start) {/* found the job with jobid */
#ifdef UNIX
    return start->pid;
#else
	return &(start->pid.hProcess);
#endif
  }
  else{ /* did not find such a job */
    return 0;
  }
}

#ifdef UNIX
int updateStatus(int pid, int state){
  struct job *start=joblist;
  while ((start != NULL) && (start->pid != pid)){
    start = start->nextjob;
  }
  if (start) {/* found the job with pid */
    start->status = state;
    return 0;
  }
  else{ /* did not find such a job */
    return 0;
  }
}

struct job * jobInfoFromPid(int pid){
  struct job *start=joblist;
  while ((start != NULL) && (start->pid != pid)){
    start = start->nextjob;
  }
  return start;
}
#else
pid_t handleFromPid(int pid){
  struct job *start=joblist;
  while ((start != NULL) && (start->pid.dwProcessId != pid)){
    start = start->nextjob;
  }
  if (start) {/* found the job with jobid */
	return &(start->pid.hProcess);
  }
  else{ /* did not find such a job */
    return 0;
  }
}
#endif

pid_t pidFromName(const char * command){
  int count = 0;
  struct job * matched = NULL;
  struct job * start = joblist;
  int cmdsize = strlen(command);
  while (start != NULL) {
    if (strncmp(command, start->command, cmdsize)){
    }
    else{
      count++;
      matched = start;
    }
    start = start->nextjob;
  }
  if (count > 1){ /* ambiguous matching */
	return 0;
  }
  else if (count == 1){ /* there is exactly one match */
#ifdef UNIX
    return matched -> pid;
#else
	return &(matched -> pid.hProcess);
#endif
  }
  else{ /*no match */
    return 0;
  }
}


int alldigits(char * str){
  while(*str){
    if (isdigit(*str)) str ++;
    else return 0;
  }
  return 1;
}

int killEverything(int argc, char * const argv [], int sig, int start){
  int i;
#ifdef UNIX
  pid_t pidToKill = 0;
#else
  HANDLE *pidToKill;
#endif
  int jobid;
  for(i = start; i < argc; i++){
    if (argv[i][0] == '%'){ /* given jobid to kill*/
      if (alldigits(&(argv[i][1]))){
	jobid = atoi(&(argv[i][1]));
	pidToKill = pidFromJobid(jobid);
	if (pidToKill == 0){
	  fprintf(stderr, "shell: no job with jobid %%%d \n", jobid);
	}
      }
      else{
		fprintf(stderr, "shell: no job with jobid %s\n", argv[i]);
      }
    }
    else if (alldigits(argv[i])){/* given pid to kill */
#ifdef UNIX
      pidToKill = atoi(argv[i]);
#else
	  pidToKill = handleFromPid(atoi(argv[i]));
#endif
    }
    else {
      pidToKill = pidFromName(argv[i]);
      if (pidToKill == 0){
		fprintf(stderr, "shell: no job or ambiguous jobs match the name %s\n", argv[i]);
      }
    }
    
    /* now we have the pid to send kill signal to */
    if (pidToKill != 0){
#ifdef UNIX
      if (kill(pidToKill, sig)){ /* error occured */
		  perror("shell");
      }else{
	if (sig == SIGCONT){
	  updateStatus(pidToKill, 0);

	}
      }
#else
		if (TerminateProcess(*pidToKill, sig)==0){
			fprintf(stderr, "shell: kill:");
			wperror();
		}
#endif
    }
  }
#ifdef UNIX
  return pidToKill;
#else
  return 0;
#endif
}

void allupper(char * str){
  while (*str){
    *str = toupper(*str);
    str++;
  }
  return;
}

void killUsage(){
  printf("kill [-sigspec] [pid | jobid | command] ... or kill -l sigspec\n");
}


int signalNameToNum(char * str){
  int i;
  for(i=0; i<NUMOFSIGNALS; i++){
    if (strcmp(str, signals[i]) == 0){
      return (i+1);
    }
  }
  return 0;
}

int getsignal(char * str){
  char * buf;
  int tmp;

  if (alldigits(str)){
    return atoi(str);
  }

  else{
    allupper(str);
    if (strncmp("SIG", str, 3)==0){
      return signalNameToNum(str);
    }
    else { /* need to put SIG at the front */
      if (strlen(str)>=11){
	return 0;
      }
      else {
	buf = malloc(sizeof(char)*(4+strlen(str)));
	strcpy(buf, "SIG");
	strcat(buf, str);
	tmp = signalNameToNum(buf);
	free(buf);
	return tmp;
      }
    }
  }
}

int dokill(int argc, char* const argv[]){
#ifdef UNIX
  int sig = 15; /* the default in unix is SIGTERM */
#else
  int sig = 0; /* the default in WINDOWS is exit(0) */
#endif
  int tmp;
  int i;
  /* case 1: no target to kill */
  if (argc < 2){
      killUsage();
	  return 0;
  }
  
  removeJobs();
  /* case 2: starting from here, there is at least one argument */
  if (argv[1][0] =='-'){
    if (argv[1][1] == 'l'){ /* -l */
#ifdef UNIX
      if (argc == 2){ /* print all signals and its name */
	for (i = 0; i< NUMOFSIGNALS; i++){
	  printf("%d) %s\t", i+1, signals[i]);
	  if ((i+1)%4 == 0){
	    printf("\n");
	  }
	}
	printf("\n");
      }
      else{
	for(i = 2; i< argc; i++){
	  tmp = getsignal(argv[i]);
	  if ((tmp <= 0) || tmp>NUMOFSIGNALS){
	    printf("shell: kill: no such signal %s\n", argv[i]);
	  }
	  else{
	    printf("%d: %s\n",tmp, signals[tmp-1]);
	  }
	}
      }
#endif
    }

    else{ /* -sigspec */
      sig = getsignal(&(argv[1][1]));
      if (argc > 2){
	killEverything(argc, argv, sig, 2);
      }
      else{
	killUsage();
      }
    }    
  }
  else {

    killEverything(argc, argv, sig, 1);
  }
  return 0;
}


#ifdef UNIX
int dostop(int argc, char * const argv []) {
  if(argc < 2){
    /* no argument print usage */
  }
  else{
    killEverything(argc, argv, SIGSTOP, 1);
  }
  return 0;
}
int fg(int argc, char * const argv []) {
  int pid;
  int status;
  struct job *jobInfo;
  struct command *cc;
  if (argc != 2){
    /* print usage, fg takes in exactly one argument */
  }
  else {
    /* to do fg, we have to wake up the process first */
    pid = killEverything(argc, argv, SIGCONT, 1);

    
    /* then save all the information about the job for future reference */
    jobInfo = jobInfoFromPid(pid);
    
    if (jobInfo){
      cc = commandAlloc();
      cc->cmdLine=strclone(jobInfo->command);
      cc->pid=jobInfo->pid;


      /* remove the job from the job list (it's not background any more) */
      updateStatus(pid, 4);
      removeJobs();
    
      ccForeground = cc;
      waitpid(pid, &status, WUNTRACED);
      ccForeground = NULL;

      commandFree(cc);
    }else{
      fprintf(stderr, "shell: fg: the specified job not in the job list\n");
    }
  }
  return 0;
}
int bg(int argc, char * const argv []) {
  if (argc < 2){
  }
  else{
    killEverything(argc, argv, SIGCONT, 1);
  }
  return 0;
}
#else
int dostop(int argc, char * const argv []) {
  return 0;
}
int fg(int argc, char * const argv []) {
  return 0;
}
int bg(int argc, char * const argv []) {
  return 0;
}

#endif
