#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include "dirent.h"

#define VERSION "2.1"

void process (char *class, char *infile, char *outfile, char *t1, char *t1acc,
              char *t2, char *t2acc, char *prefix);
void list (char *pattern, char *dir, int catalog);

void usage ()
{
   fprintf (stderr, 
	    "usage:\n"
	    "    genclass -list [proto ...]\n"
	    "    genclass -catalog [proto ...]\n"
	    "    genclass type1 {ref|val} proto [out_prefix]\n"
	    "    genclass -2 type1 {ref|val} type2 {ref|val} proto [out_prefix]\n");
   exit(1);
}

main(int argc, char *argv[])
{
   char *protodir;
   char currentdir[512];
   int twotypes;
   char *t1, *t1acc, *t2, *t2acc;
   char class[512];
   char protoh[512];
   char protocc[512];
   char prefix[512];
   char protohpath[512];
   char protoccpath[512];
   DIR *searchdir;
   struct dirent *searchfile;
   char outh[512];
   char outcc[512];

   if (argc == 1 || (strcmp (argv[1], "-usage") == 0)) {
      usage();
      exit (0);
   }

   if (strcmp (argv[1], "-version") == 0) {
      fprintf (stdout, "genclass: version %s\n", VERSION);
      exit (0);
   }

   if (strcmp (argv[1], "-requires") == 0) {
      fprintf (stdout, "nothing else\n");
      exit (0);
   }

   protodir = (char *)getenv ("PROTODIR");
   getcwd (currentdir, 511);

   if (strcmp (argv[1], "-list") == 0 || strcmp (argv[1], "-catalog") == 0) {
      int cnt, cat = 0;
      char set[256];

      if (strcmp (argv[1], "-catalog") == 0) {
	 cat = 1;

	 for (set[0] = 0, cnt = 2; cnt < argc; cnt++)
	   strcat(strcat(set, argv[cnt]), " ");

	 if (set[0] == 0) {
	    strcpy(set, "all");
	 }

	 printf("Catalog of %s class templates\n"
		"Directories searched:\n"
		"    %s\n"
		"    %s\n"
		"selecting: %s\n"
		"classes available:\n", argv[0], protodir, currentdir, set);
      }

      if (argc > 2) {
	 for (cnt = 2; cnt < argc; cnt++)
	 { 
	    if (protodir)
	       list(argv[cnt], protodir, cat);
	    list(argv[cnt], currentdir, cat);
	 }
      }
      else {
	 if (protodir)
	   list("", protodir, cat);
	 list("", currentdir, cat);
      }

      if (cat)
	 putchar('\n');

      exit (0);
   }

   if (strcmp (argv[1], "-2") == 0) {
      if (argc != 7 && argc != 8)
         usage();
      twotypes = 1;
      t1 = argv[2];
      if (strcmp (argv[3], "ref") == 0)
         t1acc = "&";
      else
         if (strcmp (argv[3], "val") == 0)
            t1acc = " ";
         else
            usage();
      t2 = argv[4];
      if (strcmp (argv[5], "ref") == 0)
         t2acc = "&";
      else
         if (strcmp (argv[5], "val") == 0)
            t2acc = " ";
         else
            usage();
      strcpy(class, argv[6]);
      strcat (strcpy (protoh, class), ".hP");
      strcat (strcpy (protocc, class), ".ccP");
      if (argc == 8)
         strcpy (prefix, argv[7]);
      else {
         strcpy (prefix, argv[2]);
         strcat (prefix, "_");
         strcat (prefix, argv[4]);
         strcat (prefix, "_");
      }
   } else {
      if (argc != 4 && argc != 5)
         usage();
      twotypes = 0;
      t1 = argv[1];
      if (strcmp (argv[2], "ref") == 0)
         t1acc = "&";
      else
         if (strcmp (argv[2], "val") == 0)
            t1acc = " ";
         else
            usage();
      t2 = NULL;
      t2acc = NULL;
      strcpy(class, argv[3]);
      strcat (strcpy (protoh, class), ".hP");
      strcat (strcpy (protocc, class), ".ccP");
      if (argc == 5)
         strcpy (prefix, argv[4]);
      else {
         strcpy (prefix, argv[1]);
         strcat (prefix, "_");
      }
   }

   protohpath[0] = 0;
   protoccpath[0] = 0;

   /* find the proto files */
   if (protodir && (searchdir = opendir (protodir))) {
      readdir (searchdir);
      readdir (searchdir);
      while (searchfile = readdir (searchdir)) {
         if (stricmp (protoh, searchfile -> d_name) == 0)
            strcpy (protohpath, protodir);
         else
            if (stricmp (protocc, searchfile -> d_name) == 0)
               strcpy (protoccpath, protodir);
      }
      closedir (searchdir);
   }

   if (protohpath[0] == 0) {
      searchdir = opendir (currentdir);
      readdir (searchdir);
      readdir (searchdir);
      while ((searchfile = readdir (searchdir))) {
         if (stricmp (protoh, searchfile -> d_name) == 0)
            strcpy (protohpath, "./");
         else
            if (stricmp (protocc, searchfile -> d_name) == 0)
               strcpy (protoccpath, "./");
      }
      closedir (searchdir);
   }

   if (protohpath[0] == 0)
      usage();

   strcat (strcat (protohpath, "/"), protoh);
   strcat (strcpy (outh, prefix), protoh);
   outh[strlen (outh) - 1] = '\0';
   if (!IsFileNameValid(outh))
      ChangeNameForFAT(outh);
   process (class, protohpath, outh, t1, t1acc, t2, t2acc, prefix);

   if (protoccpath[0]) {
      strcat (strcat (protoccpath, "/"), protocc);
      strcat (strcpy (outcc, prefix), protocc);
      outcc[strlen (outcc) - 1] = '\0';
      if (!IsFileNameValid(outcc))
	 ChangeNameForFAT(outcc);
      process (class, protoccpath, outcc, t1, t1acc, t2, t2acc, prefix);
   }

   exit (0);
}

