/*
    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 CLUSTERING_ROCK_ROCK_H
#define CLUSTERING_ROCK_ROCK_H

#include "RockHeaps.h"
#include "matrices_all.h"
#include "comparator_all.h"

//#define CLUSTERING_DEBUG_ROCK

namespace clustering
{
	namespace ROCK
	{
		class ROCK
		{
		public:
			ROCK(boost::shared_ptr<Matrices::SuperMatrix> pMatrix,
				boost::shared_ptr<smartcomparator::simfunctions::LocalSimilarityFunction> simfun,
				double simlinktreshold);

			void StartClustering();
			ClusteringBase::ClusterSetPtr GetClusters();
			//ClusteringBase::LinkSetPtr GetLinks();

			void LabelAdditionalData(boost::shared_ptr<Matrices::SuperMatrix> pMatrix);

			void CreateParentsMap();

			void SaveClutoTreeFile(const std::string & path);

		protected:
			boost::shared_ptr<Matrices::SuperMatrix> m_pMatrix;
			boost::shared_ptr<smartcomparator::simfunctions::LocalSimilarityFunction> m_pSimilarityFunction;
			double m_dSimLinkTreshold;
			double m_dFi;
			/**
			Czy interesuje nas hierarchia, czy tylko plaskie grupy
			*/
			bool m_bFlatClustering;

			/**
			Wyjsciowe grupy (patrz na m_stMinSizeOfCluster)
			*/
			ClusteringBase::ClusterSetPtr m_pOutputClusters;

			/**
			   TODO zemienic na ClusteringBase::ClusterTreeNodePtr
			*/
//			ClusteringBase::LinkSetPtr m_pOutputLinks;

			/**
			Wymagana liczba minimalnej liczby grup na najwyzszym poziomie.
			*/
			size_t m_stNumberOfGroups;
			/**
			Parametr uzywany podczas liczenie grup. Okresla ile co najmniej musi byc dokumentow,
			aby stworzyc grupe.
			*/
			size_t m_stMinSizeOfCluster;


			/**
			Powiazanie pomiedzy grupami.
			idGrupy -> (idGrupy, liczba_powiwazan)
			*/
			std::map<size_t, std::map<size_t, int> > m_Links;

			std::vector<size_t> m_vSampleIndices;
			/**
			   TODO: jezeli nie bedzie gdzie indziej uzywane poza CreateInitialLinks,
			   to nalezy wyczyscic to odwzorowanie na koniec CreateInitialLinks
			*/
			std::map<size_t,size_t> m_InitialClusterToSample;
			std::vector<size_t> m_vUnusedIndices;

			/**
			Zbior wszystkich grup
			*/
			ClusteringBase::ClusterSetPtr m_pClusterSet;
			/**
			Indeks dla grup (po id)
			TODO sprawdzic czy uzywane
			*/
			ClusteringBase::ClusterMapPtr m_pClusterMap;

			/**
			Dodaje grupe do m_pClusterSet i m_pClusterMap
			*/
			void AddCluster(ClusteringBase::ClusterPtr &pCluster);

			/**
			Lokalne kopce dla poszczegolnych grup. Odwzorowanie numer grupy -> kopiec.
			Kopiec to wartosc_goodness -> numer_grupy_o_danej_wartosci_goodnes. Kopiec jest
			posortowany malejaco - dlatego pierwszy element z kopca daje najlepsza grupe
			do scalenia dla grupy, ktorej dotyczy ten kopiec.
			TODO poprawic komentarz
			*/
			std::map<size_t, GoodnessHeap> m_LocalHeaps;

			/**
			Globalny kopiec zawiera on dane w nastepujacej postaci: best_goodness -> pCluster.
			Przy czym best_goodnes jest najlepsza (najwieksza) wartoscia jakosci dla
			grupy o
			TODO poprawic komentarz
			*/
			GoodnessHeap m_GlobalHeap;


			/**
			Narazie grupujemy calosc (ewentualnie przekazemy macierz bedaca wycinkiem
			danych)
			*/
			void DrawRandomSample();
			/**
			Narazie nic nie robi - docelowo bedzie liczyc linkTreshold
			*/
			void CountAlgorithmParams();

			/**
			   Oblicza odleglosc pomiedzy wszystkimi punktami ze zbioru sample
			*/
			void CountNeighbors(std::vector< std::vector<size_t> > &n);
			void CreateInitialLinks();

			void CreateSingeltonClusters();

			void Cluster();
			void CreateLocalHeaps();
			/**
			Buduje lokalny kopiec dla danej grupy uzywajac powiazan z wszystkimi innymi
			istniejacymi grupami.
			*/
			void BuildLocalHeap(ClusteringBase::ClusterPtr pCluster);

			void BuildGlobalHeap();
			//void CleanAfterMerge();
			void LabelReminingData();

			int GetNumberOfLinks(size_t c1, size_t c2);
			double CountGoodness(ClusteringBase::ClusterPtr  C1, ClusteringBase::ClusterPtr C2);

			/**
			   Dodaje polaczenia pomiedzy grupami (x,w). W powstalo ze zlaczenia
			   u i v.
			*/
			void AddMergedCluster(ClusteringBase::ClusterPtr w, ClusteringBase::ClusterPtr x,
				ClusteringBase::ClusterPtr u, ClusteringBase::ClusterPtr v);

			std::map<size_t, int> m_ParentsMap;

			void FindParent(ClusteringBase::ClusterPtr cluster);

#ifdef CLUSTERING_DEBUG_ROCK
			void TestLinksDifferences();
#endif

		private:
		};
	}//namespace ROCK
}//namespace Clustering
#endif // CLUSTERING_ROCK_ROCK_H
