#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <sysexits.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>

void usage(const char *pname) {
	fprintf(stderr, "Usage: %s [-h|--help] [-d|--drvr driverfile] [-o|--output outfile]\n", pname);
	fprintf(stderr, "-h | --help:                 This usage statement\n");
	fprintf(stderr, "-d | --drvr:                 The filename for the device driver\n");
	fprintf(stderr, "-o | --output:               The filename to write the resource to\n");
	return;
}

int main(int argc, char *argv[]) {
	char c = 0;
	int loptind = 0;
	char *infile = NULL;
	char *outfile = NULL;
	int ifd;
	int ofd;
	struct option o[] = {
		{"help", 0, 0, 'h'},
		{"drvr", 1, 0, 'd'},
		{"output", 1, 0, 'o'},
		{0,0,0,0}
	};

	while( (c = getopt_long(argc, argv, "d:ho:", o, &loptind)) != -1 ) {
		switch(c) {
		case 'd':
			if(!optarg) {
				usage(argv[0]);
				exit(EX_USAGE);
			}
			infile = optarg;
			break;
		case 'o':
			if(!optarg) {
				usage(argv[0]);
				exit(EX_USAGE);
			}
			outfile = optarg;
			break;
		case 'h':
			usage(argv[0]);
			exit(EX_USAGE);
			break;
		}
	}

	if(!infile || !outfile) {
		usage(argv[0]);
		exit(EX_USAGE);
	}
	errno = 0;

	ifd = open(infile, O_RDONLY);
	if(ifd < 0) {
		fprintf(stderr, "Could not open driver input file %s: %d %s\n", infile, errno, strerror(errno));
		exit(EX_CONFIG);
	}

	ofd = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
	if(ofd < 0) {
		fprintf(stderr, "Could not open output file %s: %d %s\n", outfile, errno, strerror(errno));
		exit(EX_CONFIG);
	}
	
	uint32_t tmpint;
	tmpint = 0x02000000 | (4 /* this entry */ + 4 /* EOL */);
	tmpint = htonl(tmpint);
	if(write(ofd, &tmpint, sizeof(tmpint)) != sizeof(tmpint)) {
		fprintf(stderr, "Error writing sDriver directory entry: %d %s\n", errno, strerror(errno));
		exit(EX_SOFTWARE);
	}
	tmpint = 0xFF000000;
	tmpint = htonl(tmpint);
	if(write(ofd, &tmpint, sizeof(tmpint)) != sizeof(tmpint)) {
		fprintf(stderr, "Error writing sDriver directory EOL: %d %s\n", errno, strerror(errno));
		exit(EX_SOFTWARE);
	}

	struct stat sb;
	if(fstat(ifd, &sb) != 0) {
		fprintf(stderr, "Error fstat'ing input file: %d %s\n", errno, strerror(errno));
		exit(EX_SOFTWARE);
	}

	char *drvrbuffer = calloc(1, sb.st_size);
	tmpint = (uint32_t)sb.st_size + 4;
	tmpint = htonl(tmpint);
	if(write(ofd, &tmpint, sizeof(tmpint)) != sizeof(tmpint)) {
		fprintf(stderr, "Error writing sBlock size: %d %s\n", errno, strerror(errno));
		exit(EX_SOFTWARE);
	}

	if(read(ifd, drvrbuffer, sb.st_size) != sb.st_size) {
		fprintf(stderr, "Error reading driver file: %d %s\n", errno, strerror(errno));
		exit(EX_SOFTWARE);
	}

	if(write(ofd, drvrbuffer, sb.st_size) != sb.st_size) {
		fprintf(stderr, "Error writing sBlock driver: %d %s\n", errno, strerror(errno));
		exit(EX_SOFTWARE);
	}

	free(drvrbuffer);
	close(ifd);
	close(ofd);
	exit(EX_OK);
}
