#pragma once
#include <typeinfo.h>

#include "CommonDefs.h"

//#define VECTOR_TRACE

template <typename VarType>
class CVector
{
public:
	friend class CMLP;
	CVector()
	{
		len = 0; data = NULL;
		CStringA str = typeid(VarType).raw_name();
		bClassType = (str.Left(2) == _T(".?"));
		if(!bClassType)
			memset(&FakeData, 0, sizeof(VarType));
#ifdef VECTOR_TRACE
		SetId();
#endif
	}
	//CVector(CVector &copy) { Copy(&copy); }
	CVector(int len, bool bFill = true, byte Fill = 0)
	{
		this->len = 0;
		data = NULL; 
		CStringA str = typeid(VarType).raw_name();
		bClassType = (str.Left(2) == _T(".?"));		
		init(len, bFill, Fill); 
#ifdef VECTOR_TRACE
		SetId();
#endif
	}
	~CVector()
	{
		//MessageBox(NULL, label, "", 0);
		if(len > 0){
			SAFE_DELETE_ARR(data); 
			len = 0;
		}
#ifdef VECTOR_TRACE 
		ShowId(); 
#endif
	}
	void init(DWORD len, bool bFill = true, byte FillVal = 0/*, CString label = ""*/)
	{
		if(!bClassType)
			memset(&FakeData, 0, sizeof(VarType));
		if(this->len != len){
			this->len = len;
			SAFE_DELETE_ARR(data);
			if(! len)
				return;
			data = new VarType[len];
		}
      if(bFill){
         if(!bClassType)
            FillMemory(data, len*sizeof(VarType), FillVal);
      }
		//this->label = label;
	}
	
	bool FillVal(VarType val){
		for (int i = 0; i < len; i++)
			data[i] = val;
		return true;
	}

	bool Fill(byte FillVal = 0){
		if(!bClassType)
			FillMemory(data, len*sizeof(VarType), FillVal);
		return !bClassType;
	}

   void Clear(){
      SAFE_DELETE_ARR(data);
      len = 0;
   }
	void Copy(CVector<VarType> *copy)
	{
		SAFE_DELETE_ARR(data);
		len = copy->len;
		data = new VarType[len];
		if(!bClassType)
         CopyMemory(data, copy->data, len*sizeof(VarType));
		else
			for (int i = 0; i < len; i++)
				data[i] = copy->data[i];
		//label = copy->label + " copy";
	}
	inline VarType operator[](int x) const
	{
		ASSERT((x < len) && (x > -1));
#ifdef DEBUG
		((x < len) && (x > -1)) ? (return data[x]) : return 0;
#endif
		return data[x];
	}
	inline VarType& operator[](int x)
	{
		ASSERT((x < len) && (x > -1));
#ifdef DEBUG
		if((x < len) && (x > -1)) 
			return data[x];
		else
			return FakeData;
#endif
		return data[x];
	}

/************************************************************************/
/* Get & Set functions																	*/
/************************************************************************/
//Safe get function
	inline VarType get(int x)
   {
      ASSERT((x < len) && (x > -1));
      if((x < len) && (x > -1))
         return data[x];
		else{
#ifdef VECTOR_TRACE
			if(fpTrace) fprintf(fpTrace, "vecId: %d, get(%d), len: %d\n", id, x, len);
			if(fpTrace) fflush(fpTrace);
#endif         
			return FakeData;
		}
   }
//////////////////////////////////////////////////////////////////////////
//Safe set function
	inline void set(int x, VarType val)
	{
		ASSERT((x < len) && (x > -1));
		if((x < len) && (x > -1)) data[x] = val;
#ifdef VECTOR_TRACE
		else {
			if(fpTrace) fprintf(fpTrace, "vecId: %d, set(%d), len: %d\n", id, x, len);
			if(fpTrace) fflush(fpTrace);
		}
#endif         
	}

//////////////////////////////////////////////////////////////////////////
//Unsafe but fast get function
	inline VarType Get(int x){
#ifdef VECTOR_TRACE
		return get(x);
#endif
#ifdef DEBUG
		return get(x);
#endif
		return data[x];
	}

//////////////////////////////////////////////////////////////////////////
//Unsafe but fast set functions
	inline void Set(int x, VarType val){
#ifdef VECTOR_TRACE
		return set(x,val);
#endif
#ifdef DEBUG
		return set(x, val);
#endif
		data[x] = val;
	}
//////////////////////////////////////////////////////////////////////////


   //use carefully
	inline VarType* getData(void){return data;}
   void Resize(int newsize)
   {
      if(newsize == len)
         return;
	  VarType* tmpdata = new VarType[newsize];
	  if(newsize < len){
         if(!bClassType){
            CopyMemory(tmpdata, data, newsize*sizeof(VarType));
				SAFE_DEL(data, len);
            data = new VarType[newsize];
            CopyMemory(data, tmpdata, newsize*sizeof(VarType));
         }
         else{
            for (int i = 0; i < newsize; i++)
               tmpdata[i] = data[i];         
				SAFE_DEL(data, len);
            data = new VarType[newsize];
            for (int i = 0; i < newsize; i++)
               data[i] = tmpdata[i];         
         }
      }
      else {//if(newsize > len)
         if(!bClassType){
            FillMemory(tmpdata, newsize*sizeof(VarType), 0);
            CopyMemory(tmpdata, data, len*sizeof(VarType));
				SAFE_DEL(data, len);
				data = new VarType[newsize];
            CopyMemory(data, tmpdata, newsize*sizeof(VarType));
         }
         else{
            for (int i = 0; i < len; i++)
               tmpdata[i] = data[i];
				SAFE_DEL(data, len);
            data = new VarType[newsize];
            for (int i = 0; i < len; i++)
               data[i] = tmpdata[i];
         }
      }
		SAFE_DEL(tmpdata, newsize);
		len = newsize;
   }
	
