/*
 * Mini ASCII Movie Player Alexander Strange, July 11, 2003                         *
 * Bitmap font by Joseph Spiros                                                     *
 * $Id$                                                                             *
 * Not based on Apple's ASCIIMoviePlayer or QuickASCII except for the parts that are*
 * This source is completely public domain.                                         *
 */

#include <CoreServices/CoreServices.h>
#include <ApplicationServices/ApplicationServices.h>
#include <QuickTime/QuickTime.h>

typedef struct CPSProcessSerNum {
    UInt32          lo;
    UInt32          hi;
} CPSProcessSerNum;

extern OSErr    CPSGetCurrentProcess(CPSProcessSerNum *);
extern OSErr    CPSEnableForegroundOperation(CPSProcessSerNum *, UInt32, UInt32, UInt32, UInt32);
extern OSErr    CPSSetFrontProcess(CPSProcessSerNum *);
extern OSErr    NativePathNameToFSSpec(char *, FSSpec *, unsigned long);
/*
 * 0,                        ' ' 
 * 0,                        ' ' 
 * 0,                        ' ' 
 * 0x220000,                 ',' 
 * 0x630000,                 '.' 
 * 0x20080,                  ':' 
 * 0x440100,                 ';' 
 * 0x3800,                   '-' 
 * 0x23880,                  '+' 
 * 0x000f83e0,               '=' 
 * 0x401084,                 '!' 
 * 0x421004,                 'i' 
 * 0xe53800,                 'o' 
 * 0x423880,                 't' 
 * 0x51140,                  'x' 
 * 0x0011111f,               '7' 
 * 0x00e5384e,               '6' 
 * 0x00e5294e,               '0'
 * 0xa53944,                 'A' 
 * 0x928ca9,                 'K' 
 * 0x00e5394e,               '8' 
 * 0x19d1173,                '%' 
 * 0xafabea,                 '#' 
 */

const static unsigned long combined_map[256] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0x220000, 0x220000, 0x220000, 0x220000, 0x220000, 0x220000, 0x220000, 0x220000, 0x220000, 0x220000,
    0x630000, 0x630000, 0x630000, 0x630000, 0x630000, 0x630000, 0x630000, 0x630000, 0x630000, 0x630000,
    0x20080, 0x20080, 0x20080, 0x20080, 0x20080, 0x20080, 0x20080, 0x20080, 0x20080, 0x20080,
    0x440100, 0x440100, 0x440100, 0x440100, 0x440100, 0x440100, 0x440100, 0x440100, 0x440100, 0x440100,
    0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800,
    0x23880, 0x23880, 0x23880, 0x23880, 0x23880, 0x23880, 0x23880, 0x23880, 0x23880, 0x23880,
    0x000f83e0, 0x000f83e0, 0x000f83e0, 0x000f83e0, 0x000f83e0, 0x000f83e0, 0x000f83e0, 0x000f83e0, 0x000f83e0, 0x000f83e0,
    0x401084, 0x401084, 0x401084, 0x401084, 0x401084, 0x401084, 0x401084, 0x401084, 0x401084, 0x401084,
    0x421004, 0x421004, 0x421004, 0x421004, 0x421004, 0x421004, 0x421004, 0x421004, 0x421004, 0x421004,
    0xe53800, 0xe53800, 0xe53800, 0xe53800, 0xe53800, 0xe53800, 0xe53800, 0xe53800, 0xe53800, 0xe53800,
    0x423880, 0x423880, 0x423880, 0x423880, 0x423880, 0x423880, 0x423880, 0x423880, 0x423880, 0x423880,
    0x51140, 0x51140, 0x51140, 0x51140, 0x51140, 0x51140, 0x51140, 0x51140, 0x51140, 0x51140,
    0x0011111f, 0x0011111f, 0x0011111f, 0x0011111f, 0x0011111f, 0x0011111f, 0x0011111f, 0x0011111f, 0x0011111f, 0x0011111f,
    0x00e5384e, 0x00e5384e, 0x00e5384e, 0x00e5384e, 0x00e5384e, 0x00e5384e, 0x00e5384e, 0x00e5384e, 0x00e5384e, 0x00e5384e,
    0x00e5294e, 0x00e5294e, 0x00e5294e, 0x00e5294e, 0x00e5294e, 0x00e5294e, 0x00e5294e, 0x00e5294e, 0x00e5294e, 0x00e5294e,
    0xa53944, 0xa53944, 0xa53944, 0xa53944, 0xa53944, 0xa53944, 0xa53944, 0xa53944, 0xa53944, 0xa53944,
    0x928ca9, 0x928ca9, 0x928ca9, 0x928ca9, 0x928ca9, 0x928ca9, 0x928ca9, 0x928ca9, 0x928ca9, 0x928ca9,
    0x00e5394e, 0x00e5394e, 0x00e5394e, 0x00e5394e, 0x00e5394e, 0x00e5394e, 0x00e5394e, 0x00e5394e, 0x00e5394e, 0x00e5394e,
    0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173,
    0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173, 0x19d1173,
    0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea,
    0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea,
    0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea, 0xafabea
};

