#include <stdio.h>
#include <dos.h>
#include "segavga.h"
#include "mode_x.h"
#include "dgif.h"

#define OPTIONS "a:bc:d:gimp:qr:"

FILE *f;
char name[30];
unsigned int page,
             lines,
             bad_code_count,
             debug = 0,         /* debug = 0  -  No debug messages     */
                                /*       = 1  -  Image Desc one        */
                                /*       = 2  -  Image Desc two        */
                                /*       = 3  -  Screen Desc           */
                                /*       = 4  -  All three (use more!) */
             y;


void usage(void)
{
    printf("\nUsage: %s [options] filename.gif\n\n", name);
    printf("Options are:  -b    : Blank screen during decode\n");
    printf("              -g    : Apply grey scale to map\n");
    printf("              -i    : Ignore local color maps\n");
    printf("              -m    : Use grey scale map\n");
    printf("              -q    : Disable info at end\n");
    printf("              -a x  : Port address in hex\n");
    printf("              -c x  : Comm port [1..4]\n");
    printf("              -d x  : Debug level [1..4]\n");
    printf("              -p x  : External palette file\n\n");
    printf("Where <x> is either a number or a filename.\n");
    printf("Example: %s -i -d 3 -a 2f8\n", name);
    exit(-1);
}


void disable_greyscale(unsigned char c)
{
    union REGS regs;

    regs.h.ah = 0x12;
    regs.h.al = c;
    regs.h.bl = 0x33;

    int86(0x10, &regs, &regs);
}

void blank_screen(unsigned char c)
{
    union REGS regs;

    regs.h.ah = 0x12;
    regs.h.al = c;
    regs.h.bl = 0x36;

    int86(0x10, &regs, &regs);
}

int out_line(unsigned char pixels[], int linelen)
{
    register int x;

    if (!debug)
    {
        setactpage(page);               /* Select page to write pixels into */

        for(x = 0; x < linelen; x++)
            writepixel(x, y, pixels[x]);
        y++;

        if (lines <= 200)
        {
            for(x = 0; x < linelen; x++)
                writepixel(x, y, pixels[x]);
            y++;
        }
    }

    return 0;
}


int get_byte(void)
{
    return (int) fgetc(f);
}


