#include "attributes.h"
#include "scheduled.h"
#include "uogetopt.h"
#include "byte.h"
#include "bailout.h"
#include "iopause.h"
#include "buffer.h"
#include "str.h"
#include "fmt.h"
#include "error.h"
#include "open.h"
#include "wrap_stat.h"
#include "fmt_tai.h"
#include <unistd.h>

static const char *o_dir;
static int o_verbose;
static int o_cmdfile;
static int o_interactive=-1;
static int o_dot_as_home;

static uogetopt2 myopts[]={
{'.',"dot-as-home",uogo_flag,UOGO_NOARG,&o_dot_as_home, 1 ,
"Use current directory instead of $HOME.",0,0},
{'c',"command",uogo_flag,UOGO_NOARG,&o_cmdfile, 1 ,
"Delete command file, not schedule files.",
"The default is to delete scheduled jobs, leaving\n"
"the registered command files alone.",0},
{'d',"dir",uogo_string,0,&o_dir, 0 ,
"Jobdirectory.",
"Jobs will be read from this directory.","DIR"},
{'i',"interactive",uogo_flag,UOGO_NOARG,&o_interactive, 1 ,
"Ask before deleting.",0,0},
{'N',"non-interactive",uogo_flag,UOGO_NOARG,&o_interactive, 0 ,
"Do not ask before deleting.",0,0},
{'v',"verbose",uogo_flag,UOGO_NOARG,&o_verbose, 1 ,
"Print success messages.",0,0},
{0,0,0,0,0,0,0,0,0}
};              
static void die_write(void) attribute_noreturn;

static void die_write(void)  {xbailout(111,errno,"failed to write",0,0,0);}

static void outs(const char *s) 
{ if (-1==buffer_puts(buffer_1,s)) die_write();}
static void outnum(unsigned long ul)
{ char nb[FMT_ULONG]; 
  if (-1==buffer_put(buffer_1,nb,fmt_ulong(nb,ul))) die_write();}

static int ask(void)
{
	/* eat typeahead */
	iopause_fd x;
	struct taia now,then;
	x.fd=0;
	x.events=IOPAUSE_READ;
	while (1) {
		char c;
		int r;
		taia_now(&now);
		then=now;
		iopause(&x,1,&now,&then);
		if (!x.revents) 	
			break;
		r=read(0,&c,1);
		if (-1==r) {
			if (errno==error_intr || errno==error_again 
				|| errno==error_wouldblock)
					continue;
			xbailout(111,errno,"failed to read typeahead",0,0,0);
		}
	}

	while (1) {
		int flag=-1;
		outs("delete? [y/n, default n]:");
		if (-1==buffer_flush(buffer_1)) die_write();
		/* read one line, char by char */
		while (1) {
			int r;
			char c;
			r=read(0,&c,1);
			if (-1==r) {
				if (errno==error_intr || errno==error_again 
					|| errno==error_wouldblock)
						continue;
				xbailout(111,errno,"failed to read answer",0,0,0);
			}
			if (!r)
				xbailout(111,0,"failed to read answer: end of file",0,0,0);
			if ('\n'==c)
				break;
			if (-1==flag) {
				switch(c) {
				case 'Y': case 'y':  flag=1; break;
				case 'N': case 'n':  flag=0; break;
				case ' ': case '\t': break;
				default: flag=2; break;
				}
			}
		}
		if (1==flag) return 1;
		if (0==flag) return 0;
		if (-1==flag) return 0; /* default no */
		/* flag==2 -> continue to ask */
	}
}

static int do_cmdfile(char **argv)
{
	int i;
	int exitcode=0;
	int olddir=open_read(".");
	if (-1==olddir) xbailout(111,errno,"failed to open_read `.'",0,0,0);
	if (-1==chdir(IDDIR)) xbailout(111,errno,"failed to chdir to ",IDDIR,0,0);
	for (i=1;argv[i];i++) {
		struct wrap_stat st;
		if (-1==wrap_stat(argv[i],&st)) {
			warning(errno,"failed to stat ",argv[i],0,0);
			exitcode=1;
			continue;
		}
		if (o_interactive) {
			char buf[128];
			outs(argv[i]); outs("\n");
			outs("  links: "); outnum(st.nlink); outs("\n");
			outs("  modified: "); 
			fmt_tai(buf,sizeof(buf),&st.mtime.sec);
			outs(buf); outs("\n");
			if (st.nlink>1) {
				outs(" not deleted: still in use\n");
				continue;
			} else {
				if (!ask()) 
					continue;
			}
		} else if (st.nlink>1) {
			outs(" not deleted: still in use\n");
			continue;
		}
		if (-1==unlink(argv[i])) {
			warning(errno,"failed to remove ",argv[i],0,0);
			exitcode=1;
		} else {	
			if (o_verbose) {
				outs("removed command with id ");
				outs(argv[i]);
				outs("\n");
			}
		}
	}
	if (-1==fchdir(olddir))
		xbailout(111,errno,"failed to fchdir back from ", IDDIR,0,0);
	close(olddir);
	return exitcode;
}
static int
do_schedulefile(char **argv)
{
  static stralloc jobs;
  int i;
  int exitcode=0;
  struct taia now;
  taia_now(&now);
  load_jobs(".",&jobs);
  for (i=1;argv[i];i++) {
    unsigned int k;
    int done=0;
    for (k=0;jobs.s[k];k+=str_len(jobs.s+k)+1) {
      struct jobinfo j;
      parse_job(jobs.s+k,&j); /* load_jobs did the checks on the job */
      if (j.idlen!=str_len(argv[i]))
	continue;
      if (!byte_equal(j.id,j.idlen,argv[i]))
	continue;
      if (o_interactive) {
	print_job(&j,&now);
	if (!ask()) 
	  continue;
      }
      done=1;
      if (-1==unlink(jobs.s+k)) {
	warning(errno,"failed to remove ",argv[i],0,0);
	exitcode=1;
      } else {	
	if (o_verbose) {
	  outs("removed scheduled job with id ");
	  outs(argv[i]);
	  outs("\n");
	}
      }
    }
    if (!done) {
      warning(0,"No job deleted for ",argv[i],0,0);
      exitcode=1;
    }
  }
  return exitcode;
}

uogetopt_env myoptenv={
"uschedulerm", PACKAGE,VERSION,
"uschedulelist [options] ID1 [...]",
"This program deletes jobs with the identifiers ID1 ... from ~/.uschedule.",
"long",
"Report bugs to uschedule@lists.ohse.de",
2,0,
0,0,
uogetopt_out,
myopts
};



int 
main(int argc, char **argv)
{
	int exitcode=0;

	bailout_progname(argv[0]);
	flag_bailout_fatal_begin=3;
	uogo_posixmode=1;
	myoptenv.program=flag_bailout_log_name;
	uogetopt_parse(&myoptenv,&argc,argv);
	change_dir(0,o_dir,o_dot_as_home);

	if (-1==o_interactive) {
		if (isatty(0) && isatty(1))
			o_interactive=0;
		else
			o_interactive=1;
	}
	if (o_cmdfile)
		exitcode=do_cmdfile(argv);
	else
		exitcode=do_schedulefile(argv);
	if (-1==buffer_flush(buffer_1)) die_write();
	notice();

	return exitcode;
}


syntax highlighted by Code2HTML, v. 0.9.1