	void RemoveAt(int idx)
	{
		ASSERT(idx < len && idx > -1);
		if(len <= 1)
			return init(0);

		VarType* tmpdata = new VarType[len-1];

		if(!bClassType){
			CopyMemory(tmpdata, data, idx*sizeof(VarType));
			CopyMemory(tmpdata+idx, data+idx+1, (len-idx)*sizeof(VarType));
			SAFE_DEL(data, len);
			data = new VarType[len-1];
			CopyMemory(data, tmpdata, (len-1)*sizeof(VarType));
		}
		else{
			for (int i = 0; i < idx; i++)
				tmpdata[i] = data[i];         
			for (int i = idx+1; i < len; i++)
				tmpdata[i-1] = data[i];
			SAFE_DEL(data, len);
			data = new VarType[len-1];
			for (int i = 0; i < len-1; i++)
				data[i] = tmpdata[i];         
		}
		--len;
		SAFE_DELETE_ARR(tmpdata);
	}

	void SaveToCSV(TCHAR* fileName, TCHAR* format = NULL){
		FILE* fp = fopen(fileName, "wt");	
		if (!format)
			format = "%.3d,";
		for (int x = 0; x < len; x++)
			fprintf(fp, format, Get(x));
		fclose(fp);
	}
	//searches in the vector from startIdx to endIdx containing both indices
	//returns -1 if nothing found
	int Find(VarType val, int startIdx = 0, int endIdx = -1){
		if(len <= 0)
			return -1;
		if(startIdx < 0) startIdx = 0;
		if((endIdx < 0) || (endIdx >= len)) endIdx = len-1;
		ASSERT((startIdx >= 0) && (startIdx < len) && (endIdx >= 0) && (endIdx < len) && (endIdx >= startIdx));
		for (int i = startIdx; i <= endIdx; i++)
			if(Get(i) == val)
				return i;
		return -1;
	}
	VarType maxVal(int startIdx = 0, int endIdx = -1, int *idx = NULL)
	{
		if(startIdx < 0) startIdx = 0;
		if((endIdx < 0) || (endIdx >= len)) endIdx = len-1;
		ASSERT((startIdx >= 0) && (startIdx < len) && (endIdx >= 0) && (endIdx < len) && (endIdx >= startIdx));
		VarType maxVal = Get(startIdx);
		int maxIdx = startIdx;
		for (int i = startIdx+1; i <= endIdx; i++)
			if(Get(i) > maxVal){
				maxVal = Get(i);
				maxIdx = i;
			}
		if(idx) *idx = maxIdx;
		return maxVal;
	}

	VarType minVal(int startIdx = 0, int endIdx = -1, int *idx = NULL)
	{
		if(startIdx < 0) startIdx = 0;
		if((endIdx < 0) || (endIdx >= len)) endIdx = len-1;
		ASSERT((startIdx >= 0) && (startIdx < len) && (endIdx >= 0) && (endIdx < len) && (endIdx >= startIdx));
		VarType minV = Get(startIdx);
		int minIdx = startIdx;
		for (int i = startIdx+1; i <= endIdx; i++)
			if(Get(i) < minV){
				minV = Get(i);
				minIdx = i;
			}
			if(idx) *idx = minIdx;
			return minV;
	}

	void StdMean(double* mean, double* std = 0){
		if((! mean) || (len < 1)) return;
		(*mean) = 0;
		for(int i = 0; i < len; i++) (*mean) += data[i];
		(*mean) /= len;
		if((! std) || (len < 2)) return;

		*std = 0;
		for(int i = 0; i < len; i++) (*std) += sqr(data[i] - (*mean));
		(*std) = sqrt((*std) / (len - 1));
	}
	//should be private!	
	int len;
#ifdef VECTOR_TRACE
	void SetId(){
		id = ++Counter; 
		if(fpTrace) fprintf(fpTrace, "**** CVector # %d ****\n", id);
		if(fpTrace) fflush(fpTrace);
	}
	void ShowId(){
		if(fpTrace) fprintf(fpTrace, "**** ~CVector # %d ****\n", id);
		if(fpTrace) fflush(fpTrace);
	}
	static int Counter;
	static FILE* fpTrace;
#endif

private:
#ifdef VECTOR_TRACE
	int id;
#endif
	VarType* data;
	VarType FakeData;//used for operator [] when trying to access out of range data
	bool bClassType;
};
//template <typename VarType>
//int CVector<VarType>::Counter = 0;
#ifdef VECTOR_TRACE
	template <typename _T>
	int CVector<_T>::Counter = 0;
	template <typename _T>
	FILE* CVector<_T>::fpTrace = fopen("C:\\vecTrace.txt", "w+t");
#endif