typedef struct rcontext {
    void           *mvBase;
    void           *oBase;
    unsigned long   mvRow;
    unsigned long   oRow;
    unsigned long   height;
    unsigned long   width;
    CGrafPtr        gw;
    PixMapHandle    pm;
    RgnHandle       rgn;
}               rcontext;

static void
realDrawCompleteProc(rcontext * rc)
{
    char           *oba = rc->oBase;
    unsigned long   orb = rc->oRow;
    
    const unsigned long height = rc->height, width = rc->width, irb = rc->mvRow,
	orbfive = orb * 5;
    unsigned long   y = height;
    char           *iba = rc->mvBase;
    while (y--) {
	unsigned long   x = width;
	unsigned long  *iba2 = (unsigned long  *) iba, *oba2;
	unsigned long  *obas = (unsigned long  *) oba;
	while (x--) {
	    unsigned long   XRGB = *iba2++;
	    
	    unsigned long   R = (XRGB & 0xFF0000) >> 16;
	    unsigned long   G = (XRGB & 0xFF00) >> 8;
	    unsigned long   B = (XRGB & 0xFF);
	    unsigned long   glyph = combined_map[(1740 * R + 5860 * G + 590 * B + 4096) >> 13];
	    
	    unsigned char   chary = 5;
	    oba2 = (unsigned long *) obas;
	    while (chary--) {
		unsigned long  *oba3 = oba2;
		unsigned char   charx = 5;
		while (charx--) {
		    *oba3++ = (glyph&1)?XRGB:0;
		    glyph >>= 1;
		}
		oba2 = (unsigned long *) (((unsigned long) oba2) + orb);
	    }
	    obas += 5;
	}
	iba += irb;
	oba += orbfive;
    }
}

/* Just so I don't have to bother with function calls in realDrawCompleteProc */
static pascal   OSErr
DrawCompleteProc(Movie theMovie, long refCon)
{
    rcontext       *rc = (rcontext *) refCon;
    CGrafPtr        gw = rc->gw;
    RgnHandle       rgn = rc->rgn;
    LockPortBits(gw);
    realDrawCompleteProc(rc);
    QDFlushPortBuffer(gw, rgn);
    UnlockPortBits(gw);
    return noErr;
}

