#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define BUF_SIZE 100

typedef struct font
   {
    char *name, ic;
    int width;
    int height;
    int numsegs;
    char ***segs;
    int *segx, *segy;
    int **map, **move;
   } font;

void merge(char **out, int outw, int outh, font *fnt, int sn, int x, int y)
{
 int x2, y2, x3, y3;

 y2 = 0;
 while(fnt->segs[sn][y2])
    {
     x2 = 0;
     while(fnt->segs[sn][y2][x2])
        {
         x3 = x + x2 + fnt->segx[sn];
         y3 = y + y2 + fnt->segy[sn];
         if(x3 < 0 || x3 >= outw || y3 < 0 || y3 >= outh)
            {
             fprintf(stderr, "Attempt to draw outside buffer: %d, %d of %dx%d (char %d, %d) (in %d, %d).\n", x3, y3, outw, outh, x2, y2, x, y);
             exit(1);
            }
         if(fnt->segs[sn][y2][x2] != ' ') out[y3][x3] = fnt->segs[sn][y2][x2];
         x2++;
        }
     y2++;
    }
}

int main(int argc, char **argv)
{
 FILE *f;
 int mode = 0, numfonts = 0, sn, lines, fn, mc, nm, t, u, inc, inlen, mv, posx, posy, hadmove, outw, outh;
 char buf[BUF_SIZE], *del, *bp, *in, **out;
 font *fonts = NULL;

 f = fopen("7segfont.txt", "r");

 while(!feof(f))
    {
     fgets(buf, BUF_SIZE-2, f);
     if(feof(f)) break;
     if(!strtok(buf, "\r\n")) continue;
     if(!strlen(buf)) continue;
     if(0) fprintf(stderr, "Read: %s\n", buf);
     switch(mode)
        {
         case 0:
            fonts = realloc(fonts, sizeof(font) * (numfonts + 1));
            bp = strtok(buf, " ");
            if(!strlen(bp))
               {
                fprintf(stderr, "Unable to read font name.\n");
                exit(1);
               }
            fonts[numfonts].name = strdup(bp);
            bp = strtok(NULL, " ");
            fonts[numfonts].numsegs = atol(bp);
            bp = strtok(NULL, "x");
            fonts[numfonts].width = atol(bp);
            bp = strtok(NULL, "");
            fonts[numfonts].height = atol(bp);

            fprintf(stderr, "Loading font '%s' with %d segments using %d by %d characters.\n", fonts[numfonts].name, fonts[numfonts].numsegs, 
fonts[numfonts].width, fonts[numfonts].height);

            fonts[numfonts].segs = malloc(sizeof(char **) * fonts[numfonts].numsegs);
            fonts[numfonts].segx = malloc(sizeof(int) * fonts[numfonts].numsegs);
            fonts[numfonts].segy = malloc(sizeof(int) * fonts[numfonts].numsegs);
            fonts[numfonts].map = malloc(sizeof(int *) * 256);
            fonts[numfonts].move = malloc(sizeof(int *) * 256);

            for(t = 0; t < 256; t++)
                fonts[numfonts].map[t] = NULL;

            mode = 1;
            break;

         case 1:
            bp = strtok(buf, " ");
            sn = atol(bp) - 1;
            if(sn < 0 || sn >= fonts[numfonts].numsegs)
               {
                fprintf(stderr, "Attempted to read segment %d of %d.  D'oh!\n", sn + 1, fonts[numfonts].numsegs);
                exit(1);
               }

            bp = strtok(NULL, ",");
            fonts[numfonts].segx[sn] = atol(bp);
            bp = strtok(NULL, "");
            fonts[numfonts].segy[sn] = atol(bp);

            fonts[numfonts].segs[sn] = NULL;

            fprintf(stderr, "Loading segment %d, offset (%d,%d).\n", sn + 1, fonts[numfonts].segx[sn], fonts[numfonts].segy[sn]);

            lines = 0;

            mode = 2;
            break;

         case 2:
            del = strdup(buf);
            fprintf(stderr, "Delimiter line is '%s'.\n", del);
            mode = 3;
            break;

         case 3:
            if(!strcmp(buf, del))
               {
                fprintf(stderr, "Delimiter line found, ending segment.\n");
                free(del);

                fonts[numfonts].segs[sn] = realloc(fonts[numfonts].segs[sn], sizeof(char *) * (lines + 1));
                fonts[numfonts].segs[sn][lines] = NULL;

                if(sn == fonts[numfonts].numsegs - 1)
                   {
                    fprintf(stderr, "Done loading segments for font '%s'.\n", fonts[numfonts].name);
                    mode = 4;
                   }
                   else
                   {
                    mode = 1;
                   }
                break;
               }

            fonts[numfonts].segs[sn] = realloc(fonts[numfonts].segs[sn], sizeof(char *) * (lines + 1));
            fonts[numfonts].segs[sn][lines] = strdup(buf);

            fprintf(stderr, "Line %d is '%s'.\n", lines, fonts[numfonts].segs[sn][lines]);

            lines++;
            break;


         case 4:
            if(!strcmp(buf, "map"))
               {
                fprintf(stderr, "Starting to read map.\n");
                mode = 5;
                break;
               }
            fprintf(stderr, "Map not found?\n");
            exit(1);

         case 5:
            if(!strcmp(buf, "endmap"))
               {
                fprintf(stderr, "Done reading map, done reading font '%s'.\n", fonts[numfonts].name);
                numfonts++;
                mode = 0;
                break;
               }

            if(*buf != ':')
                bp = strtok(buf, " :"); 
               else
                bp = strtok(buf, " "); 

            if(!bp)
               {
                fprintf(stderr, "Map character not found.\n");
                exit(1);
               }
            mc = *bp;
            fprintf(stderr, "Reading map for '%c':", mc);

            nm = 0;
            while(bp = strtok(NULL, " :,"))
               {
                if(!strlen(bp)) continue;
                mv = 0;
                if(*bp == '-')
                   {
                    mv = -1;
                    bp++;
                   }
                else if(*bp == '+')
                   {
                    mv = 1;
                    bp++;
                   }
                if(!strlen(bp)) continue;
                sn = atol(bp) - 1;
                if(sn < 0 || sn >= fonts[numfonts].numsegs)
                   {
                    fprintf(stderr, "Map has '%d' of '%d'.  D'oh.\n", sn, fonts[numfonts].numsegs);
                    exit(1);
                   }
                fonts[numfonts].map[mc] = realloc(fonts[numfonts].map[mc], sizeof(int) * (nm + 2));
                fonts[numfonts].move[mc] = realloc(fonts[numfonts].move[mc], sizeof(int) * (nm + 2));
                fonts[numfonts].map[mc][nm] = sn;
                fonts[numfonts].move[mc][nm] = mv;
                nm++;
                fonts[numfonts].map[mc][nm] = -1;
                fprintf(stderr, " (%d)%d", mv, sn + 1);
               }
            fprintf(stderr, "\n");

            break;

        }
    }

 if(mode != 0)
    {
     fprintf(stderr, "End of input file reached before end of font reached?\n");
    }


 if(argc < 3)
    {
     fprintf(stderr, "Usage: %s <font name> <string>\n", argv[0]);
     exit(1);
    }

 for(fn = 0; fn < numfonts; fn++)
    {
     if(!strcmp(argv[1], fonts[fn].name)) break;
    }
 if(fn == numfonts)
    {
     fprintf(stderr, "Unable to find font '%s'.\n", argv[1]);
     exit(1);
    }

 fprintf(stderr, "Building string with font '%s', #%d.\n", argv[1], fn);

 in = strdup(argv[2]);
 inlen = strlen(in);
 if(inlen <= 0)
    {
     fprintf(stderr, "Null input?\n");
     exit(1);
    }

 outh = 0;
 outw = 0;
 t = 0;
 for(inc = 0; inc < inlen; inc++)
    {
     if(t == 0)
         outh += fonts[fn].height;
     if(in[inc] == '\n')
        {
         t = 0;
         continue;
        }
     t += fonts[fn].width;
     if(t > outw) outw = t;
    }

 out = malloc(sizeof(char *) * outh);
 for(t = 0; t < outh; t++)
    {
     out[t] = malloc(sizeof(char) * outw + 1);
     for(u = 0; u < outw; u++)
         out[t][u] = ' ';
     out[t][u] = '\x0';
    }

 posx = 0;
 posy = 0;
 for(inc = 0; inc < inlen; inc++)
    {
     if(0) fprintf(stderr, "Processing '%c'.\n", in[inc]);
     if(in[inc] == '\n')
        {
         posy++;
         posx = 0;
         continue;
        }
     if(in[inc] == ' ')
        {
         posx++;
         continue;
        }
     if(!fonts[fn].map[in[inc]])
        {
         fprintf(stderr, "No map found for '%c', skipping.\n", in[inc]);
         posx++;
         continue;
        }
     nm = 0;
     hadmove = 0;
     while((sn = fonts[fn].map[in[inc]][nm]) >= 0)
        {
         if(0) fprintf(stderr, "Adding segment %d, offset %d.\n", sn, fonts[fn].move[in[inc]][nm]);
         merge(out, outw, outh, &(fonts[fn]), sn, (posx + fonts[fn].move[in[inc]][nm]) * fonts[fn].width, posy * fonts[fn].height);
         if(fonts[fn].move[in[inc]][nm] != 0) hadmove++;
         nm++;
        }
     if(!hadmove) posx++;
    }

 for(t = 0; t < outh; t++)
    {
     u = outw; 
     while(u >=0 && (out[t][u] == ' ' || out[t][u] == '\x0')) 
        {
         out[t][u] = '\x0';
         u--;
        }
     printf("%s\n", out[t]);
    }

 return 0;
}


