
public class AltDemo : GLib.Object {

    private int w;
    private int h;
    private static uint8[,] testMap = {{1, 10, 10, 1},
    {10, 10, 10, 10},
    {40, 80, 80, 40},
    {30, 140, 120, 30}};
    private uint8 *landscape;
    private uint8[,] heightMap;
    private uint8[,] palette;
    int landscapeW;
    int landscapeH;
    const int mapW = 100;
    const int mapH = 100;
    double cameraX;
    double cameraY;
    double cameraZ;
    int screenW;
    int screenH;
    uint8 *tex;
    int leftmostProcessedColumn;

    public AltDemo(int wid, int hei) {
        w = wid;
        h = hei;
        cameraX = mapW/2.0;
        cameraY = 300;
        cameraZ = -2;
        screenW = 256;
        screenH = 256;
        heightMap = new uint8[mapH, mapW];
	palette = new uint8[256, 3];
	initPalette();
    }

    private void initPalette() {
        int i;
	for (i=0; i<256; i++) {
	  palette[i, 0] = (uint8)(132*i/255.0 + 154*(255-i)/255.0);
	  palette[i, 1] = (uint8)(255*i/255.0 + 0*(255-i)/255.0);
	  palette[i, 2] = (uint8)(240*i/255.0 + 225*(255-i)/255.0);
	}
	palette[0, 0] = palette[0, 1] = palette[0, 2] = 0;
    }
    
    private void drawPoint(int x, int y, uint8 c) {
        if (x < 0 || x >= screenW || y < 0 || y >= screenH)
          return;
        uint8 *dst = tex + (y*screenW + x)*4;
        dst[0] = palette[c, 0];
        dst[1] = palette[c, 1];
        dst[2] = palette[c, 2];
    }

    private void drawColumn(int x, int top, int cutoff, uint8 c) {
        int y;
      
        //stdout.printf("Column. X:%d top:%d cutoff:%d color:%d.\n", x, top, cutoff, (int)c);
        if (cutoff < 0) {
            cutoff = 0;
        }
      
        for (y = top; y<cutoff; y++) {
            drawPoint(x, y, c);
        }
    }
    
    private void castSingleRay(double x, double deltaX, double deltaZ) {
        double step = 0.05;
        double cx;
        double cz;
        double cy;
        int screenX;
        cx = x;
        cz = 0;
        assert(deltaZ > 0);
        double tmp1, tmp2;
        int bottom;
        int highestDrawn; // In screen coordinates, so grows downwards.
        
        deltaX *= step;
        deltaZ *= step;
        project(x, 0, 0, out tmp1, out tmp2);
        if (tmp2 <= 0.0)
            bottom = 0;
        else
            bottom = (int)tmp2;
        highestDrawn = bottom;
        
        screenX = (int)tmp1;
        if (screenX < 0 || screenX >= screenW) {
            return;
        }
        // Only process every vertical row of pixels once.
        if (screenX <= leftmostProcessedColumn) {
            //stdout.printf("Duplicate line.\n");
            return;
        }
        assert(screenX == leftmostProcessedColumn + 1);
        //stdout.printf("Processing line %d\n", screenX);
        leftmostProcessedColumn = screenX;
        while (cx >= 0 && cx < mapW && cz >= 0 && cz < mapH) {
            //stdout.printf("Stepping at (%f, %f).\n", cx, cz);
            double screenY;
            int currentHeight;
            cy = heightMap[(int)cz, (int)cx];
            project(cx, cy*2, cz, out tmp1, out screenY);
            //stdout.printf("Ass: %f, %f.\n", tmp1, screenX);
            //assert((int)tmp1 == (int)screenX);
            currentHeight = (int) screenY;

            //stdout.printf("Height: %d HighestDrawn: %d.\n", currentHeight, highestDrawn);
            if (currentHeight < highestDrawn) {
                drawColumn(screenX, currentHeight, highestDrawn, heightMap[(int)cz, (int)cx]);
                highestDrawn = currentHeight;
            }
            if (currentHeight >= screenH) {
                return;
            }
            // Go a bit further.
            cx += deltaX;
            cz += deltaZ;
       }
        
    }

    private void rayCast() {
        int subdivisions = 768;
        double deltaX;
        double deltaZ;
        int i;
        
        deltaZ = (-cameraZ);
        for (i=0; i< subdivisions; i++) {
            double currentX = mapW/((double)subdivisions)*i;
            deltaX = currentX - cameraX;
            //stdout.printf("Casting: currentX:%f deltaX:%f deltaZ:%f\n", currentX, deltaX, deltaZ); 
            castSingleRay(currentX, deltaX, deltaZ);
        }
    }
    
    private void project(double x, double y, double z, out double screenX, out double screenY) {
        double projectedX;
        double projectedY;
        double shiftedX;
        double shiftedY;
        double shiftedZ;
        shiftedX = x - cameraX;
        shiftedY = y - cameraY;
        shiftedZ = z - cameraZ;
                
        projectedX = shiftedX/shiftedZ;
        projectedY = shiftedY/shiftedZ;
                
        screenX = projectedX*10 + screenW/2;
        screenY = -projectedY + screenH/2;
    
    }
        
    private void plotTest() {
        int i;
        int j;
        for (j=0; j<mapH; j++) {
            for (i=0; i<mapH; i++) {
                double x = i;
                double y = heightMap[j, i];
                double z = j;
                double screenX;
                double screenY;
                project(x, y, z, out screenX, out screenY);
                drawPoint((int)screenX, (int)screenY, 255);//heightMap[j, i]);
            }
        }
    }
    
    private void buildHeightMap(int num) {
        assert(num < landscapeH);
        assert(num >= 0);
        //stdout.printf("Num: %d\n", num);
        //stdout.printf("MapW %d landscapeW %d.\n", mapW, landscapeW);
        assert(mapW == landscapeW);
        for (int j=0; j<mapH; j++) {
            for (int i=0; i<mapW; i++) {
                heightMap[j, i] = landscape[((j+num) % landscapeH)*landscapeW + i];
            }
        }
    }

    private void setupWorld(int time) {
        int maxTime = 30000;
        int fractionTime = maxTime / landscapeH;
        int wrappedTime = time % maxTime;
        int whole = (wrappedTime) / fractionTime;
        double fraction = (wrappedTime - whole*fractionTime)/((double)fractionTime);
        assert(fraction <= 1.0);
        assert(fraction >= -1.0);
        cameraZ = -3.0;// + fraction;
        buildHeightMap(whole);
        leftmostProcessedColumn = -1;
    }
    
    public void setLandscape(int w, int h, uint8 *land) {
        landscapeW = w;
        landscapeH = h;
        landscape = land;
    }

    private void hmapTest() {
        for (int j=0; j<mapH; j++) {
            for (int i=0; i<mapW; i++) {
                drawPoint(i, j, heightMap[j, i]);
            }
        }
    }

    public void render(uint8 *t, uint32 time) {
        tex = t;
        setupWorld((int)time);
        rayCast();
        //plotTest();
        //hmapTest();
        //assert(0 == 1);
    }
}
