#pragma once

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

using namespace std;

namespace sor {

  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
  //
  //};

} //ends namespace 'sor'
