/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.pepsoft.worldpainter.themes.impl.fancy;

import java.util.HashSet;
import java.util.Random;
import javax.vecmath.Point3i;
import org.pepsoft.util.undo.UndoManager;
import org.pepsoft.worldpainter.Dimension;
import org.pepsoft.worldpainter.Tile;
import static org.pepsoft.worldpainter.Constants.*;
import org.pepsoft.worldpainter.gardenofeden.Garden;
import org.pepsoft.worldpainter.gardenofeden.Seed;

/**
 *
 * @author pepijn
 */
public class RiverGenerator {
    public RiverGenerator(Dimension dimension) {
        this.dimension = dimension;
    }
    

    public void generateRivers() {
        dimension.setEventsInhibited(true);
        UndoManager undoManager = new UndoManager(2);
        dimension.register(undoManager);
        snapshot = dimension.getSnapshot();
        dimension.armSavePoint();
        garden = dimension.getGarden();
        try {
            for (Tile tile: dimension.getTiles()) {
                generateRivers(tile);
            }
            
            // Grow seeds until all activity has ceased
            while (! garden.tick());
            
            // Apply the river nodes to the landscape
            for (Seed seed: dimension.getGarden().getSeeds()) {
                if ((seed instanceof RiverNode) && (seed.getParent() == null)) {
                    // A river source
                    ((RiverNode) seed).apply(dimension, snapshot, new HashSet<RiverNode>());
                }
            }
        } finally {
            garden = null;
            snapshot = null;
            dimension.unregister();
            dimension.setEventsInhibited(false);
        }
    }
    
    public void generateRivers(Tile tile) {
        long seed = dimension.getSeed() + tile.getX() * 65537 + tile.getY();
        Random random = new Random(seed);
        for (int x = 0; x < TILE_SIZE; x++) {
            for (int y = 0; y < TILE_SIZE; y++) {
                if (random.nextInt(1000) == 0) {
                    generateRiver((tile.getX() << TILE_SIZE_BITS) | x, (tile.getY() << TILE_SIZE_BITS) | y);
                }
            }
        }
    }

    public void generateRiver(int x, int y) {
//        System.out.println("Start coordinates: " + x + ", " + y);
        int waterLevel = snapshot.getWaterLevelAt(x, y);
        float height = snapshot.getHeightAt(x, y);
        int intHeight = (int) (height + 0.5f);
        if (waterLevel > intHeight) {
            // Already flooded
        } else {
            garden.plantSeed(new RiverNode(garden, new Point3i(x, y, -1), 1));
        }
//        while (true) {
//            if (waterLevel > intHeight) {
//                // Already flooded
////                System.out.println("Already flooded");
//                return;
//            } else {
//                int lowestSurroundingDryHeight = getLowestSurroundingDryHeight(x, y);
//                if (lowestSurroundingDryHeight == 0) {
//                    // At bedrock; can't go any deeper
////                    System.out.println("Lowest surrounding dry block is at level 0");
//                    return;
//                } else if (lowestSurroundingDryHeight == Integer.MAX_VALUE) {
//                    // This means that all surrounding blocks are flooded.
//                    dimension.setWaterLevelAt(x, y, getLowestSurroundingWaterLevel(x, y));
//                    return;
//                } else {
//                    dimension.setHeightAt(x, y, lowestSurroundingDryHeight - 1);
//                    dimension.setWaterLevelAt(x, y, lowestSurroundingDryHeight);
//                }
//            }
//            int nextX = 0, nextY = 0;
//            float lowestSurroundingHeight = height;
//            for (int dx = -2; dx <= 2; dx++) {
//                for (int dy = -2; dy <= 2; dy++) {
//                    if ((dx != 0) || (dy != 0)) {
//                        float surroundingHeight = snapshot.getHeightAt(x + dx, y + dy);
//                        if (surroundingHeight < lowestSurroundingHeight) {
//                            lowestSurroundingHeight = surroundingHeight;
//                            nextX = x + dx;
//                            nextY = y + dy;
//                        }
//                    }
//                }
//            }
//            if (lowestSurroundingHeight == height) {
//                // No lower block found; end of river
//                // TODO: start a lake?
////                System.out.println("No lower surrounding block found");
//                return;
//            }
//            x = nextX;
//            y = nextY;
////            System.out.println("Next coordinates: " + x + ", " + y);
//            waterLevel = snapshot.getWaterLevelAt(x, y);
//            height = snapshot.getHeightAt(x, y);
//            intHeight = (int) (height + 0.5f);
//        }
    }
    
    public int getLowestSurroundingDryHeight(int x, int y) {
        int lowestSurroundingDryHeight = Integer.MAX_VALUE;
        for (int dx = -1; dx <= 1; dx++) {
            for (int dy = -1; dy <= 1; dy++) {
                if ((dx != 0) || (dy != 0)) {
                    int height = snapshot.getIntHeightAt(x + dx, y + dy);
                    if ((height >= snapshot.getWaterLevelAt(x + dx, y + dy)) && (height < lowestSurroundingDryHeight)) {
                        if (height == 0) {
                            return 0;
                        } else {
                            lowestSurroundingDryHeight = height;
                        }
                    }
                }
            }
        }
        return lowestSurroundingDryHeight;
    }
 
    public int getLowestSurroundingWaterLevel(int x, int y) {
        int lowestSurroundingWaterLevel = Integer.MAX_VALUE;
        for (int dx = -1; dx <= 1; dx++) {
            for (int dy = -1; dy <= 1; dy++) {
                if ((dx != 0) || (dy != 0)) {
                    int waterLevel = snapshot.getWaterLevelAt(x + dx, y + dy);
                    if ((waterLevel > snapshot.getIntHeightAt(x + dx, y + dy)) && (waterLevel < lowestSurroundingWaterLevel)) {
                        if (waterLevel == 0) {
                            return 0;
                        } else {
                            lowestSurroundingWaterLevel = waterLevel;
                        }
                    }
                }
            }
        }
        return lowestSurroundingWaterLevel;
    }
    
    private final Dimension dimension;
    private Dimension snapshot;
    private Garden garden;
}