void main(int argc, char *argv[])
{
    Palette pal, local_pal;
    GIFDesc gd;
    ImageDesc id;
    char *Sig[6], *GIFSig = "GIF89a", separator, *gif_file, map_file[30];
    unsigned char local_map, interlaced, bpp, global_map, col_res, blank,
                  ignore_local_maps, external_map, grey, greymap, quiet;
    unsigned int base, loop, colors, error_count1, error_count2;
    int success1, success2, c;

    base = com_base[1];     /* Default output to COM2 */

    strcpy(name, argv[0]);

    for (loop = 0; loop < strlen(name); loop++)
    {
        name[loop] = tolower(name[loop]);
    }

    ignore_local_maps = external_map = grey = blank = greymap = quiet =\
                        opterr = 0;

    while((c = getopt(argc,argv,OPTIONS)) != EOF)
    {
        switch(c)
        {
            case 'a':     if(!sscanf(optarg,"%x",&base))
                              usage();
                          break;

            case 'b':     blank = 1;
                          break;

            case 'c':     base = com_base[atoi(optarg) - 1];
                          break;

            case 'd':     debug = atoi(optarg);
                          break;

            case 'g':     grey = 1;
                          break;

            case 'i':     ignore_local_maps = 1;
                          break;

            case 'm':     greymap = 1;
                          break;

            case 'q':     quiet = 1;
                          break;

            case 'p':     strcpy(map_file, optarg);
                          external_map = 1;
                          break;

            default:      usage();
        }
    }

    if ((gif_file = argv[optind]) == NULL)
    {
        printf("\nError: You _must_ supply an input file to view.\n");
        usage();
    }

    if ((f = fopen(gif_file, "rb")) == NULL)
    {
        fprintf(stderr, "File %s does not exist.\n", gif_file);
        exit(-1);
    }

    if (!debug)
        Glasses(NONE);              /* Set both lenses clear */

    if (debug)
      printf("\nFile: %s.\n\n", gif_file);

    fread(&Sig, 6, 1, f);       /* Read the header */

    if (strncmp(Sig, GIFSig, 3) != 0)
    {
        printf("File %s is not a valid '%.6s' GIF file.\n", argv[1], Sig);
        close(f);
        exit(-1);
    }

    fread(&gd, sizeof(gd), 1, f);   /* Read the screen descriptor */

    global_map = gd.ColInfo >> 7;
    col_res = ((gd.ColInfo & 0x70) >> 4) + 1;
    bpp = (gd.ColInfo & 0x07) + 1;
    colors = 1 << bpp;
    lines = gd.Height;

    if ((debug == 3) || (debug == 4))
    {
        printf("Screen width: %d\n", gd.Width);
        printf("Screen height: %d\n", gd.Height);
        printf("Screen color info: 0x%.2x\n", gd.ColInfo);
        printf("Screen background: %d\n", gd.BackGround);
        printf("Screen aspect ratio: %d\n", gd.PixelAspect);
        printf("Color resolution: %d bits\n", col_res);
        printf("Bits per pixel: %d\n", bpp);

        if (global_map)
        {
            printf("Global colormap present.\n");
            printf("There are %d colors in the global color map.\n\n", colors);
        }
        else
        {
            printf("Global colormap absent.\n\n");
            printf("Will use local color map, if found.\n\n");
            printf("Press any key to continue.......");
            while (!kbhit());
            getch();
        }
    }

    if (!debug)
    {
        set320x400mode();               /* Jump into 320x400 mode */
        if (grey)
            disable_greyscale(0);
        if (blank)
            blank_screen(1);
    }

    if (global_map)
    {
        fread(&pal, colors, 3, f);        /* Read global colormap */

        for(loop = 0; loop < colors; loop++)
        {
            pal[loop].r /= 4;
            pal[loop].g /= 4;
            pal[loop].b /= 4;
        }

        if (!debug)
            setvgapalette(&pal);            /* Set the palette from BIOS */
    }

    /* All this is for the left image */

    separator = fgetc(f);           /* Read in the image separator ',' char */

    if (separator != 0x2c)
    {
        if (!debug)
            end320x400mode();
        printf("No image separator -> Empty file: no image!\n");
        printf("Character read is %c(%x)\n", separator, separator);
        if (!debug)
            exit(-1);
    }

    fread(&id, sizeof(id), 1, f);   /* Read the image descriptor */

    local_map = id.Info >> 7;
    interlaced = (id.Info & 0x40) == 0x40;

    if (local_map)
    {
        if (!ignore_local_maps)      /* Abort if local map present */
        {
            if (!debug)
                end320x400mode();
            printf("Found local color map in the first image..... Aborting.\n");
            printf("If you wish to ignore the local maps and use the global\n");
            printf("one, use the -i flag. The info byte is %c(0x%2x)\n", id.Info,\
                                                                         id.Info);
            if (!debug)
                exit(-1);
        }

        bpp = (id.Info & 0x07) + 1;
        colors = 1 << bpp;
        fread(&local_pal, colors, 1, f);     /* Read first local colormap */
        if (!global_map)
        {
            for (loop = 0; loop < colors; loop++)
            {
                pal[loop].r = local_pal[loop].r / 4;
                pal[loop].g = local_pal[loop].g / 4;
                pal[loop].b = local_pal[loop].b / 4;
            }
            if (!debug)
                setvgapalette(&pal);       /* Set the palette from BIOS */
        }
        global_map = 1;
    }

    if ((debug == 1) || (debug == 4))
    {
        printf("Image separator is '%c'\n", separator);
        printf("Image left: %d\n", id.Left);
        printf("Image top: %d\n", id.Top);
        printf("Image width: %d\n", id.Width);
        printf("Image height: %d\n", id.Height);
        printf("Image info: 0x%.2x\n", id.Info);
        printf("Color resolution: %d bits\n", col_res);
        printf("Bits per pixel: %d\n", bpp);

        if (interlaced)
        {
            if (!debug)
                end320x400mode();
            printf("Image is interlaced.\n");
            printf("Cannot handle it!\n");
            if (!debug)
                exit(-1);
        }
        else
            printf("Image is not interlaced.\n");

        if (local_map)
        {
            printf("Local colormap present.\n");
            printf("There are %d colors in the local color map.\n\n", colors);
        }
        else
            printf("Local colormap absent.\n\n");
    }

    if (!debug)
        setvispage(0);

    y = 0;
    page = 0;

    if (!debug)
        Glasses(LEFT);

    success1 = decoder(id.Width);

    error_count1 = bad_code_count;

    bad_code_count = 0;

    if (success1 != 0)
    {
        if (!debug)
            end320x400mode();
        printf("Error number %d in decoding of first image.\n", success1);
        printf("Look in dgif.h.\n");
        if (!debug)
            exit(-1);

    }

    /* All this is for the right image */

    fgetc(f);                       /* Read in trailer code */

    separator = fgetc(f);           /* Read in the image separator ',' char */

    if (separator != 0x2c)
    {
        if (!debug)
            end320x400mode();
        printf("Single image GIF file: This is a stereo viewer!\n");
        printf("Character read is %c(%x)\n", separator, separator);
        if (!debug)
            exit(-1);
    }

    fread(&id, sizeof(id), 1, f);   /* Read in image descriptor */


    local_map = id.Info >> 7;
    interlaced = ((id.Info & 0x40) == 0x40);

    if (local_map)
    {
        if (!ignore_local_maps)      /* Abort if local map present */
        {
            if (!debug)
                end320x400mode();
            printf("Found local color map in the second image..... Aborting.\n");
            printf("If you wish to ignore the local maps and use the global\n");
            printf("one, use the -i flag. The info byte is %c(0x%2x)\n", id.Info,\
                                                                         id.Info);
            if (!debug)
                exit(-1);
        }

        bpp = (id.Info & 0x07) + 1;
        colors = 1 << bpp;
        fread(&local_pal, colors, 1, f);   /* Read second local colormap */
        if (!global_map)
        {
            for (loop = 0; loop < colors; loop++)
            {
                pal[loop].r = local_pal[loop].r;
                pal[loop].g = local_pal[loop].g;
                pal[loop].b = local_pal[loop].b;
            }
            if (!debug)
                setvgapalette(&pal);       /* Set the palette from BIOS */
        }
        global_map = 1;
    }

    if ((debug == 2) || (debug == 4))
    {
        printf("Image separator is '%c'\n", separator);
        printf("Image left: %d\n", id.Left);
        printf("Image top: %d\n", id.Top);
        printf("Image width: %d\n", id.Width);
        printf("Image height: %d\n", id.Height);
        printf("Image info: 0x%.2x\n", id.Info);
        printf("Color resolution: %d bits\n", col_res);
        printf("Bits per pixel: %d\n", bpp);

        if (interlaced)
        {
            if (!debug)
                end320x400mode();
            printf("Image is interlaced.\n");
            printf("Cannot handle it!\n");
        }
        else
            printf("Image is not interlaced.\n");

        if (local_map)
        {
            printf("Local colormap present.\n");
            printf("There are %d colors in the local color map.\n\n", colors);
        }
        else
            printf("Local colormap absent.\n\n");
    }

    if (!debug)
        setvispage(1);
    y = 0;
    page = 1;

    if (!debug)
        Glasses(RIGHT);

    success2 = decoder(id.Width);

    error_count2 = bad_code_count;

    close(f);

    if (success2 != 0)
    {
        if (!debug)
            end320x400mode();
        printf("Error number %d in decoding of second image.\n", success2);
        printf("Look in dgif.h.\n");
        if (!debug)
            exit(-1);
    }

    if (greymap)
    {
        for (loop = 0; loop < 255; loop++)
        {
            pal[loop].r = loop / 4;
            pal[loop].g = loop / 4;
            pal[loop].b = loop / 4;
        }
        if (!debug)
            setvgapalette(&pal);       /* Set the palette from BIOS */
    }

    if (external_map)
    {
        if ((f = fopen(map_file, "rb")) == NULL)
        {
            fprintf(stderr, "File %s does not exist.\n", gif_file);
            exit(-1);
        }

        for (loop = 0; loop < 255; loop++)
            fscanf(f, "%d %d %d\n", &pal[loop].r, &pal[loop].g, &pal[loop].b);

        for (loop = 0; loop < 255; loop++)
        {
            pal[loop].r /= 4;
            pal[loop].g /= 4;
            pal[loop].b /= 4;
        }

        setvgapalette(&pal);       /* Set the palette from BIOS */

        fclose(f);
    }

    if (!debug)
    {
        if (blank)
            blank_screen(0);
        while (!kbhit())
        {
            WaitForVerticalRetrace();
            Glasses(LEFT);
            setvispage(0);
            WaitForVerticalRetrace();
            Glasses(RIGHT);
            setvispage(1);
        }

        getch();
        Glasses(BOTH);
        end320x400mode();
        if (grey)
            disable_greyscale(1);
    }

    if (debug)
    {
        if (error_count1)
            printf("Image one decoded with %d errors, returning code %d.\n\n",\
                    error_count1, success1);
        else
            printf("No errors were found in image one.\n\n");

        if (error_count2)
            printf("Image two decoded with %d errors, returning code %d.\n\n",\
                    error_count2, success2);
        else
            printf("No errors were found in image two.\n\n");
    }

    if (!quiet)
    {
        printf("The image '%s' has a resolution of %dx%d.\n", gif_file, id.Width, id.Height);
        printf("Port address 0x%.4x was used for the glasses output.\n", base);
        if (grey)
            printf("GIF palette was changed to greyscale.\n");
        if (greymap && global_map)
            printf("GIF palette was ignored and greyscale used.\n");
        else
            if (greymap && !global_map)
                printf("GIF palette was not present, so greyscale used.\n");
    }
}
