using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.IO;
using Microsoft.Xna.Framework.Graphics.PackedVector;

namespace DOFerVolumeRenderer
{
	class Volume
	{
		public static Texture3D CreateSampleData(Game game)
		{
			const int VOLUME_TEX_SIZE = 128;
			int size = VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE ;
			Color[] data = new Color[size];

			Texture3D tex3d = new Texture3D(game.GraphicsDevice, VOLUME_TEX_SIZE, VOLUME_TEX_SIZE, VOLUME_TEX_SIZE, true, SurfaceFormat.Color);

			for (int x = 0; x < VOLUME_TEX_SIZE; x++)
			{
				for (int y = 0; y < VOLUME_TEX_SIZE; y++)
				{
					for (int z = 0; z < VOLUME_TEX_SIZE; z++)
					{
						data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(z % 250, y % 250, 250, 230);

						Vector3 p = new Vector3(x, y, z) - new Vector3(VOLUME_TEX_SIZE - 20, VOLUME_TEX_SIZE - 30, VOLUME_TEX_SIZE - 30);
						bool test = (p.Length() < 42);
						if (test)
							data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)].A = 0;

						p = new Vector3(x, y, z) - new Vector3(VOLUME_TEX_SIZE / 2, VOLUME_TEX_SIZE / 2, VOLUME_TEX_SIZE / 2);
						test = (p.Length() < 24);
						if (test)
							data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)].A = 0;


