/**
 * @file graphics_tables.c
 * @brief Implements the BASIC 7.0 color palette lookup.
 *
 * This is a table, so it is laid out as one. The RGB values are the widely
 * reproduced VIC-II palette (Pepto's measurement of a PAL 6569R3), which is what
 * an emulator shows and therefore what somebody porting a listing expects to
 * see. They are not a C128 ROM constant -- the real machine has no RGB anywhere
 * in it, it has a chroma/luma encoder -- so this is a choice, not a transcription.
 */

#include <akerror.h>

#include <akbasic/error.h>
#include <akbasic/graphics.h>

/**
 * @brief The sixteen BASIC 7.0 colors, indexed from 1 as BASIC indexes them.
 *
 * Slot 0 is unused and left black so an off-by-one reads as black rather than as
 * whatever the last entry happened to be.
 */
static const akbasic_Color PALETTE[17] = {
    { 0x00, 0x00, 0x00, 0xff },	/*  0 -- unused; BASIC counts colors from 1  */
    { 0x00, 0x00, 0x00, 0xff },	/*  1 -- black				     */
    { 0xff, 0xff, 0xff, 0xff },	/*  2 -- white				     */
    { 0x88, 0x39, 0x32, 0xff },	/*  3 -- red				     */
    { 0x67, 0xb6, 0xbd, 0xff },	/*  4 -- cyan				     */
    { 0x8b, 0x3f, 0x96, 0xff },	/*  5 -- purple				     */
    { 0x55, 0xa0, 0x49, 0xff },	/*  6 -- green				     */
    { 0x40, 0x31, 0x8d, 0xff },	/*  7 -- blue				     */
    { 0xbf, 0xce, 0x72, 0xff },	/*  8 -- yellow				     */
    { 0x8b, 0x54, 0x29, 0xff },	/*  9 -- orange				     */
    { 0x57, 0x42, 0x00, 0xff },	/* 10 -- brown				     */
    { 0xb8, 0x69, 0x62, 0xff },	/* 11 -- light red			     */
    { 0x50, 0x50, 0x50, 0xff },	/* 12 -- dark grey			     */
    { 0x78, 0x78, 0x78, 0xff },	/* 13 -- medium grey			     */
    { 0x94, 0xe0, 0x89, 0xff },	/* 14 -- light green			     */
    { 0x78, 0x69, 0xc4, 0xff },	/* 15 -- light blue			     */
    { 0x9f, 0x9f, 0x9f, 0xff }	/* 16 -- light grey			     */
};

/**
 * @brief What each COLOR source is bound to before a program says otherwise.
 *
 * A C128 powers on with a blue screen and light blue text. That is not
 * reproduced: a graphics screen defaulting to blue-on-blue makes an unset COLOR
 * look like a bug in the interpreter, so the defaults here are the readable ones.
 * The choice is worth naming rather than hiding, since it is the one place these
 * tables are not a transcription.
 */
static const int SOURCE_DEFAULTS[AKBASIC_COLOR_SOURCES] = {
     1,				/* 0 -- 40-column background:	black	     */
     2,				/* 1 -- 40-column foreground:	white	     */
     3,				/* 2 -- multicolor 1:		red	     */
     4,				/* 3 -- multicolor 2:		cyan	     */
     1,				/* 4 -- 40-column border:	black	     */
     2,				/* 5 -- character color:	white	     */
     1				/* 6 -- 80-column background:	black	     */
};

akerr_ErrorContext *akbasic_graphics_palette(int index, akbasic_Color *dest)
{
    PREPARE_ERROR(errctx);

    FAIL_ZERO_RETURN(errctx, (dest != NULL), AKERR_NULLPOINTER,
		     "NULL destination in palette");
    FAIL_ZERO_RETURN(errctx, (index >= 1 && index <= 16), AKBASIC_ERR_BOUNDS,
		     "Color index %d out of range (1 to 16)", index);
    *dest = PALETTE[index];
    SUCCEED_RETURN(errctx);
}

akerr_ErrorContext *akbasic_graphics_state_init(akbasic_GraphicsState *obj)
{
    PREPARE_ERROR(errctx);
    int source = 0;

    FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER,
		     "NULL graphics state in init");
    obj->mode = 0;
    obj->x = 0.0;
    obj->y = 0.0;
    obj->scaling = false;
    obj->linewidth = 1;
    obj->xmax = (double)AKBASIC_GRAPHICS_WIDTH;
    obj->ymax = (double)AKBASIC_GRAPHICS_HEIGHT;
    /*
     * The size assumed until a device says otherwise, which require_graphics()
     * asks it before every verb that draws. GRAPHIC CLR comes back through here
     * and deliberately resets it: the next verb re-asks, so a stale size cannot
     * outlive the device that reported it.
     */
    obj->devwidth = AKBASIC_GRAPHICS_WIDTH;
    obj->devheight = AKBASIC_GRAPHICS_HEIGHT;
    for ( source = 0; source < AKBASIC_COLOR_SOURCES; source++ ) {
	obj->source[source] = SOURCE_DEFAULTS[source];
    }
    SUCCEED_RETURN(errctx);
}

akerr_ErrorContext *akbasic_graphics_source_color(akbasic_GraphicsState *obj, int source, akbasic_Color *dest)
{
    PREPARE_ERROR(errctx);

    FAIL_ZERO_RETURN(errctx, (obj != NULL && dest != NULL), AKERR_NULLPOINTER,
		     "NULL argument in source_color");
    FAIL_ZERO_RETURN(errctx, (source >= 0 && source < AKBASIC_COLOR_SOURCES),
		     AKBASIC_ERR_BOUNDS,
		     "Color source %d out of range (0 to %d)",
		     source, AKBASIC_COLOR_SOURCES - 1);
    PASS(errctx, akbasic_graphics_palette(obj->source[source], dest));
    SUCCEED_RETURN(errctx);
}
