/* add to a project, or just compile with:
	cc screenspew.m -lobjc -framework AppKit
*/
#include <CoreServices/CoreServices.h>
//#include <ApplicationServices/CoreGraphics/CoreGraphics.h>
//#include "ApplicationServices/ApplicationServices.h"
#include <ApplicationServices/ApplicationServices.h>
#include <AppKit/AppKit.h>
#include <time.h>

void randomAttractorDance( unsigned char* screen, unsigned int sWidth,
    unsigned int sh, unsigned int sw ) {
    int x, y;
#if 01
#if 01
// kinda pretty
#define NATT 5
    static int attractors[5][2] = {{300,10},{800,10},{1100,400},{550,700},{10,400}};
#elif 01
// noise?
#define NATT 4
    static int attractors[4][2] = {{10,10},{1100,10},{1100,700},{10,700}};
#else
// "pascal's trangle"
#define NATT 3
    static int attractors[3][2] = {{10,10},{1100,10},{550,700}};
#endif
    int dir;
    dir = random() % NATT;
    x = (attractors[dir][0] + x) / 2;
    y = (attractors[dir][1] + y) / 2;
    screen[y*sWidth + x*4 + 2] += 8;
#elif 01
    y = random() % sh;
    x = random() % (sw*4);
    screen[y*sWidth + x] += 16;
#else
    for ( y = 0; y < sh; y++ ) {
        unsigned int* row;
        row = (unsigned int*)(screen + (sWidth*y));
        for ( x = 0; x < sw; x++ ) {
            row[x] = i;
            i++;
        }
    }
#endif
}

void caRule( unsigned char* dest, unsigned char* pix0, unsigned char* pix1, unsigned char* pix2 ) {
#if 01
#define SELFWEIGHT (.5)
    dest[1] = (pix0[1]*SELFWEIGHT + pix1[1]*SELFWEIGHT + pix2[3] /*+ pix1[2] /*+ pix1[3]*/) / (2*SELFWEIGHT + 1) + 0; // red
    dest[2] = (pix0[2]*SELFWEIGHT + pix1[2]*SELFWEIGHT + pix2[1] /*+ pix1[1] /*+ pix1[3]*/) / (2*SELFWEIGHT + 1) + 2; // green
    dest[3] = (pix0[3]*SELFWEIGHT + pix1[3]*SELFWEIGHT + pix2[2] /*+ pix1[2] /*+ pix1[1]*/) / (2*SELFWEIGHT + 1) + 1; // blue
#elif 0
#define SELFWEIGHT (1)
    dest[1] = (pix0[1]*SELFWEIGHT + pix1[1]*SELFWEIGHT + pix2[3] /*+ pix1[2] /*+ pix1[3]*/) / (2*SELFWEIGHT + 1) + 0; // red
    dest[2] = (pix0[2]*SELFWEIGHT + pix1[2]*SELFWEIGHT + pix2[1] /*+ pix1[1] /*+ pix1[3]*/) / (2*SELFWEIGHT + 1) + 2; // green
    dest[3] = (pix0[3]*SELFWEIGHT + pix1[3]*SELFWEIGHT + pix2[2] /*+ pix1[2] /*+ pix1[1]*/) / (2*SELFWEIGHT + 1) + 1; // blue
#elif 0
#define SELFWEIGHT (3)
    dest[1] = (pix0[1]*SELFWEIGHT + pix1[1]*SELFWEIGHT + pix2[1]*SELFWEIGHT + pix1[3] /*+ pix1[3]*/) / (SELFWEIGHT*3 + 1) + 0x50; // red
    dest[2] = (pix0[2]*SELFWEIGHT + pix1[2]*SELFWEIGHT + pix2[2]*SELFWEIGHT + pix1[1] /*+ pix1[3]*/) / (SELFWEIGHT*3 + 1) + 0x01; // green
    dest[3] = (pix0[3]*SELFWEIGHT + pix1[3]*SELFWEIGHT + pix2[3]*SELFWEIGHT + pix1[2] /*+ pix1[1]*/) / (SELFWEIGHT*3 + 1) + 0x01; // blue
#elif 0
    dest[1] = (pix0[1] + pix1[1] + pix2[1] /*+ pix1[2] /*+ pix1[3]*/) / 3 + 0x10; // red
    dest[2] = (pix0[2] + pix1[2] + pix2[2] /*+ pix1[1] /*+ pix1[3]*/) / 3 + 0x10; // green
    dest[3] = (pix0[3] + pix1[3] + pix2[3] /*+ pix1[2] /*+ pix1[1]*/) / 3 + 0x10; // blue
#elif 0
    unsigned char val;
    val = (pix0[1] + pix1[1] + pix2[1]) / 3 + 0x3f;
    //dest[0] = val; // alpha, unused
    dest[1] = val; // red
    dest[2] = val; // green
    dest[3] = val; // blue
#else
    /* rule30 */
static const unsigned char ruleTable[8] = {
    0xFF,	// 0 0 0
    0xFF,	// 0 0 1
    0xFF,	// 0 1 0
    0x0,	// 0 1 1
    0x0,	// 1 0 0
    0x0,	// 1 0 1
    0x0,	// 1 1 0
    0xFF	// 1 1 1
};
    int index;
    index  = (*pix0 & 0x80) >> 5;
    index |= (*pix1 & 0x80) >> 6;
    index |= (*pix2 & 0x80) >> 7;
//    printf("%0.2x %0.2x %0.2x =%1d> %0.2x\n",*pix0, *pix1, *pix2, index, ruleTable[index] );
    *dest = *(dest+1) = *(dest+2) = *(dest+3) = ruleTable[index];
#endif
}

