

import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;

import java.awt.image.ImageObserver;

public class InternetIncome extends BApplet {
// The Only Way to Make a Descent Living on the Internet
// or The Rights of Spring
// Reed Kram

// Created    	October 2002
// Revised		November 2002

class particle {
    float x, y, z;
    float Vx, Vy, Vz;
    float Ax, Ay, Az;
    float mass, drag;

    particle(float theMass, float theDrag, float startX,
    float startY, float startZ) {
        x = startX;
        y = startY;
        z = startZ;
        Vx = 0.0f;
        Vy = 0.0f;
        Vz = 0.0f;
        Ax = 0.0f;
        Ay = 0.0f;
        Az = 0.0f;
        mass = theMass;
        drag = theDrag;
    }

    // iterate for a single force acting on the particle
    void iterate(float Fkx, float Fky, float Fkz) {
        float Fx, Fy, Fz;

        Fx = Fkx;
        Fy = Fky;
        Fz = Fkz;
        Ax=Fx/mass;
        Ay=Fy/mass;
        Az=Fz/mass;

        Vx += Ax;
        Vy += Ay;
        Vz += Az;
        Vx *= (1.0f-drag);
        Vy *= (1.0f-drag);
        Vz *= (1.0f-drag);

        x += Vx;
        y += Vy;
        z += Vz;
    }
}

class imageDownloader implements Runnable {
	Vector imagesToDownload;
	Vector inbox;
	Thread runner;
	BApplet parent;

	public imageDownloader(BApplet parent) {
		this.parent = parent;
		imagesToDownload = new Vector();
		inbox = new Vector();
		
		runner = new Thread(this);
	}

	public void downloadFile(String filename) {
		imagesToDownload.addElement(filename);
		if (!runner.isAlive()) {
			runner.start();
		}
	}
	
	public Vector fetchDownloadedImages() {
		Vector theInbox = (Vector) inbox.clone();
		if (inbox != null) {
			inbox.removeAllElements();
		}
		if (imagesToDownload.size() < 1 && runner.isAlive()) {
			runner.stop();  // done downloading files...
		}
		return theInbox;
	}
	