void process (char *class,
	      char *infile,
              char *outfile,
              char *t1,
              char *t1acc,
              char *t2, 
              char *t2acc,
              char *prefix)
{
   struct stat statbuf;
   char *inbuf, *outbuf;
   char *ip, *op;
   int ifd, ofd, size;

   stat (infile, &statbuf);

   inbuf = (char *) malloc (statbuf.st_size + 1);
   outbuf = (char *) malloc (statbuf.st_size * 2);

   ifd = open (infile, O_RDONLY|O_TEXT);
   size = read (ifd, inbuf, statbuf.st_size);
   close (ifd);

   inbuf[size] = '\0';

   ip = inbuf;
   op = outbuf;

   while (*ip) {
	if (*ip == '<') {
		if (ip[1] == 'T') {
			if (ip[2] == '>') {
				if (ip[3] == '.') {
					strcpy (op, prefix);
					op += strlen (prefix);
					ip += 4;
					if (!strncmp (ip, "<C>.", 4))
						ip += 4;
				} else {
					strcpy (op, t1);
					op += strlen (t1);
					ip += 3;
				}
			} else {
				if (ip[2] == '&') {
					if (ip[3] == '>') {
						strcpy (op, t1);
						op += strlen (t1);
						strcpy (op, t1acc);
						op += strlen (t1acc);
						ip += 4;
					} else {
						*op++ = *ip++;
					}
				} else {
					*op++ = *ip++;
				}
				
			}
		} else {
			if (ip[1] == 'C' && t2 && t2acc ) {
				if (ip[2] == '>') {
					if (ip[3] == '.') {
						strcpy (op, prefix);
						op += strlen (prefix);
						ip += 4;
						if (!strncmp (ip, "<T>.", 4))
							ip += 4;
					} else {
						strcpy (op, t2);
						op += strlen (t2);
						ip += 3;
					}
				} else {
					if (ip[2] == '&') {
						if (ip[3] == '>') {
							strcpy (op, t2);
							op += strlen (t2);
							strcpy (op, t2acc);
							op += strlen (t2acc);
							ip += 4;
						} else {
							*op++ = *ip++;
						}
					} else {
						*op++ = *ip++;
					}
					
				}
			} else {
				*op++ = *ip++;
			}
		}
	} else {
		*op++ = *ip++;
	}
   }

   *op = 0;

   if (strstr(outbuf, "<C>")) {
      fprintf(stderr, "genclass: the %s class requires the -2 syntax for the 2nd type\n", class);
      exit(1);
   }

   ofd = open (outfile, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0666);
   write (ofd, outbuf, op - outbuf);
   close (ofd);

   free(inbuf);
   free(outbuf);
}

void list(char *pattern, char *dir, int catalog)
{
   char buf[256], *ptr;
   DIR *searchdir;
   struct dirent *searchfile;
   static int column = 0;

   sprintf(buf, "%s/*%s.hP", dir, pattern);

   if ((searchdir = opendir (buf)) == NULL)
      return;

   while ((searchfile = readdir (searchdir))) {
      strcpy(buf, searchfile -> d_name);
      if (buf[0] == '.')
	 continue;
      if ((ptr = strrchr(buf, '.')))
	 *ptr = 0;
      if (catalog) {
	 if (column + strlen(buf) + 1 > 72)
	    putchar('\n'), column = 0;
	 if (column == 0)
	    printf("    "), column = 4;
	 column += strlen(buf) + 1;
      }
      printf("%s%c", buf, catalog ? ' ' : '\n');
   }

   closedir (searchdir);
}
