/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package csa.tiletools.worldgen.worldgen;

import csa.gui.ImageCache;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 *
 * @author salchr
 */
public class WorldGen {



/* Fractal Worldmap Generator Version 2.1
 *
 * Creator: John Olsson
 * Thanks to Carl Burke for interesting discussions and suggestions of
 * how to speed up the generation! :)
 *
 * Please visit my WWW-pages located at: http://www.edu.isy.liu.se/~d91johol/
 * You can send E-Mail to this adress: d91johol@isy.liu.se
 *
 * I compile this program with: gcc -O3 gengif.c -lm -o gengif
 *
 * This program will write the GIF-file to stdout, so you need to
 * either redirect the output (EG. gengif > foo.gif) or change
 * the program... :)
 *
 * To change sixe of the generated picture, change the default values
 * of the variables XRange och YRange.
 *
 * You use this program at your own risk! :)
 *
 *
 * When you run the program you are prompted to input three values:
 *
 * Seed:             This the "seed" used to initialize the random number
 *                   generator. So if you use the same seed, you'll get the
 *                   same sequence of random numbers...
 *
 * Number of faults: This is how many iterations the program will do.
 *                   If you want to know how it works, just enter 1, 2, 3,...
 *                   etc. number of iterations and compare the different
 *                   GIF-files.
 *
 * PercentWater:          This should be a value between 0 and 100 (you can
 *                   input 1000 also, but I don't know what the program
 *                   is up to then! :) The number tells the "ratio"
 *                   between water and land. If you want a world with
 *                   just a few islands, input a large value (EG. 80 or
 *                   above), if you want a world with nearly no oceans,
 *                   a value near 0 would do that.
 *
 */

/*
 * Very minor adaptations to make it work under DOS (386+)
 * by Martijn Faassen (faassen@phil.ruu.nl).
 * All credit goes to the original creator, however.
 * - Martijn
 */

/*
 * Very Very Very Very (And I mean it) minor adaptions to make it compile
 * under OS/2 Warp by Nils Jeppe (Nils.Jeppe@hamburg.netsurf.de)
 * All credit goes to the other two dudes, however ;)
 *
 * - Nils
 *
 */



    int[]           WorldMapArray;
    int[]           Histogram = new int[256];
    int             FilledPixels;
    int[]           Red   = {0,0,0,0,0,0,0,0,34,68,102,119,136,153,170,187,
                                 0,34,34,119,187,255,238,221,204,187,170,153,
                                 136,119,85,68,
                                 255,250,245,240,235,230,225,220,215,210,205,200,
                                 195,190,185,180,175};
    int[]           Green = {0,0,17,51,85,119,153,204,221,238,255,255,255,
                                 255,255,255,68,102,136,170,221,187,170,136,
                                 136,102,85,85,68,51,51,34,
                                 255,250,245,240,235,230,225,220,215,210,205,200,
                                 195,190,185,180,175};
    int[]           Blue  = {0,68,102,136,170,187,221,255,255,255,255,255,
                                 255,255,255,255,0,0,0,0,0,34,34,34,34,34,34,
                                 34,34,34,17,0,
                                 255,250,245,240,235,230,225,220,215,210,205,200,
                                 195,190,185,180,175};

    float           YRangeDiv2, YRangeDivPI;
    float[]         SinIterPhi;

    Random rand =new Random();
    boolean generated = false;
    public WorldGen()
    {
    }

    int mapColor(int pixel)
    {
        return pixel;
    }

    public BufferedImage getImage()
    {
        if (!generated) return null;

        final BufferedImage tmpImg = new BufferedImage(XRange, YRange, BufferedImage.TYPE_INT_RGB);
        final Graphics2D g = tmpImg.createGraphics();

        int j=0;
        int row = 0;
        int i=0;
        for (j=0; j<XRange; j++)
        {
          for (i=0; i<YRange; i++)
          {
            int c = WorldMapArray[row+i];
            Color col=new Color(Red[c], Green[c], Blue[c]);

            g.setColor(col);
            g.drawLine(i, j, i, j);
          }
          row += YRange;
        }

        g.dispose();






        int w = tmpImg.getHeight();
        int h = tmpImg.getWidth();

        BufferedImage newImage;

        newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D go = newImage.createGraphics();

        for (int y=0; y<h; y++)
        {
            for (int x=0; x<w; x++)
            {
                newImage.setRGB(x, y, tmpImg.getRGB(y, x));
            }
        }
        return newImage;

    }

    public long       Seed=50;
    public int       PercentWater =20;
    public int       PercentIce = 10;
    public int       XRange = 320;
    public int       YRange = 320;
    public int       NumberOfFaults = 1000;