						if (x > 20 && x < 40 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
						{
							data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(100, 250, y % 100, 250);
						}

						if (x > 50 && x < 70 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
						{

							data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(250, 250, y % 100, 250);
						}

						if (x > 80 && x < 100 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
						{
							data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(250, 70, y % 100, 250);
						}

						p = new Vector3(x, y, z) - new Vector3(24, 24, 24);
						test = (p.Length() < 40);
						if (test)
							data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)].A = 0;
					}
				}
			}

			tex3d.SetData<Color>(data);
			return tex3d;
		}

       

        public static Texture3D CreateSampleData2(Game game)
        {
            const int VOLUME_TEX_SIZE = 128;
            int size = VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE;
            Color[] data = new Color[size];

            Texture3D tex3d = new Texture3D(game.GraphicsDevice, VOLUME_TEX_SIZE, VOLUME_TEX_SIZE, VOLUME_TEX_SIZE, true, SurfaceFormat.Color);

            for (int x = 0; x < VOLUME_TEX_SIZE; x++)
            {
                for (int y = 0; y < VOLUME_TEX_SIZE; y++)
                {
                    for (int z = 0; z < VOLUME_TEX_SIZE; z++)
                    {
                        data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(250, 250, 250, 10);

                        if (x > 0 && x < 20 && y > 0 && y < 20)
                        {
                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(250,0,0,400);
                        }

                        else if (y > 0 && y < 20 && z > 0 && z < 20)
                        {

                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(0, 250, 0, 400);
                        }

                        else if (z > 00 && z < 20 && x > 0 && x < 20)
                        {
                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(0, 0, 250, 400);
                        }

                    }
                }
            }

            tex3d.SetData<Color>(data);
            return tex3d;
        }

        public static Texture3D CreateSampleData3(Game game)
        {
            const int VOLUME_TEX_SIZE = 128;
            int size = VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE;
            Color[] data = new Color[size];

            Texture3D tex3d = new Texture3D(game.GraphicsDevice, VOLUME_TEX_SIZE, VOLUME_TEX_SIZE, VOLUME_TEX_SIZE, true, SurfaceFormat.Color);

            for (int x = 0; x < VOLUME_TEX_SIZE; x++)
            {
                for (int y = 0; y < VOLUME_TEX_SIZE; y++)
                {
                    for (int z = 0; z < VOLUME_TEX_SIZE; z++)
                    {
                        data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(50, 50, 50, 0);

                        Vector3 p = new Vector3(x, y, z) - new Vector3(VOLUME_TEX_SIZE - 20, VOLUME_TEX_SIZE - 30, VOLUME_TEX_SIZE - 30);
                        bool test = (p.Length() < 42);
                        if (test)
                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)].A = 250;

                        p = new Vector3(x, y, z) - new Vector3(VOLUME_TEX_SIZE / 2, VOLUME_TEX_SIZE / 2, VOLUME_TEX_SIZE / 2);
                        test = (p.Length() < 24);
                        if (test)
                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)].A = 250;


                        if (x > 20 && x < 40 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
                        {
                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(1, 100, 2, 250);
                        }

                        if (x > 50 && x < 70 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
                        {

                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(100, 2, 1, 250);
                        }

                        if (x > 80 && x < 100 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
                        {
                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)] = new Color(2, 1, 100, 250);
                        }

                        p = new Vector3(x, y, z) - new Vector3(24, 24, 24);
                        test = (p.Length() < 40);
                        if (test)
                            data[(x) + (y * VOLUME_TEX_SIZE) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE)].A = 250;
                    }
                }
            }

            tex3d.SetData<Color>(data);
            return tex3d;
        }

		/// <summary>
		/// Loads a 16-bit RAW file.
		/// </summary>
		/// <param name="file"></param>
		private static Texture3D loadRAWFile16(Game game, FileStream file, int width, int height, int depth)
		{
			BinaryReader reader = new BinaryReader(file);
			if (null == reader)
				return null;

			ushort[] buffer = new ushort[width * height * depth];

			for (int i = 0; i < buffer.Length; i++)
				buffer[i] = reader.ReadUInt16();

			reader.Close();

			Texture3D tex3d = new Texture3D(game.GraphicsDevice, width, height, depth, false, SurfaceFormat.HalfVector4);

			//scale the scalar values to [0, 1]
			HalfVector4[] scalars = new HalfVector4[buffer.Length];
			for (int i = 0; i < buffer.Length; i++)
			{
				scalars[i] = new HalfVector4(0, 0, 0, (float)buffer[i] / ushort.MaxValue);
			}

			tex3d.SetData<HalfVector4>(scalars);
			return tex3d;
		}


		/// <summary>
		/// Loads a 8-bit RAW file.
		/// </summary>
		/// <param name="file"></param>
		private static Texture3D loadRAWFile8(Game game, FileStream file, int width, int height, int depth)
		{
			BinaryReader reader = new BinaryReader(file);
			if (null == reader)
				return null;

			byte[] buffer = new byte[width * height * depth];

			for (int i = 0; i < buffer.Length; i++)
				buffer[i] = reader.ReadByte();

			reader.Close();

			Texture3D tex3d = new Texture3D(game.GraphicsDevice, width, height, depth, false, SurfaceFormat.Alpha8);

			//scale the scalar values to [0, 1]
			Alpha8[] scalars = new Alpha8[buffer.Length];
			for (int i = 0; i < buffer.Length; i++)
			{
				scalars[i] = new Alpha8((float)buffer[i] / byte.MaxValue);
			}

			tex3d.SetData<Alpha8>(scalars);
			return tex3d;
		}


		/// <summary>
		/// Loads a RAW file into a Texture3D
		/// </summary>
		/// <param name="filepath"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="depth"></param>
		/// <returns>Texture3D containing teh volume data, or null if failed</returns>
		public static Texture3D LoadFromFile(Game game, string filepath, int width, int height, int depth)
		{
			FileStream file = new FileStream(filepath, FileMode.Open);
			if (null == file)
				return null;

			long length = file.Length;

			Texture3D result = null;
			if (length > width * height * depth)
			{
				result = loadRAWFile16(game, file, width, height, depth);
			}
			else
			{
				result = loadRAWFile8(game, file, width, height, depth);
			}

			file.Close();
			return result;
		}
	}
}
