/* [ʞ] smake.c <c.hale.su/lexi/util>
 *  ~ lexi hale <lexi@hale.su>
 *  $ cc -Ofast smake.c -osmake
 *  © affero general public license

        	    * I N C O M P L E T E *
			| basic functionality is still |
			|  heavily  under development  |

 * a replacement  for sassc  --watch, smake  maintains an
 * in-memory  dependency graph  for  the specified  files
 * and  automatically re-compiles  them  when  they or  a
 * dependency of theirs changes

 TODO add switch to generate makefile from constructed
      graph and exit */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <assert.h>

enum       e { ok, badname, nofile };
enum    kind { css, sass, scss, bad };
typedef enum { false, true } bool;

#define corebufsz 1024
char corebuf[corebufsz];
void* extbuf, * bufptr;
size_t run = 0;
#define max(a,b) (a > b ? a : b)

void* alloc(size_t sz) {
	if((bufptr + sz) < ((void*)corebuf + corebufsz)) goto mkptr;
	else {
		if (run == 0) {
			run = corebufsz;
			extbuf = malloc(run);
			bufptr = extbuf + sz;
			return extbuf;
		} else {
			if ((bufptr + sz) < (extbuf + run)) goto mkptr;
			else {
				run += max(512, sz+128);
				size_t rsz = bufptr-extbuf;
				extbuf = realloc(extbuf, run);
				bufptr = extbuf + rsz + sz;
				return bufptr - sz;
			}
		}
	}
	assert(false); /* this point should never be reached */
	mkptr: {
		void* ret = bufptr;
		bufptr += sz;
		return ret;
	}
}



struct file {
	char* src;
	char* out;
	enum kind kind;
	struct dep* deplink;
};

struct dep { struct file* f; struct dep* nextdep; };

void readfile(struct file* f, char* src) {
	f->src = src;

	int fd = open(src, O_RDONLY);
	int sz = lseek(fd,0,SEEK_END); lseek(fd,0,SEEK_SET);
	char* file = mmap(0,sz,PROT_READ,MAP_PRIVATE,fd,0);
	close(fd);

	char namebuf[256];
	char* namecur = namebuf;
	char* cur = file;

	char strqt = 0;

read_start:
	if (*cur == 0) goto read_done;
	if (*cur == '"')  { ++ cur; strqt = '"';  goto read_string; }
	if (*cur == '\'') { ++ cur; strqt = '\''; goto read_string; }
	if (*cur == '(')  { ++ cur; strqt = ')';  goto read_string; }
	if (*cur == '[')  { ++ cur; strqt = ']';  goto read_string; }
	if (*cur == '/') {
		switch(cur[1]) {
			case '/': goto skip_line;
			case '*': cur += 2; goto read_ml_comment;
			case   0: goto read_done;
		}
		++cur; goto read_start;
	}
	if (*cur == '@') {
		if (strncmp(cur+1, "import", 6) == 0) {
			cur += 7; goto read_import;
		}
	}
	++cur; goto read_start;

read_import:
	if (*cur == ' ' || *cur == '\t' || *cur == '\n') {
		++cur; goto read_import;
	} else if (*cur == 'u' && cur[1] == 'r' && cur[2] == 'l') {
		strqt=')'; goto read_string;
	} else if (*cur == '"' || *cur=='\'') {
		strqt=*cur;
		while(*++cur != strqt) {
			if (*cur == '\\' && cur[1] == strqt) {
				*namecur++=strqt; cur += 2;
				// FIXME support other escapes
			} else {
				*namecur++=*cur;
			}
		}
		*namecur = 0;
		printf("found import to %s;\n", namebuf);
		namecur = namebuf;
	}
	++cur; goto read_start;

read_string:
	if (*cur == 0) goto read_done; /* unterminated string!? */
	if (*cur++ == strqt) goto read_start;
	goto read_string;

read_ml_comment:
	if (*cur == 0) goto read_done; /* unterminated comment! */
	if (*cur == '*' && cur[1] == '/') {
		cur += 2;
		goto read_start;
	}
	++cur; goto read_ml_comment;

skip_line:
	if (*cur == 0) goto read_done;
	if (*cur++ == '\n') goto read_start;
	goto skip_line;

read_done:

	munmap(file,sz);
}

enum e parsename(struct file* f) {
	char* ext = NULL;
	char* path = f->src;
	
	while(*path++!=0) {
		if(*path == '.') ext = path + 1;
	}

	
	if(ext == NULL) return badname;

	// quickly determine file type
	if        (ext[0] == 's'
			&& ext[2] == 's'
			&& ext[3] == 's') {
		if      (ext[1] == 'c') f -> kind = scss;
		else if (ext[1] == 'a') f -> kind = sass;
		else                    f -> kind = bad;
	} else if (ext[0] == 'c'
			&& ext[1] == 's'
			&& ext[2] == 's' )  f -> kind = css;
	else                        f -> kind = bad;

	if(f -> kind == sass || f -> kind == scss) {
		size_t sz = (ext - f->src);
		f->out = alloc(sz + 4);
		strncpy(f->out, f->src, sz);
		f->out[sz+0] = 'c';
		f->out[sz+1] = 's';
		f->out[sz+2] = 's';
		f->out[sz+3] = 0;
	} else f -> out = f -> src;

	printf("src %s; dst %s; ext %s;\n", f->src,f->out,ext);
}

int main(int argc, char** argv) {
	bufptr = corebuf;
	struct file top;
	readfile(&top, argv[1]);
	parsename(&top);
}
