
#include "visual_context.h"

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

#define MAX_INTENSITY 0xFFFF           /* according to X... */

#ifndef BYTE_ORDER
/*
 * Definitions for byte order,
 * according to byte significance from low address to high.
 * I do not know how to determine if BYTE_ORDER ise defined under <sys/types.h> (that
 * seem to exist on most machines) or in another file like <sys/machine.h>, that may
 * not exist at all and crash the precompiler!. So in the absence of the definition of
 * BYTE_ORDER, I provide it myself... Obviously at YOUR own risks!
 */
#define LITTLE_ENDIAN   1234           /* least-significant byte first (vax) */
#define BIG_ENDIAN      4321           /* most-significant byte first (IBM, net) */
#define PDP_ENDIAN      3412           /* LSB first in word, MSW first in long (pdp) */
#ifdef vax
#define BYTE_ORDER      LITTLE_ENDIAN
#else
#define BYTE_ORDER      BIG_ENDIAN
#endif /* vax */
#ifdef cygwin
#define BYTE_ORDER      LITTLE_ENDIAN
#endif
#endif /* BYTE_ORDER */

visual_context_t g_vc;

static unsigned long vc_xcolor_to_pixel( XColor * color)
{
    unsigned long pixel = 0;

    /* The following is a trick to convert 1byte p/pixel internal images (128
     * different colors) to 16 or 24 bit 'images' to be passed to the X
     * hardware. The trick consists in replacing the pixels computed by the
     * GTVIRT by the good index in fixed (usually TrueColor) hardware maps to
     * get a good representation of the desired colors. Normally you would
     * retrieve the red_max, red_mult, etc... of the default colormap with a
     * call to XGetRGBColormaps( ), and scale the pixels accordingly. But under
     * linux, or other vendors, this call usually does not work. Fortunately it
     * seems that I am able to deduce the correct values from the R,G and B
     * masks returned by a call to XGetVisualInfo.
     * I MUST figure out the byte order of the machine on which the image is
     * created !
     */

    pixel |= (unsigned long)((double)color->red / MAX_INTENSITY * g_vc.red_max) * g_vc.red_mult;
    pixel |= (unsigned long)((double)color->green / MAX_INTENSITY * g_vc.green_max) * g_vc.green_mult;
    pixel |= (unsigned long)((double)color->blue / MAX_INTENSITY * g_vc.blue_max) * g_vc.blue_mult;
    return pixel;
}

static void vc_compute_rgb_max_mult( unsigned long visual_mask, unsigned long *color_max, unsigned long *color_mult)
{
    /* Compute max and mult by right-shifting the bit fields of mask */

    *color_max = visual_mask;
    *color_mult = 1;
    for (; *color_max != 0; *color_max >>= 1) {
        if (*color_max & 01)
            break;
        *color_mult <<= 1;
    }
}

void vc_build( Display *display, Screen *screen)
{
    int class;
    int visualsMatched;
    XVisualInfo *visualList;
    XVisualInfo *visual_info;

    g_vc.display = display;
    g_vc.screen_pt = screen;
    g_vc.root_window = RootWindowOfScreen( g_vc.screen_pt);
    g_vc.depth = DefaultDepthOfScreen( g_vc.screen_pt);
    g_vc.black_pixel = BlackPixelOfScreen( g_vc.screen_pt);
    g_vc.white_pixel = WhitePixelOfScreen( g_vc.screen_pt);

    visual_info = &g_vc.visual_info;
    visual_info->screen = DefaultScreen( g_vc.display);
    visual_info->depth = g_vc.depth;
    visual_info->class = TrueColor;
    visualList = XGetVisualInfo( g_vc.display, VisualDepthMask | VisualClassMask | VisualScreenMask, visual_info, &visualsMatched);
    if (visualList == NULL) {
        /* try without class mask */
        visualList = XGetVisualInfo( g_vc.display, VisualDepthMask | VisualScreenMask, visual_info, &visualsMatched);
    }
    if (visualList != NULL) {
        /* save the XVisualInfo struture */
        *visual_info = *visualList;
        XFree( visualList);
    }

    g_vc.visual = g_vc.visual_info.visual;
    class = g_vc.visual_info.class;
    g_vc.static_display = !(class == PseudoColor || class == DirectColor || class == GrayScale);
    g_vc.color_display = !(class == GrayScale || class == StaticGray);
    g_vc.is_default_visual = (g_vc.visual == DefaultVisualOfScreen( g_vc.screen_pt));

    vc_compute_rgb_max_mult( g_vc.visual_info.red_mask, &g_vc.red_max, &g_vc.red_mult);
    vc_compute_rgb_max_mult( g_vc.visual_info.green_mask, &g_vc.green_max, &g_vc.green_mult);
    vc_compute_rgb_max_mult( g_vc.visual_info.blue_mask, &g_vc.blue_max, &g_vc.blue_mult);

    if (!(g_vc.gc = XCreateGC( g_vc.display, g_vc.root_window, (unsigned long)0, NULL))) {
        fprintf( stderr, "E-build_dc: Could not get a graphic context. Aborting!");
        exit( 0);
    }
}

void vc_set_colormap( Window w)
{
    XSetWindowColormap( g_vc.display, w, g_vc.colormap);
}

void AffSimplePixmap( char *sdata, Drawable drawable, int sx, int sy)
{
    AffPixmap( sdata, drawable, sx, sy, 0, 0, 0, 0, sx, sy);
}

typedef unsigned int int32;

void AffPixmap( char *sdata, Drawable drawable, int sx, int sy, int src_x, int src_y, int dest_x, int dest_y, int width, int height)
{
    XImage *ximage;
    int i;
    short *adr = NULL;
    int32 *adrl = NULL;
    XColor *col = g_vc.exact_defs;

    ximage = XCreateImage( g_vc.display, g_vc.visual, g_vc.depth, ZPixmap, 0, sdata, sx, sy, 8, 0);

#if (BYTE_ORDER == BIG_ENDIAN)
        ximage->byte_order = MSBFirst;
#else /* if (BYTE_ORDER == LITTLE_ENDIAN) */
        ximage->byte_order = LSBFirst;
#endif

    if (g_vc.depth > 8 && g_vc.depth < 24) {
        adr = malloc( sx * sy * sizeof( short));
        for (i = 0; i < sx * sy; i++) {
            adr[i] = vc_xcolor_to_pixel( col + sdata[i]);
        }
        ximage->data = (char *)adr;
    } else if (g_vc.depth >= 24) {
        adrl = malloc( sx * sy * sizeof( int32));
        for (i = 0; i < sx * sy; i++) {
            adrl[i] = vc_xcolor_to_pixel( col + sdata[i]);
        }
        ximage->data = (char *)adrl;
    } else {
        ximage->data = (char *)sdata;
    }

    XPutImage( g_vc.display, drawable, g_vc.gc, ximage, src_x, src_y, dest_x, dest_y, width, height);
    XFree( ximage);
    if (adr != NULL)
        free( adr);
    if (adrl != NULL)
        free( adrl);
}

