/*
    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.
*/

#ifndef GHSOM_SOMNET_H
#define GHSOM_SOMNET_H

#include <boost/smart_ptr.hpp>
#include "SOMNeuron.h"

#include "clusteringbase_all.h"

namespace clustering
{
	namespace som
	{
		class CSOMNet
		{
		public:
			/**
			\param documentMatrix przetrasformowana i znormalizowana wierszami macierz dokumentów
			(działajaca na CRS). Musi być wczytana w trybie ONLY_CRS lub CCS_AND_CRS
			*/
			CSOMNet(size_t cNeuronsInRow, Matrices::SuperMatrixPtr documentMatrix,
					smartcomparator::simfunctions::LocalSimilarityFunctionPtr simfun,
				double mqe0 = 0,
				boost::shared_ptr<std::set<size_t> > indices = boost::shared_ptr<std::set<size_t> > (),
				std::ostream &logout = std::cout);
			~CSOMNet(void);

			/**
			 * Sets maximum number of epochs and calls operator()
			 */
			void TrainNet(int maxEpoch);

			/**
			 * Performs training of net.
			 */
			void operator() ();

			void SetMaxEpochCount(int max);

			void SaveState(std::string filename);
			void LoadState(std::string filename);

			ClusteringBase::ClusterSetPtr GetClusters();

			std::vector< std::vector<double> >& CountMQE();
			double GetMQE0() const;

			boost::shared_ptr<std::set<size_t> > GetData(size_t row, size_t column);


			/**
			id = 0, dla nieustawionego id
			*/
			void SetParentId(size_t id);
			/**
			id = 0, dla nieustawionego id
			*/
			size_t GetParentId() const;

			/**
			Zwraca liczbę neuronów w wierszu.
			*/
			size_t GetNumberOfNeurons() const;
			void SetNumberOfNeurons(size_t cneurons);

			std::vector< std::vector<boost::shared_ptr<CSOMNeuron> > >& GetNeurons();

			size_t GetId() const;

		private:
			/**
			Tworzy kwadratową siatkę
			\param cNeuronsInRow liczba neuronów w wierszu
			\param cWeights liczba wag na neuron
			*/
			void CreateNet(size_t cNeuronsInRow, size_t cWeights);

			/**
			Indeksy w macierzy dla których ta warstwa SOM działa
			*/
			boost::shared_ptr<std::set<size_t> > m_pMatrixIndices;

			/**
			Funkcja podobienstwa
			*/
			smartcomparator::simfunctions::LocalSimilarityFunctionPtr m_SimFunction;

			/**
			Siec neuronow
			*/
			std::vector< std::vector<boost::shared_ptr<CSOMNeuron> > > m_vNet;

			/**
			Wszystkie neurony w sieci zapisane w tablicy (ulatwienie w implementacji)
			*/
			std::vector<boost::shared_ptr<CSOMNeuron> > m_vNeurons;
			/**
			Macierz
			*/
			Matrices::SuperMatrixPtr m_Matrix;

			/**
			Liczba wag na neuron
			*/
			size_t m_cWeights;
			/**
			Aktualny współczynnik uczenia
			*/
			double m_dCurrentLearningRate;
			/**
			Początkowy współczynnik uczenia
			*/
			double m_dInitialLearningRate;
			int m_iCurrentEpoch;///< aktualna epoka
			int m_iMaxEpoch;///<maksymalna liczba epok

			/**
			Strumień, do którego są wypisywane informacje
			*/
			std::ostream &m_LogOut;

			/**
			Przypisanie do neuronów dokumentów
			*/
			void AssingDataIndicesToNeurons();


			/// Wyszukuje BMU -> Best Matching Unit wg okreslonej funkcji podobieństwa
			int FindBMU(Matrices::CompressedVectorPtr document);

			/**
			tablica indeksów w macierzy uzywana do podawania danych na siec w sposob czesciowo losowy.
			jej rozmiar to m_pMatrixIndices.size() lub m_Matrix.NumberOfRows() (gdy przetwarzamy cala macierz)
			*/
			size_t* m_piIndices;
			/**
			Losowo zamienia 1/4 elementow parami z tablicy m_piIndices
			*/
			size_t* GenerateRanodomIdices();

			// rzeczy zwiazane z MQE

			/**
			Średnie mqe wzorcowej epoki... istotne tylko dla GHSOM
			*/
			double m_dMQE0;

			/**
			MQE dla każdego z wektorów
			*/
			std::vector< std::vector<double> > m_vMQE;


			size_t m_stParentId;
			size_t m_stId;
			static size_t m_stIdGen;
		};
	}//namespace
}//namespace Clustering

#endif //GHSOM_SOMNET_H