    public void generate()
    {
      int       a, j, i, Color, MaxZ=1, MinZ=-1;
      int       row, TwoColorMode=0;
      int       index2;
      int       Threshold, Count;
      int       Cur;
      generated = false;


      WorldMapArray = new int[XRange*YRange];
      SinIterPhi = new float[2*XRange];

      for (i=0; i<XRange; i++)
      {
            SinIterPhi[i] = SinIterPhi[i+XRange] = (float)Math.sin(i*2*Math.PI/XRange);
      }
      rand.setSeed(Seed);

      for (j=0, row=0; j<XRange; j++)
      {
        WorldMapArray[row] = 0;
        for (i=1; i<YRange; i++)
            WorldMapArray[i+row] = Integer.MIN_VALUE;
        row += YRange;
      }

      /* Define some "constants" which we use frequently */
      YRangeDiv2  = YRange/2;
      YRangeDivPI = (float)(YRange/Math.PI);

      /* Generate the map! */
      for (a=0; a<NumberOfFaults; a++)
      {
        GenerateWorldMap();
      }

      /* Copy data (I have only calculated faults for 1/2 the image.
       * I can do this due to symmetry... :) */
      index2 = (XRange/2)*YRange;
      for (j=0, row=0; j<XRange/2; j++)
      {
        for (i=0; i<YRange; i++)
        {
          if (row+index2+YRange-i >= YRange*XRange)
          {
              System.out.println("RangeError1");
              continue;
          }
          WorldMapArray[row+index2+YRange-i] = WorldMapArray[row+i];
        }
        row += YRange;
      }

      /* Reconstruct the real WorldMap from the WorldMapArray and FaultArray */
      for (j=0, row=0; j<XRange; j++)
      {
        /* We have to start somewhere, and the top row was initialized to 0,
         * but it might have changed during the iterations... */
        Color = WorldMapArray[row];
        for (i=1; i<YRange; i++)
        {
          /* We "fill" all positions with values != INT_MIN with Color */
          Cur = WorldMapArray[row+i];
          if (Cur != Integer.MIN_VALUE)
          {
            Color += Cur;
          }
          WorldMapArray[row+i] = Color;
        }
        row += YRange;
      }

      /* Compute MAX and MIN values in WorldMapArray */
      for (j=0; j<XRange*YRange; j++)
      {
        Color = WorldMapArray[j];
        if (Color > MaxZ) MaxZ = Color;
        if (Color < MinZ) MinZ = Color;
      }

      /* Compute color-histogram of WorldMapArray.
       * This histogram is a very crude aproximation, since all pixels are
       * considered of the same size... I will try to change this in a
       * later version of this program. */
      for (j=0, row=0; j<XRange; j++)
      {
        for (i=0; i<YRange; i++)
        {
          Color = WorldMapArray[row+i];
          Color = (int)(((float)(Color - MinZ + 1) / (float)(MaxZ-MinZ+1))*30)+1;
          Histogram[Color]++;
        }
        row += YRange;
      }

      /* Threshold now holds how many pixels PercentWater means */
      Threshold = PercentWater*XRange*YRange/100;

      /* "Integrate" the histogram to decide where to put sea-level */
      for (j=0, Count=0;j<256;j++)
      {
        Count += Histogram[j];
        if (Count > Threshold) break;
      }

      /* Threshold now holds where sea-level is */
      Threshold = j*(MaxZ - MinZ + 1)/30 + MinZ;

      {
        /* Scale WorldMapArray to colorrange in a way that gives you
         * a certain Ocean/Land ratio */
        for (j=0, row=0; j<XRange; j++)
        {
          for (i=0; i<YRange; i++)
          {
            Color = WorldMapArray[row+i];

            if (Color < Threshold)
              Color = (int)(((float)(Color - MinZ) / (float)(Threshold - MinZ))*15)+1;
            else
              Color = (int)(((float)(Color - Threshold) / (float)(MaxZ - Threshold))*15)+16;

            /* Just in case... I DON't want the GIF-saver to flip out! :) */
            if (Color < 1) Color=1;
            if (Color > 255) Color=31;
            WorldMapArray[row+i] = Color;
          }
          row += YRange;
        }

        /* "Recycle" Threshold variable, and, eh, the variable still has something
         * like the same meaning... :) */
        Threshold = PercentIce*XRange*YRange/100;

        if ((Threshold <= 0) || (Threshold > XRange*YRange))
        {
            generated = true;
            return;
        }

        FilledPixels = 0;
        /* i==y, j==x */
        Boolean done = false;
        for (i=0; i<YRange; i++)
        {
          for (j=0, row=0; j<XRange; j++)
          {
            Color = WorldMapArray[row+i];
            if (Color < 32) FloodFill4(j,i,Color);
            /* FilledPixels is a global variable which FloodFill4 modifies...
             * I know it's ugly, but as it is now, this is a hack! :)
             */
            if (FilledPixels > Threshold)
            {
                done = true;
                break;
            }
            row += YRange;
          }
          if (done) break;
        }

        FilledPixels=0;
        /* i==y, j==x */
        for (i=YRange; i>0; i--)
        {
          for (j=0, row=0; j<XRange; j++)
          {
            if (row+i>=XRange*YRange)
            {
                System.out.println("RangeError2");
                continue;
            }
            Color = WorldMapArray[row+i];
            if (Color < 32) FloodFill4(j,i,Color);
            /* FilledPixels is a global variable which FloodFill4 modifies...
             * I know it's ugly, but as it is now, this is a hack! :)
             */
            if (FilledPixels > Threshold)
            {
                generated = true;
                return;
            }
            row += YRange;
          }
        }
      }
    generated = true;
    }

