#include <fnmatch.h>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fnctl.h>
#include <unistd.h>

#include "engine.h"
#include "db.h"
char *subst(char *str,fileinfo *f){
}
int evaluate(exp *t){
	int ret;
	switch (t->expr.tp){
		case T_regexp:
			retname = *buf;
			return regexec(&t->regexp.reg,*t->regexp.buf,11,t->regexp.f->matches,t->regexp.flags);
		case T_wildcard:
			return fnmatch(t->wildcard.pattern,*t->wildcard.buf,t->wildcard.flags);
		case T_stat:
			;
			int value;
   if (t->stat.f->ts <t->stat.f->statts) { stat(t->stat.f->name ,&t->stat.f->stat); t->stat.f->statts=t->stat.f->ts;}

			switch (t->stat.stat){
				case ST_ino:
				 value=t->stat.f->stat.st_ino;
				 break;
				 case ST_mode:
				 value=t->stat.f->stat.st_mode;
				 break;
				 case ST_nlink:
				 value=t->stat.f->stat.st_nlink;
				 break;
				 case ST_uid:
				 value=t->stat.f->stat.st_uid;
				 break;
				 case ST_gid:
				 value=t->stat.f->stat.st_gid;
				 break;
				 case ST_rdev:
				 value=t->stat.f->stat.st_rdev;
				 break;
				 case ST_size:
				 value=t->stat.f->stat.st_size;
				 break;
				 case ST_atime:
				 value=t->stat.f->stat.st_atime;
				 break;
				 case ST_mtime:
				 value=t->stat.f->stat.st_mtime;
				 break;
				 case ST_ctime:
				 value=t->stat.f->stat.st_ctime;
				 break;
			}
			switch (t->stat.comptp){
				case ST_less:
					return value < t->stat.val;
				case ST_greater:
					return value > t->stat.val;
				case ST_equal:
					return value == t->stat.val;
				case ST_subset:
					return value & t->stat.val == value;
				case ST_superset:
					return value & t->stat.val == t->stat.val;
			}
		case T_access:
			
   if (t->access.f->ts <t->access.f->accessts) {t->access.f->access=access(t->access.f->name); t->access.f->accessts=t->access.f->ts;}
			return t->access.val & t->access.f->access;
		case T_boole:
				return t->boole.val;
		case T_logic:
		switch (t->logic.oper){
			case L_AND:
				return (evaluate(t->logic.ex1) && evaluate(t->logic.ex2));
			case L_OR:
				return (evaluate(t->logic.ex1) || evaluate(t->logic.ex2));
			case L_COMMA:
				return (evaluate(t->logic.ex1) , evaluate(t->logic.ex2));
			case L_NOT:
				return (!evaluate(t->logic.ex1));
		}
		case T_prune:
			t->prune.f->prune=1;
			return 1;
		case T_print:
			printf("%s\n",t->print.f->wholename);
			return 1;
		case T_exec:
			system(subst(t->exec.cmd,t->exec.f));
			return 1;
	}
}
enum {NOSYNC,SYNC,RSYNC,DBONLY};
/**/
void scan(dbase *db,exp *ex,char *path,int pathfd, int sync){
	static struct stat st;
	struct dirent *e;
	DIR *dir;
	char *fsort=NULL,*dsort=NULL;
	char **filp=NULL,**dirp=NULL;
	char *fp=NULL,*dp=NULL;
	int fn=0,dn=0;
	int i;
	int needls=0,newfd;
	if (sync==SYNC||sync==RSYNC){
		if (fstat(pathfd,&st));
		db->stat=&st;
		if (st->st_mtime<db->mtime){
			needls=0;
			if (sync==SYNC){

			}else if (sync==RSYNC){
				sync=DBONLY;
			}
		}else{	needls=1;
		}else if(sync==DBONLY){			needls=0;
		}else if(sync==NOSYNC){			needls=1;		
		}
	}
	
	evaluate(ex);
	if(sync==NOSYNC){
	}else{
		readfile(db);
	}

	if (needls){
		*dir=fdopendir(pathfd);
		fp=fsort=malloc(1000000);dp=dsort=malloc(1000000);filp=malloc(1000000);dirp=malloc(1000000);
		if(dir){
			while ((e=readdir(dir))){
				if(lstat(e->d_name,&st)) continue;
				if (strcmp(e->d_name,".")&&strcmp(e->d_name,"..")) {
					if (S_ISDIR(st.st_mode)){
						strcpy(dp,e->d_name); dirp[dn++]=dp;dp+=strlen(dp)+1;
					}else{
						strcpy(fp,e->d_name); filp[fn++]=fp;fp+=strlen(fp)+1;
					}
				}
			}
			closedir(dir);
		}
		qsort(filp,fn,sizeof(char*),cmpstring);   qsort(dirp,dn,sizeof(char*),cmpstring);
		for (i=0;i<fn;i++){
			addfile(db,filp[i]);
			evaluate(ex);
		}
		free(filp);free(fp);
		if(sync==NOSYNC){
			for (i=0;i<dn;i++){
				if (!fchdir(pathfd)){
				newfd=open(db->dirname,O_NOFOLLOW);
				if (newfd!=-1){

					adddir(db,dirp[i]);
					scan(db,ex,dirp[i],newfd,sync);
					addenddir(db);
				}
			}
		}
		free(dirp);free(dp);
	} else {
		while (db->filetype!=FN_ENDDIR){
			switch(db->filetype){

				case FN_FILE:
					evaluate(ex);
					break;
				case FN_DIR:
					if (sync==DBONLY){newfd=-1;
					}else{
						fchdir(pathfd);
						newfd=open(db->dirname,O_NOFOLLOW);
					}
					scan(db,ex,db->dirname,newfd,sync);
					if (newfd!=-1) close(newfd);
					break;
			}
			nextfile(db);
		}
	}
	evaluate(ex);
}
