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

/**** p c x _ r e a d _ h e a d e r ****
    Abstract:   Reads the header of a PCX file.
    Parameters: A data storage area for the header, an open file.
    Returns:    The pointer to the data storage area passed, or NULL if error.
    Comments:   The file should be opened in binary mode.
****************************/
PCX_HDR *pcx_read_header(PCX_HDR *hdr, FILE *f)
{
    fseek(f, 0L, SEEK_SET);     /* header is at top of file */
    if (fread(hdr, 1, sizeof(PCX_HDR), f) != sizeof(PCX_HDR))
        return(NULL);
    else
        return(hdr);
}

/**** p c x _ p r i n t _ h e a d e r ****
    Abstract:   Printf's a PCX file header data to a given file.
    Parameters: The PCX file header, the file to write the data to.
    Returns:    Nothing
****************************/
void pcx_print_header(PCX_HDR *hdr, FILE *f)
{
    char *t;
    if (hdr->pcx_id != 0x0A)
    {    
        fprintf(f, "Not a PCX file.\n");
        return;
    }
    switch (hdr->version) {
    case 0: t="2.5"; break;
    case 2: t="2.8 with palette info."; break;
    case 3: t="2.8 without palette info."; break;
    case 5: t="3.0"; break;
    default: t="unknown."; break;
    }
    fprintf(f, "PCX Version %s\n", t);
    fprintf(f, "Compression: %s\n", 
                hdr->encoding==1 ? "Run length" : "Unknown");
    fprintf(f, "%d bits per pixel\n", hdr->bpp);
    fprintf(f, "Image from (%d, %d) to (%d, %d) pixels.\n", 
            hdr->upleftx, hdr->uplefty, hdr->lorightx, hdr->lorighty);
    fprintf(f, "Created on a device with %d x %d dpi resolution.\n", 
            hdr->display_xres, hdr->display_yres);
    fprintf(f, "The image contains %d image planes.\n", hdr->nplanes);
    fprintf(f, "There are %d bytes per line of data after decompression.\n",
                hdr->bytesperline);
    fprintf(f, "There are %ld total bytes in the image.\n", 
                ((long)hdr->lorighty - (long)hdr->uplefty + 1) * 
                 (long)hdr->bytesperline);
    return;
}

/**** p c x _ a l l o c _ l i n e ****
    Abstract:   Allocates enough space to store a complete scan line from the
                current PCX file.
    Parameters: The header pointer.
    Returns:    A pointer to a "big enough" block of allocated space, or
                null if not enough space or an error occurred.
****************************/
BYTE *pcx_alloc_line(PCX_HDR *hdr)
{
    return(calloc(hdr->nplanes, hdr->bytesperline));
}

/**** p c x _ n e x t _ l i n e ****
    Abstract:   Reads the next line from the PCX file.
    Parameters: Pointer to the header, a pointer to the line area (or NULL
                for automatic allocation), and the open data file.
    Returns:    A pointer to the line area, or NULL if there was a problem.
    Comments:   To read the first line, call pcx_read_header() first.
                This sets the file position to the beginning of the data.
****************************/
BYTE *pcx_next_line(PCX_HDR *hdr, BYTE *line, FILE *f)
{
    int c, len;
    BYTE *dp;
    WORD linesize = hdr->nplanes * hdr->bytesperline;
    WORD i;
    if (line == NULL)
        if ((line = pcx_alloc_line(hdr)) == NULL)
            return(line);
    dp = line;                  /* point to data */
    for (i=0; i<linesize; )
    {    
        if ((c = fgetc(f)) == EOF)
            return(NULL);
        if ((c & PCX_COMPRESSED) == PCX_COMPRESSED)
        {
            len = (c & PCX_MASK);
            if ((c = fgetc(f)) == EOF)
                return(NULL);
            memset(dp, (BYTE)c, len);
            dp += len;
            i += len;
        }
        else
        {
            *dp++ = (BYTE)c;
            i++;
        }
    }
    return(line);
}