    void FloodFill4(int x, int y, int OldColor)
    {
       if (x*YRange+y>=(YRange*XRange))
       {
           System.out.println("Fillerror");
           return;
       }
      if (WorldMapArray[x*YRange+y] == OldColor)
      {
        if (WorldMapArray[x*YRange+y] < 16)
          WorldMapArray[x*YRange+y] = 32;
        else
          WorldMapArray[x*YRange+y] += 17;

        FilledPixels++;
        if (y-1 > 0)      FloodFill4(  x, y-1, OldColor);
        if (y+1 < YRange) FloodFill4(  x, y+1, OldColor);
        if (x-1 < 0)
          FloodFill4(XRange, y, OldColor);
        else
          FloodFill4(   x-1, y, OldColor);

        if (x+1 > XRange)
          FloodFill4(     0, y, OldColor);
        else
          FloodFill4(   x+1, y, OldColor);
      }
    }

    void GenerateWorldMap()
    {
      float         Alpha, Beta;
      float         TanB;
      float         Result, Delta;
      int           i, row, N2;
      int           Theta, Phi, Xsi;
      boolean           flag1, flag2;


      /* I have to do this because of a bug in rand() in Solaris 1...
       * Here's what the man-page says:
       *
       * "The low bits of the numbers generated are not  very  random;
       *  use  the  middle  bits.  In particular the lowest bit alter-
       *  nates between 0 and 1."
       *
       * So I can't optimize this, but you might if you don't have the
       * same bug... */
      flag1 = rand.nextBoolean();
      flag2 = rand.nextBoolean();

      /* Create a random greatcircle...
       * Start with an equator and rotate it */
      Alpha = (float)(rand.nextFloat()*Math.PI);
      Beta = (float)(rand.nextFloat()*Math.PI);
      //Alpha = (((float) rand())/MAX_RAND-0.5)*PI; /* Rotate around x-axis */
      //Beta  = (((float) rand())/MAX_RAND-0.5)*PI; /* Rotate around y-axis */

      TanB  = (float)( Math.tan(Math.acos(Math.cos(Alpha)*Math.cos(Beta))));

      row  = 0;
      Xsi  = (int)(XRange/2-(XRange/Math.PI)*Beta);

      for (Phi=0; Phi<XRange/2; Phi++)
      {
        //Theta = (int)(YRangeDivPI*atan(*(SinIterPhi+Xsi-Phi+XRange)*TanB))+YRangeDiv2;
        Theta = (int)((int)(YRangeDivPI * ((float)Math.atan(SinIterPhi[Xsi-Phi+XRange]*TanB)) )+YRangeDiv2);
        if (flag1)
        {
          if (flag2)
          { /* Rise northen hemisphere <=> lower southern */
            if (WorldMapArray[row+Theta] != Integer.MIN_VALUE)
              WorldMapArray[row+Theta]--;
            else
              WorldMapArray[row+Theta] = -1;
          }
          else
          { /* Rise southern hemisphere */
            if (WorldMapArray[row+Theta] != Integer.MIN_VALUE)
              WorldMapArray[row+Theta]++;
            else
              WorldMapArray[row+Theta] = 1;
          }
        }
        else
        {
          if (flag2)
          { /* Lower northern hemisphere <=> rise southern */
            if (WorldMapArray[row+Theta] != Integer.MIN_VALUE)
              WorldMapArray[row+Theta]++;
            else
              WorldMapArray[row+Theta] = 1;
          }
          else
          { /* Lower southern hemisphere */
            if (WorldMapArray[row+Theta] != Integer.MIN_VALUE)
              WorldMapArray[row+Theta]--;
            else
              WorldMapArray[row+Theta] = -1;
          }
        }
        row += YRange;
      }
    }


}
