/*
    Copyright (C) 2012 Bartosz Broda, Jacek Bochenek, Łukasz Biłeńkij, 
    Damian Jaworski, Wojciech Mazur, Dominik Piasecki, Maciej Piasecki, 
    Radosław Ramocki
    
    This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.

    This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. 

    See the LICENSE and COPYING files for more details.
*/

#pragma once
#include "matrices_all.h"
#include "comparator_all.h"
#include <boost/smart_ptr.hpp>

namespace clustering
{
	namespace som
	{

		/**
		 * Neuron inside SOMNet.
		 */
		class CSOMNeuron
		{
			public:
				//TODO extract this as a independent functor (together with DistanceTo)
				/// Distance between two neurons
				enum DistanceType{COSINE, EUCLIDEAN};

				CSOMNeuron(size_t cWeights, double x, double y,
						boost::shared_ptr<smartcomparator::simfunctions::LocalSimilarityFunction> simFun);
				~CSOMNeuron(void);

				/**
				 * Counts distance between this neuron and the other one using given distance type (default: euclidean)
				 * \sa SetNeuronDistanceType
				 */
				double DistanceTo(const CSOMNeuron& neuron);

				/**
				  Liczy odleglosc pomiedzy tym neuronem a wekotrem treningowym zgodnie z podana
				  funkcja podobieństwa.
				  \throw std::stirng Jezeli zle zostal okreslony sposob liczenia odleglosci pomiedzy neuronami.
				  */
				double SimilarityTo(Matrices::CompressedVectorPtr inputVector);
				/**
				  Ustawia sposob liczenia  odleglosci pomiedzy neuronami... Domyslnie jest liczona odleglosc euklidesowa
				  */
				void SetNeuronDistanceType(DistanceType dt);
				/**
				  uaktualnia wagi neuronow, po czym je normalizuje
				  */
				void UpdateWeights(Matrices::CompressedVectorPtr inputCompressedVector, double learningRate,double theta);
				inline double GetX() const;
				inline double GetY() const;
				double GetMQE() const;
				size_t GetId() const;
				//std::vector<double>& GetWeights(){return m_vWeights;}
				boost::shared_ptr<std::set<size_t> >  GetDataIndices(){return m_pDataIndices;}
				void ClearData();

				void SetXY(double x, double y);
				void SetMQE(double mqe);

				void AddDataIndex(size_t i){ m_pDataIndices->insert(i);}

				// TODO: take this outside of the class, save only network (without internal state), convert indices -> values
				void SaveState(std::ostream &out);
				void LoadState(std::istream &out);


			private:
				/**
				  Mean Quantization Error
				  */
				double m_dMQE;
				DistanceType m_neuronDistance;

				double m_dX, m_dY, m_dNormalizedX, m_dNormalizedY;
				boost::shared_ptr<std::set<size_t> > m_pDataIndices;
				std::vector<double> m_vWeights;

				smartcomparator::simfunctions::LocalSimilarityFunctionPtr m_SimFunction;


				inline void Normalize(std::vector<double> &v);
				inline double Length(const std::vector<double> &v);
				void RandomizeWeights(size_t cWeights);


				static std::string END_OF_WEIGHTS;
				static std::string END_OF_DOCUMENTS;
				static size_t m_IdGen;
				size_t m_Id;
		};

	}//namespace SOM
}//namespace Clustering