void cellularAutomata( unsigned char* screen, unsigned int sWidth,
    unsigned int sh, unsigned int sw ) {
    int x;
    static int y;
    int nexty;
    unsigned char* row;
    unsigned char* dest;
    unsigned char* src;
    
    nexty = (y + 1) % sh;
    row = (unsigned char*)(screen + (sWidth*y));
    dest = (unsigned char*)(screen + (sWidth*nexty));
    caRule( dest, row + 4*(sw - 1), row, row + 4 );
    dest+=4;
    src = row;
    for ( x = 1; x < sw-1; x += 1 ) {
        caRule( dest, src, src + 4, src + 8 );
        dest+=4;
        src+=4;
    }
    caRule( dest, row + 4*(sw - 2), row + 4*(sw - 1), row );
    y = nexty;
//    printf("y=%04d\n",y);
}

int main (int argc, const char * argv[]) {
    CGDirectDisplayID they[9];
    int i, ln;
    int sec;
    unsigned char* screen;
    unsigned int sWidth;//bytes
    unsigned int sh, sw;
    NSDate *today;
    // insert code here...
//    printf("Hello, World!\n");

    if ( NSApp == nil ) {
        [NSApplication sharedApplication];
//        printf("%s:%d NSApp %x\n",__FILE__,__LINE__,NSApp);
    }
    [[NSAutoreleasePool alloc] init];
    today = [NSDate date];
    
    CGGetActiveDisplayList( 9, they, &ln );
#if 0
    for ( i = 0; i < ln; i++ ) {
        printf("active display %d id is %d (0x%x), %d pixels wide and %d pixels high\n", i, they[i], they[i],
            sw = CGDisplayPixelsWide(they[i]), sh = CGDisplayPixelsHigh(they[i]) );
        printf("\tbits per pixel %d, bits per sample %d, samples per pixel %d, bytes per row %d\n",
            CGDisplayBitsPerPixel(they[i]), CGDisplayBitsPerSample(they[i]), CGDisplaySamplesPerPixel(they[i]), sWidth = CGDisplayBytesPerRow(they[i]) );
        printf("\tis captured %d\n", CGDisplayIsCaptured(they[i]) );
    }
#endif
    sw = CGDisplayPixelsWide(they[0]);
    sh = CGDisplayPixelsHigh(they[0]);
    sWidth = CGDisplayBytesPerRow(they[0]);
    sec = time(NULL) + 10000;
    srandom( sec );
    CGDisplayCapture( they[0] );
    screen = (unsigned char*)CGDisplayBaseAddress(they[0]);
    if ( screen == NULL ) exit(11);
    for ( i = 0; i < sWidth; i++ ) {
#if 0
        *(screen+i) = 0xFF;
#else
        screen[i] = random();
#endif
    }
    ((unsigned int*)screen)[500] = 0x00FF0000; // red
//    ((unsigned int*)screen)[400] = 0x0000FF00; // green
//    ((unsigned int*)screen)[300] = 0x000000FF; // blue
    while ( time(NULL) < sec ) {
        NSEvent* evt;
//        randomAttractorDance( screen, sWidth, sh, sw );
        cellularAutomata( screen, sWidth, sh, sw );
        evt = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:today inMode:NSDefaultRunLoopMode dequeue:YES];
        if ( evt != nil ) switch ( [evt type] ) {
        case 0:
            break;
        case NSLeftMouseDown:
            goto quit;
        case NSKeyDown:
        case NSKeyUp: {
            NSString* s = [evt characters];
            unichar c = [s characterAtIndex:0];
            printf("key %x\n",c );
            if ( c == 'q' ) {
                goto quit;
            }
        }    break;
        default:
	    ;
        }
    }
quit:
    CGDisplayRelease( they[0] );
    return 0;
}
