#pragma once

#include "volumeshop.h"

#include "Environment.h"
#include "Importer.h"
#include "Volume.h"
#include "SelectionVoxel.h"
#include "Exception.h"
#include <vector>

class ImporterSelectionVolumeDat : public Importer
{
public:

	ImporterSelectionVolumeDat(Environment & envEnvironment) : m_envEnvironment(envEnvironment), m_fProgress(0.0f)
	{
	};

	virtual ~ImporterSelectionVolumeDat()
	{
	};

	Environment & GetEnvironment()
	{
		return m_envEnvironment;
	};

	virtual const bool IsAccepted(const std::string & strFilename)
	{		
		std::string strLower(strFilename);
		std::transform(strFilename.begin(), strFilename.end(), strLower.begin(), tolower);

		std::vector<std::string> vecExtensions;
		vecExtensions.push_back("dat");

		for (unsigned int i=0;i<vecExtensions.size();i++)
			if (strLower.substr(strLower.size()-vecExtensions[i].size()) == vecExtensions[i])
				return true;
		
		return false;
	};

	virtual void load(const std::string & strFilename)
	{
		m_fProgress = 0.0f;

		std::cout << "Loading file: " << strFilename << " ... " << std::endl;
		FILE *fp = fopen(strFilename.c_str(),"rb");

		if (!fp)
		{
			std::cerr << "Error loading file." << std::endl << std::endl;
			throw Exception("Error loading file '%s'.",strFilename.c_str());
		}

		unsigned short uSizeX, uSizeY, uSizeZ;

		fread(&uSizeX,sizeof(unsigned short),1,fp);
		fread(&uSizeY,sizeof(unsigned short),1,fp);
		fread(&uSizeZ,sizeof(unsigned short),1,fp);

		std::cout << "Volume dimensions: (" << uSizeX << "," << uSizeY << "," << uSizeZ << ")" << std::endl;
		GetEnvironment().resize(unsigned int(uSizeX),unsigned int(uSizeY),unsigned int(uSizeZ));

		const unsigned int uSlice = unsigned int(uSizeX)*unsigned int(uSizeY); 
		const unsigned int uSize = uSlice*unsigned int(uSizeZ);

		short *pData = new short[uSize];
		fread(pData,sizeof(short),uSize,fp);

		const unsigned int uBlocksX = (GetEnvironment().GetSelectionVolume().GetSizeX() / BLOCKDIMENSION) + ((GetEnvironment().GetSelectionVolume().GetSizeX() % BLOCKDIMENSION) > 0);
		const unsigned int uBlocksY = (GetEnvironment().GetSelectionVolume().GetSizeY() / BLOCKDIMENSION) + ((GetEnvironment().GetSelectionVolume().GetSizeY() % BLOCKDIMENSION) > 0);
		const unsigned int uChunkFormatted = uBlocksX * uBlocksY;

		SelectionVoxel *pDataFormatted = new SelectionVoxel[uChunkFormatted * BLOCKSIZE];

		for (unsigned int uZ=0;uZ<GetEnvironment().GetSelectionVolume().GetSizeZ();uZ+=BLOCKDIMENSION)
		{
			std::cout << "  " << (100 * uZ) / GetEnvironment().GetSelectionVolume().GetSizeZ() << " %" << "\r";
			m_fProgress = float(uZ) / float(GetEnvironment().GetSelectionVolume().GetSizeZ());

			for (unsigned int k=0;k<BLOCKDIMENSION;k++)
			{
				if (uZ + k < GetEnvironment().GetDataVolume().GetSizeZ())
				{
					for (unsigned int j=0;j<GetEnvironment().GetSelectionVolume().GetSizeY();j++)
					{
						for (unsigned int i=0;i<GetEnvironment().GetSelectionVolume().GetSizeX();i++)
						{
/*
							const int i1nx = std::max(int(i)-1,0);
							const int i1ny = std::max(int(j)-1,0);
							const int i1nz = std::max(int(uZ + k)-1,0);

							const int i0px = int(i);
							const int i0py = int(j);
							const int i0pz = int(uZ+k);

							const int i1px = std::min(int(i)+1,int(GetEnvironment().GetSelectionVolume().GetSizeX()-1));
							const int i1py = std::min(int(j)+1,int(GetEnvironment().GetSelectionVolume().GetSizeY()-1));
							const int i1pz = std::min(int(uZ + k)+1,int(GetEnvironment().GetSelectionVolume().GetSizeZ()-1));

							const float fValue1nx1ny1nz = float(pData[i1nx + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px1ny1nz = float(pData[i0px + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px1ny1nz = float(pData[i1px + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1nx0py1nz = float(pData[i1nx + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px0py1nz = float(pData[i0px + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px0py1nz = float(pData[i1px + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1nx1py1nz = float(pData[i1nx + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px1py1nz = float(pData[i0px + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px1py1nz = float(pData[i1px + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1nz*uSlice]) > 0.0f ? 1.0f : 0.0f;

							const float fValue1nx1ny0pz = float(pData[i1nx + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px1ny0pz = float(pData[i0px + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px1ny0pz = float(pData[i1px + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1nx0py0pz = float(pData[i1nx + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px0py0pz = float(pData[i0px + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px0py0pz = float(pData[i1px + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1nx1py0pz = float(pData[i1nx + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px1py0pz = float(pData[i0px + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px1py0pz = float(pData[i1px + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) > 0.0f ? 1.0f : 0.0f;

							const float fValue1nx1ny1pz = float(pData[i1nx + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px1ny1pz = float(pData[i0px + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px1ny1pz = float(pData[i1px + i1ny*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1nx0py1pz = float(pData[i1nx + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px0py1pz = float(pData[i0px + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px0py1pz = float(pData[i1px + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1nx1py1pz = float(pData[i1nx + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue0px1py1pz = float(pData[i0px + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;
							const float fValue1px1py1pz = float(pData[i1px + i1py*GetEnvironment().GetSelectionVolume().GetSizeX() + i1pz*uSlice]) > 0.0f ? 1.0f : 0.0f;

							const unsigned int uBlockFormatted = (i / BLOCKDIMENSION) + (j / BLOCKDIMENSION) * uBlocksX;
							const unsigned int uOffsetFormatted = (i % BLOCKDIMENSION) + (j % BLOCKDIMENSION) * BLOCKDIMENSION + (k % BLOCKDIMENSION) * BLOCKDIMENSION * BLOCKDIMENSION;
							const unsigned int uIndex = uBlockFormatted * BLOCKSIZE + uOffsetFormatted;

							const float fValue = (1.0f / 26.0f) * (fValue1nx1ny1nz + fValue0px1ny1nz + fValue1px1ny1nz + fValue1nx0py1nz + fValue0px0py1nz + fValue1px0py1nz + fValue1nx1py1nz + fValue0px1py1nz + fValue1px1py1nz + fValue1nx1ny0pz + fValue0px1ny0pz + fValue1px1ny0pz + fValue1nx0py0pz + fValue0px0py0pz + fValue1px0py0pz + fValue1nx1py0pz + fValue0px1py0pz + fValue1px1py0pz + fValue1nx1ny1pz + fValue0px1ny1pz + fValue1px1ny1pz + fValue1nx0py1pz + fValue0px0py1pz + fValue1px0py1pz + fValue1nx1py1pz + fValue0px1py1pz + fValue1px1py1pz);
							const SelectionVoxel selSelection(fValue);
							pDataFormatted[uIndex] = selSelection;
*/
							const int i0px = int(i);
							const int i0py = int(j);
							const int i0pz = int(uZ+k);

							const float fValue0px0py0pz = float(pData[i0px + i0py*GetEnvironment().GetSelectionVolume().GetSizeX() + i0pz*uSlice]) / 4095.0f;

							const unsigned int uBlockFormatted = (i / BLOCKDIMENSION) + (j / BLOCKDIMENSION) * uBlocksX;
							const unsigned int uOffsetFormatted = (i % BLOCKDIMENSION) + (j % BLOCKDIMENSION) * BLOCKDIMENSION + (k % BLOCKDIMENSION) * BLOCKDIMENSION * BLOCKDIMENSION;
							const unsigned int uIndex = uBlockFormatted * BLOCKSIZE + uOffsetFormatted;

							const SelectionVoxel selSelection(fValue0px0py0pz);
							pDataFormatted[uIndex] = selSelection;
						}
					}
				}
				else
				{
					for (unsigned int j=0;j<GetEnvironment().GetDataVolume().GetSizeY();j++)
					{
						for (unsigned int i=0;i<GetEnvironment().GetDataVolume().GetSizeX();i++)
						{
							static const SelectionVoxel selSelection;

							const unsigned int uBlockFormatted = (i / BLOCKDIMENSION) + (j / BLOCKDIMENSION) * uBlocksX;
							const unsigned int uOffsetFormatted = (i % BLOCKDIMENSION) + (j % BLOCKDIMENSION) * BLOCKDIMENSION + (k % BLOCKDIMENSION) * BLOCKDIMENSION * BLOCKDIMENSION;
							const unsigned int uIndex = uBlockFormatted * BLOCKSIZE + uOffsetFormatted;
							
							pDataFormatted[uIndex] = selSelection;
						}
					}
				}
			}

			for (unsigned int uY=0;uY < uBlocksY;uY++)
			{
				for (unsigned int uX=0;uX < uBlocksX;uX++)
				{
					const unsigned int uIndexFormatted = uX + uY * uBlocksX;
					GetEnvironment().GetSelectionVolume().SetBlock(uX*BLOCKDIMENSION,uY*BLOCKDIMENSION,uZ,pDataFormatted + uIndexFormatted * BLOCKSIZE);
				}
			}
		}

		delete[] pData;
		delete[] pDataFormatted;

		fclose(fp);

		std::string strAdditionalFilename;
		size_t uIndex = strFilename.find_last_of('.');
		strAdditionalFilename = strFilename.substr(0,uIndex);
		strAdditionalFilename += std::string(".ini");

		char vpSpacingX[256],vpSpacingY[256],vpSpacingZ[256];
		GetPrivateProfileString("DatFile","oldDat Spacing X","1.0",vpSpacingX,256,strAdditionalFilename.c_str());
		GetPrivateProfileString("DatFile","oldDat Spacing Y","1.0",vpSpacingY,256,strAdditionalFilename.c_str());
		GetPrivateProfileString("DatFile","oldDat Spacing Z","1.0",vpSpacingZ,256,strAdditionalFilename.c_str());

		GetEnvironment().GetSelectionVolume().SetScale(Vector(fromString<float>(std::string(vpSpacingX)),fromString<float>(std::string(vpSpacingY)),fromString<float>(std::string(vpSpacingZ))));
		std::cout << "Success loading file." << std::endl << std::endl;

		m_fProgress = 1.0f;

		GetEnvironment().update();
	};

	virtual const float GetProgress()
	{
		return m_fProgress;
	};

private:

	Environment & m_envEnvironment;
	float m_fProgress;
};