#pragma once

#include "stdio.h"
#include "Vector.h"
#include <cmath>
#include <iostream>

using namespace std;

class GaussianMixture
{
public:
	int nChannels;
	double* alpha;
	double* sigma;
	double* beta;
	double* sigma_square;
	double* beta_square;
public:
	GaussianMixture()
	{
		nChannels = 0;
		alpha = sigma = beta = sigma_square = beta_square = NULL;
	}
	GaussianMixture(int _nChannels)
	{
		nChannels = _nChannels;
		allocate();
		for(int i = 0;i<nChannels;i++)
		{
			alpha[i] = 0.95;
			sigma[i] = 0.05;
			beta[i] = 0.5;
		}
		square();
	}
	GaussianMixture(const GaussianMixture& GM)
	{
		clear();
		copy(GM);
	}
	void copy(const GaussianMixture& GM)
	{
		nChannels = GM.nChannels;
		allocate();
		for(int i  = 0;i<nChannels;i++)
		{
			alpha[i]  = GM.alpha[i];
			sigma[i] = GM.sigma[i];
			beta[i]    = GM.beta[i];
		}
		square();
	}
	void operator=(const GaussianMixture& GM)
	{
		clear();
		copy(GM);
	}
	GaussianMixture shrink(int N)
	{
		GaussianMixture GM(N);
		for(int i = 0;i<N;i++)
		{
			GM.alpha[i]  = alpha[i];
			GM.sigma[i] = sigma[i];
			GM.beta[i]    = beta[i];
		}
		GM.square();
		return GM;
	}
	void allocate()
	{
		alpha = new double[nChannels];
		sigma = new double[nChannels];
		beta = new double[nChannels];
		sigma_square = new double[nChannels];
		beta_square = new double[nChannels];
	}
	void clear()
	{
		if(!alpha)
			delete []alpha;
		if(!sigma)
			delete []sigma;
		if(!beta)
			delete []beta;
		if(!sigma_square)
			delete []sigma_square;
		if(!beta_square)
			delete []beta_square;
		alpha = sigma = beta = sigma_square = beta_square = NULL;
	}
	void reset()
	{
		//for(int i = 0;i<nChannels;i++)
		//	alpha[i] = sigma[i] = beta[i] = sigma_square[i] = beta_square[i] = 0;
		for(int i = 0;i<nChannels;i++)
		{
			alpha[i] = 0.95;
			sigma[i] = 0.05;
			beta[i] = 0.5;
		}
		square();
	}
	void reset(int _nChannels)
	{
		clear();
		nChannels = _nChannels;
		allocate();
		reset();
	}
	double Gaussian(double x,int i,int k) const
	{
		if(i==0)
			 return exp(-x/(2*sigma_square[k]))/(2*M_PI*sigma[k]);
		else
			return exp(-x/(2*beta_square[k]))/(2*M_PI*beta[k]);
	}
	~GaussianMixture()
	{
		clear();
	}
	void square()
	{
		for(int i =0;i<nChannels;i++)
		{
			sigma_square[i] = sigma[i]*sigma[i];
			beta_square[i] = beta[i]*beta[i];
		}
	}
	void display()
	{
		for(int i = 0;i<nChannels;i++)
			cout<<"alpha: "<<alpha[i] << " sigma: "<<sigma[i]<<" beta: "<<beta[i]<<" sigma^2: "<<sigma_square[i]<<" beta^2: "<<beta_square[i]<<endl;
	}
	bool write(const char* filename)
	{
		ofstream myfile(filename,ios::out | ios::binary);
		if(myfile.is_open())
		{
			bool foo = write(myfile);
			myfile.close();
			return foo;
		}
		return false;
	}
	bool write(ofstream& myfile)
	{
		myfile.write((char *)&nChannels,sizeof(int));
		myfile.write((char *)alpha,sizeof(double)*nChannels);
		myfile.write((char *)sigma,sizeof(double)*nChannels);
		myfile.write((char *)beta,sizeof(double)*nChannels);
		return true;
	}
	bool read(const char* filename)
	{
		ifstream myfile(filename, ios::in | ios::binary);
		if(myfile.is_open())
		{
			bool foo = read(myfile);
			myfile.close();
			square();
			return foo;
		}
		return false;
	}
	bool read(ifstream& myfile)
	{
		myfile.read((char *)&nChannels,sizeof(int));
		allocate();
		myfile.read((char *)alpha,sizeof(double)*nChannels);
		myfile.read((char *)sigma,sizeof(double)*nChannels);
		myfile.read((char *)beta,sizeof(double)*nChannels);
		square();
		return true;
	}
};

//class Laplacian
//{
//public:
//	int nChannels;
//	Vector<double> scale;
//public:
//	Laplacian()
//	{
//	}
//	Laplacian(int _nChannels)
//	{
//		nChannels = _nChannels;
//		scale.allocate(nChannels);
//	}
//	Laplacian(const Laplacian
//
//};