	public void run() {
		Toolkit tk = Toolkit.getDefaultToolkit();
		Image imageToDownload = null;
		while(true) {
			try {
				if ((imageToDownload != null) && ((tk.checkImage(imageToDownload, -1, -1, null) & ImageObserver.ALLBITS) != 0)) {  // the image has downloaded fully
					int imageWidth = imageToDownload.getWidth(null);
					for (int i=0; i<imageToDownload.getHeight(null) / imageWidth; i++) {
						Image newImage = parent.createImage(imageWidth, imageWidth);
						if (newImage == null) {
							System.out.println("unable to allocate offscreen graphics. need more memory.");
							break;
						}
        				Graphics newImageGraphics = newImage.getGraphics();
						newImageGraphics.drawImage(imageToDownload, 0, 0, 16, 16, 0, i*imageWidth, 16, (i+1)*imageWidth -1, null);
						inbox.addElement(newImage);
					}
					imageToDownload = null;
				}
				Thread.sleep(200);
				if (imageToDownload == null && imagesToDownload.size() > 0) {
					String imageFilename = (String) imagesToDownload.elementAt(0);
					imagesToDownload.removeElementAt(0);
					imageToDownload = tk.getImage(getClass().getResource(imageFilename));
					tk.prepareImage(imageToDownload, -1, -1, null);
				}
			} catch (Exception e) {
				System.out.println("downloading image >>>>"+e.toString()); e.printStackTrace(System.out);
			}
		}
	}
}

String logo[] = {"**",
    			 "**",
    			 "**",
    			 "**  ** ** ** ****  ****** ***",
    			 "** **  *****    ** **   **  **",
    			 "****   **    ***** **   **  **",
    			 "** **  **   **  ** **   **  **",
    			 "**  ** **    ***** **   **  **"};

static int MAX_NUM_IMAGES = 300;
static int NUM_IMAGE_FILES = 16;
imageDownloader theDownloader;
Vector frameImages = new Vector();
static int frameImageWidth = 16;
static int frameImageHeight = 16;
particle pixelParticles[][] = new particle[frameImageWidth][frameImageHeight];

int framePixels[] = new int[frameImageWidth * frameImageHeight];
Image frameOffImage;
Graphics frameOffGraphics;
PixelGrabber framePixelGrabber;

int numImagesLoaded = 0;
int thisImageIndex = 0;
long lastFrameTime = 0;

particle cameraRotation = new particle(3, 0.7f, PI, 1.0f, 1.0f);
float cameraRotationDestination[] = { 0.0f, 0.0f, 0.0f };

long creditTextTimer;

void setup()
{
    size(300, 300);
    background(49, 81, 74);
    noStroke();
    colorMode(RGB, 100);

    for (int i=0; i<frameImageWidth; i++) {
        for (int j=0; j<frameImageHeight; j++) {
            pixelParticles[i][j] = new particle(4.0f, 0.8f, 0.0f, 0.0f, 0.0f);
        }
    }

    creditTextTimer = millis();
}

void mousePressed() {
    cameraRotationDestination[0] = (mouseX - width/2) / 2.0f * (PI/180.0f);
    cameraRotationDestination[1] = (mouseY - height/2) / 2.0f * (PI/180.0f);
    cameraRotationDestination[2] = 100;
}

void mouseDragged() {
    cameraRotationDestination[0] = (mouseX - width/2) / 2.0f * (PI/180.0f);
    cameraRotationDestination[1] = (mouseY - height/2) / 2.0f * (PI/180.0f);
    cameraRotationDestination[2] = 100;
}

void mouseReleased() {
    cameraRotationDestination[0] = 0;
    cameraRotationDestination[1] = 0;
    cameraRotationDestination[2] = 0;
}

void drawBoxTop(float x1, float x2,
                                float y1, float y2,
                                float z1, float z2) {
    beginShape(QUADS);
    vertex(x1, y2, z2); // draw front
    vertex(x1, y1, z2);
    vertex(x2, y1, z2);
    vertex(x2, y2, z2);
    vertex(x2, y2, z2); // draw right side
    vertex(x2, y1, z2);
    vertex(x2, y1, z1);
    vertex(x2, y2, z1);
    vertex(x2, y2, z1); // draw back
    vertex(x2, y1, z1);
    vertex(x1, y1, z1);
    vertex(x1, y2, z1);
    vertex(x1, y2, z1); // draw left side
    vertex(x1, y1, z1);
    vertex(x1, y1, z2);
    vertex(x1, y2, z2);
    endShape();
}

static Color
getPixelColor(int pixels[], int X, int Y, int w) {
	int pixel = pixels[Y*w + X];

	int alpha = (pixel >> 24) & 0xff;
	int red   = (pixel >> 16) & 0xff;
	int green = (pixel >>  8) & 0xff;
	int blue  = (pixel      ) & 0xff;

	return new Color(red, green, blue);
}

void drawLogo(int x, int y) {
	int c = color(100, 100, 100);
	for (int i=0; i<logo.length; i++) {
		String thisLine = logo[i];
		for (int j=max(0, -x); j<thisLine.length(); j++) {
			if (thisLine.charAt(j) != ' ') {
				setPixel(x + j, y + i, c);
			}
		}
	}
}

void loop()
{
	if (frameOffImage == null || frameOffGraphics == null) {
    	theDownloader = new imageDownloader(this);
		for (int i=0; i<NUM_IMAGE_FILES; i++) {
			theDownloader.downloadFile("media/images"+i+".jpg");
		}
		
		frameOffImage = createImage(frameImageWidth, frameImageHeight);
		frameOffGraphics = frameOffImage.getGraphics();
    	creditTextTimer = millis();
	}

	cameraRotation.iterate(cameraRotationDestination[0] - cameraRotation.x,
						   cameraRotationDestination[1] - cameraRotation.y,
						   cameraRotationDestination[2] - cameraRotation.z);

    long curTime = millis();

    Vector downloadedImages = theDownloader.fetchDownloadedImages();
    if (downloadedImages != null) {
    	for (int i=0; i<downloadedImages.size(); i++) {
    		frameImages.addElement(downloadedImages.elementAt(i));
    	}
    }
    if (curTime - lastFrameTime > 450) {
        if ((thisImageIndex < MAX_NUM_IMAGES-1) && (frameImages.size() > thisImageIndex+1) && ((checkImage((Image) frameImages.elementAt(thisImageIndex+1), null) & ImageObserver.ALLBITS) != 0)) {
            thisImageIndex++;
        } else {
            thisImageIndex = 0;
        }
        lastFrameTime = curTime;
    }

    if (frameImages.size() < 1 || !((checkImage((Image) frameImages.elementAt(thisImageIndex), null) & ImageObserver.ALLBITS) != 0)) {
    	return;
    }
   	frameOffGraphics.drawImage((Image) frameImages.elementAt(thisImageIndex), 0, 0, null);

	framePixelGrabber = new PixelGrabber(frameOffImage, 0, 0, frameImageWidth, frameImageHeight, framePixels, 0, frameImageWidth);
	try {
		framePixelGrabber.grabPixels();
	} catch (InterruptedException e) {
		System.err.println("interrupted waiting for pixels!");
		return;
	} catch (Exception e) { System.out.println("grabbing pixels>>>>" + e.toString()); e.printStackTrace(System.out); }
	if ((framePixelGrabber.getStatus() & ImageObserver.ABORT) != 0) {
		System.err.println("image fetch aborted or errored");
		return;
	}

    for (int X = 0; X < frameImageWidth-1; X++) {
        for (int Y = 0; Y < frameImageHeight-1; Y++) {
            Color pixelColor = getPixelColor(framePixels, X, Y, frameImageWidth);
            pixelParticles[X][Y].iterate(
                pixelColor.getRed()*100/255 - pixelParticles[X][Y].x,
                pixelColor.getGreen()*100/255 - pixelParticles[X][Y].y,
                pixelColor.getBlue()*100/255 - pixelParticles[X][Y].z);
        }
    }

    push();

    translate(width/2, height/2, 0);

    rotateY( cameraRotation.x);
    rotateX(-cameraRotation.y);

    float xOffset = frameImageWidth/2.0f -1;
    float yOffset = frameImageHeight/2.0f -1;
    float scale = 4.0f;
	for (int X = 0; X < frameImageWidth-1; X++) {
        for (int Y = 0; Y < frameImageHeight-1; Y++) {
            float x = 3*(X - xOffset);
            float y = 3*(Y - yOffset);
            int pixelColor = color(pixelParticles[X][Y].x, pixelParticles[X][Y].y, pixelParticles[X][Y].z);
            float z = red(pixelColor)/6.0f;

            push();
            translate(scale*x, scale*y);
            fill(red(pixelColor), green(pixelColor), blue(pixelColor));
            drawBoxTop(-scale, scale,
                                -scale, scale,
                                -scale*z, scale*z);
            pop();
        }
    }
    pop();
    
	if (curTime - creditTextTimer <= 15000) {
		drawLogo(5, height-5 -8);
    } else if (curTime - creditTextTimer < 18000) {
    	drawLogo((int)(5 - 60 * (curTime - creditTextTimer - 15000)/700.0), height-5 -8);
    }
}


}
