﻿////////////////////////////////////////////////////////////////////////////////
//worldgenerator.cs
//Created on: 2015-8-21, 18:18
//
//Project VoxelEngine
//Copyright C bajsko 2015. All rights Reserved.
////////////////////////////////////////////////////////////////////////////////

using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VoxelEngine.Game.Blocks;
using VoxelEngine.GameConsole;
using VoxelEngine.Utility;

namespace VoxelEngine.Game
{
    public class WorldGenerator
    {
        private World mWorldRef;

        public WorldGenerator(World worldRef)
        {
            this.mWorldRef = worldRef;
        }

        public Chunk GenerateChunk(int xp, int yp, int zp)
        {
            Chunk chunk = null;
            chunk = new Chunk(new Vector3(xp, yp, zp));

            Vector3 chunkPosBlock = new Vector3(xp, yp, zp) * Chunk.CHUNK_SIZE;
            int treeDensity = 2;

            float stoneBaseHeight = 0;
            float stoneBaseNoise = 0.05f;
            float stoneBaseNoiseHeight = 4;

            float stoneMountainHeight = 48;
            float stoneMountainFrequency = 0.008f;
            float stoneMinHeight = -12;

            float dirtBaseHeight = 1;
            float dirtNoise = 0.04f;
            float dirtNoiseHeight = 3;
            
            for (int x = (int)chunkPosBlock.X - 5; x < chunkPosBlock.X + Chunk.CHUNK_SIZE + 5; x++)
            {
                for(int z = (int)chunkPosBlock.Z - 5; z < chunkPosBlock.Z + Chunk.CHUNK_SIZE + 5; z++)
                {

                    int stoneHeight = (int)Math.Floor(stoneBaseHeight);
                    stoneHeight += (int)Math.Floor(GetNoiseValue(x, 0, z, stoneMountainFrequency, (int)(stoneMountainHeight)));
 
                    if (stoneHeight < stoneMinHeight)
                        stoneHeight = (int)Math.Floor((stoneMinHeight));
 
                    stoneHeight += (int)Math.Floor(GetNoiseValue(x, 0, z, stoneBaseNoise, (int)(stoneBaseNoiseHeight)));
 
                    int dirtHeight = stoneHeight + (int)(dirtBaseHeight);
                    dirtHeight += (int)Math.Floor(GetNoiseValue(x, 100, z, dirtNoise, (int)(dirtNoiseHeight)));


                    for (int y = (int)chunkPosBlock.Y - 10; y < chunkPosBlock.Y+Chunk.CHUNK_SIZE + 10; y++)
                    {
                        //from world block space to local chunk space
                        int xfactor = (int)chunkPosBlock.X;
                        int yfactor = (int)chunkPosBlock.Y;
                        int zfactor = (int)chunkPosBlock.Z;

                        if (y <= stoneHeight)
                            chunk.SetBlock(x-xfactor, y-yfactor, z-zfactor, new Block(BlockType.Stone));
                        else if (y <= dirtHeight)
                        {
                            chunk.SetBlock(x - xfactor, y - yfactor, z - zfactor, new Block(BlockType.Grass));

                            if (y == dirtHeight && GetNoiseValue(x, 0, z, 0.2f, 100) < treeDensity)
                            {
                                //CreateTree(x, y, z);
                            }
                        }
                    }
                }
            }

            //chunk.ApplySunlight();

            return chunk;
        }

        public Chunk GenerateChunk(Vector3 position)
        {
            return GenerateChunk((int)position.X, (int)position.Y, (int)position.Z);
        }

        private void CreateTree(int x, int y, int z)
        {
            //TODO: 
            //major performance hit here...
            //fix..
            Random random = new Random();
            int trunkLength = random.Next(5, 10);

            for(int yy = y; yy < y + trunkLength; yy++)
            {
                mWorldRef.SetBlock(x, yy+1, z, new Block(BlockType.Log));
            }

            int leavesStart = y+trunkLength;

            for(int xi = -3; xi <= 3; xi++)
            {
                for(int yi = 0; yi <= 1; yi++)
                {
                    for(int zi = -3; zi <= 3; zi++)
                    {
                        mWorldRef.SetBlock(x + xi, leavesStart + yi, z + zi, new Block(BlockType.Leaves));
                    }
                }
            }


            for(int xi = -2; xi <= 2; xi++)
            {
                for(int zi = -2; zi <= 2; zi++)
                {
                    mWorldRef.SetBlock(x + xi, leavesStart + 2, z + zi, new Block(BlockType.Leaves));
                }
            }

            mWorldRef.SetBlock(x - 1, leavesStart + 3, z, new Block(BlockType.Leaves));
            mWorldRef.SetBlock(x + 1, leavesStart + 3, z, new Block(BlockType.Leaves));
            mWorldRef.SetBlock(x, leavesStart + 3, z - 1, new Block(BlockType.Leaves));
            mWorldRef.SetBlock(x, leavesStart + 3, z + 1, new Block(BlockType.Leaves));
        }

        private int GetNoiseValue(int x, int z, float scale, int max)
        {
            int value = (int)((Noise.Generate(x*scale, 0, z*scale)) * (max / 2f));
            return value;
        }

        private float GetNoiseValue(int x, int y, int z, float scale, int max)
        {
            int value = (int)Math.Floor((Noise.Generate(x * scale, y * scale, z * scale) + 1f) * (max / 2f));
            return value;
        }
    }
}