int
main(int argc, const char *argv[])
{
    if (argc < 2) {
	puts("You forgot the file name.");
	return 1;
    } else if (argc > 2) {
	puts("What, you think this has command-line arguments or something?");
	return 1;
    }
    ProcessSerialNumber psn;
    CPSProcessSerNum PSN;
    GetCurrentProcess(&psn);
    if (!CPSGetCurrentProcess(&PSN)) {
	if (!CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103)) {
	    if (!CPSSetFrontProcess(&PSN)) {
		GetCurrentProcess(&psn);
	    }
	}
    }
    EnterMovies();
    MovieDrawingCompleteUPP dc = NewMovieDrawingCompleteUPP(DrawCompleteProc);
    
    const char     *fname = argv[1];
    FSSpec          theFSSpec;
    NativePathNameToFSSpec((char *) fname, &theFSSpec, 0);
    short           resRefNum;
    OpenMovieFile(&theFSSpec, &resRefNum, 0);
    short           actualResId = DoTheRightThing;
    Movie           theMovie;
    NewMovieFromFile(&theMovie, resRefNum, &actualResId, (unsigned char *) 0, 0, (Boolean *) 0);
    CloseMovieFile(resRefNum);
    WindowRef       theWin;
    Ptr             restoreState;
    short           wR = 0, hR = 0;
    Rect           movRect;
    GetMovieBox(theMovie,&movRect);
    BeginFullScreen(&restoreState, NULL, &wR, &hR, &theWin, NULL, fullScreenHideCursor);
    Rect            calcBestRect = {0, 0, 0, 0}, nSRect = {0, 0, hR/5.0, wR/5.0};
    {
	float mw = movRect.right - movRect.left, mh = movRect.bottom - movRect.top;
	if (mw > mh) {
	    float a = mh / mw;
	    float d = (wR/5.0) * a, hrr = hR/5.0;
	    calcBestRect.right = wR/5.0;
	    calcBestRect.bottom = d + ((hrr - d) / 2.0);
	    calcBestRect.top = (hrr - d) / 2.0;
	} else {
	    float a = mw / mh;
	    float d = (hR/5.0) * a, wrr = wR/5.0;
	    calcBestRect.bottom = hR/5.0;
	    calcBestRect.right = d + ((wrr - d) / 2.0);
	    calcBestRect.left = (wrr - d) / 2.0;
	}
    }
    Rect screenRect;
    rcontext        rc = {NULL, NULL, 0, 0, nSRect.bottom, nSRect.right, GetWindowPort(theWin), 0, NewRgn()};
    rc.pm = GetPortPixMap(rc.gw);
    GetPortBounds(rc.gw, &screenRect);
    RectRgn(rc.rgn, &screenRect);
    GWorldPtr       movieGW;
    QTNewGWorld(&movieGW, k32ARGBPixelFormat, &nSRect, NULL, NULL, 0);
    PixMapHandle    gwp = GetGWorldPixMap(movieGW);
    //GetPixBounds(gwp, &calcBestRect);
    SetMovieBox(theMovie, &calcBestRect);
    LockPixels(gwp);
    LockPixels(rc.pm);
    LockPortBits(rc.gw);
    rc.oBase = GetPixBaseAddr(rc.pm);
    rc.oRow = GetPixRowBytes(rc.pm);
    UnlockPortBits(rc.gw);
    SetGWorld(movieGW, NULL);
    rc.mvBase = GetPixBaseAddr(gwp);
    rc.mvRow = GetPixRowBytes(gwp);
    SetMovieGWorld(theMovie, movieGW, NULL);
    SetMovieDrawingCompleteProc(theMovie, movieDrawingCallWhenChanged, dc, (long) &rc);
    StartMovie(theMovie);
    MoviesTask(theMovie,0);
    MoviesTask(theMovie,0);
    MoviesTask(theMovie,0);
    while (1) {
	MoviesTask(theMovie, 0);
	if (IsMovieDone(theMovie)) break;
        long t;
	QTGetTimeUntilNextTask(&t,60);
	EventRecord     er;
	WaitNextEvent(keyDownMask, &er, (t>5)?t-5:0, NULL);
	if (er.what == keyDown)
	    break;
    }
    UnlockPixels(gwp);
    UnlockPixels(rc.pm);
    EndFullScreen(restoreState, 0);
    StopMovie(theMovie);
    DisposeMovie(theMovie);
    DisposeMovieDrawingCompleteUPP(dc);
    DisposeGWorld(movieGW);
    return 0;
}
