#include <iostream>
#include <fstream>
#include "../SummarizationFramework/TubeFeatures/Features/EmptyFeature.h"
#include "../SummarizationFramework/TubeFeatures/Features/SimpleSubtractionFeature.h"
#include "../SummarizationFramework/TubeFeatures/Features/GradientFeature.h"
#include "../SummarizationFramework/TubeFeatures/Processors/VideoSequenceProcessor.h"
#include "../SummarizationFramework/TubeFeatures/Processors/TubeProcessor.h"
#include "../SummarizationFramework/Persistence/V100DataSource/TubeCollectionDataSource.h"
#include "../SummarizationFramework/Helpers/Strings.h"

using namespace SummarizationFramework::Model;
using namespace SummarizationFramework::Persistence::V100DataSource;
using namespace SummarizationFramework::TubeFeatures::Features;
using namespace SummarizationFramework::TubeFeatures::Processors;

#define STEP_BEGIN(name) std::cout << (name) << "..." << std::flush
#define STEP_END() std::cout << std::cout << "done." << std::endl

struct Data
{
	TubeCollectionPtr tubes;
	TubeProcessor::TubeFeaturesCollectionPtr features;
	int videoLength;

	Data()
	{
		tubes = NULL;
		features = NULL;
		videoLength = -1;
	}
};

TubeProcessor::TubeFeaturesCollectionPtr getFeaturesFromTubes(TubeCollectionPtr tubes, TubeProcessor::FeatureExtractorsPtr extractors)
{
	TubeProcessorPtr processor = new TubeProcessor(extractors);
	TubeProcessor::TubeFeaturesCollectionPtr featuresCollection = processor->ProcessCollection(tubes);

	return featuresCollection;
}

TubeProcessor::TubeFeaturesCollectionPtr getFeaturesFromVideoSequence(const std::string & videoFileName, TubeProcessor::FeatureExtractorsPtr extractors)
{
	VideoSequencePtr sequence = new VideoSequence(videoFileName);
	VideoSequenceProcessorPtr processor = new VideoSequenceProcessor(extractors);
	TubeProcessor::TubeFeaturesPtr features = processor->ProcessVideoSequence(sequence);
		
	TubeProcessor::TubeFeaturesCollectionPtr featuresCollection = new TubeProcessor::TubeFeaturesCollection();
	featuresCollection->push_back(features);

	return featuresCollection;
}

int getVideoSequenceLength(const std::string & videoFileName)
{
	VideoSequence sequence(videoFileName);
	return sequence.GetLength();
}

Data getData(int argc, char *argv[], TubeProcessor::FeatureExtractorsPtr extractors)
{
	Data data;

	if(argc == 4 && !strcmp(argv[1], "-t"))
	{
		// command-line args
		std::string tubesDirName = argv[2];
		std::string videoFileName = argv[3];
		
		// load tubes
		PathParams params(tubesDirName);	
		TubeCollectionDataSourcePtr dataSource = new TubeCollectionDataSource(params.GetTubesXmlFilePath(), params);
		
		data.tubes = new TubeCollection(dataSource);
		data.tubes->Load();

		// extract features
		data.features = getFeaturesFromTubes(data.tubes, extractors);
		data.videoLength = getVideoSequenceLength(videoFileName);
	}
	else if(argc == 3 && !strcmp(argv[1], "-v"))
	{
		// command-line args
		std::string videoFileName = argv[2];
		
		// extract features
		data.features = getFeaturesFromVideoSequence(videoFileName, extractors);
		data.videoLength = getVideoSequenceLength(videoFileName);

		// put the whole video virtually into a single tube
		data.tubes = new TubeCollection();
		data.tubes->Insert(new Tube(NULL, SummarizationFramework::FrameSpan(1, data.videoLength), videoFileName, 1));
	}
	else
	{
		std::cerr << "Bad command line arguments." << std::endl
				  << std::endl
		          << "Usage: FeatureExtractor.exe -t tubes_dir video.avi | -v video.avi" << std::endl
				  << std::endl
				  << "tubes_dir...Path to the directory with extracted tubes." << std::endl
				  << "video.avi...Input video sequence." << std::endl;

		exit(1);
	}

	CV_Assert(data.tubes != NULL);
	CV_Assert(data.features != NULL);
	CV_Assert(data.videoLength > 0);

	return data;
}

void storeCsv(std::string filename, ITubeFeaturePtr feature)
{
	ITubeFeature::FeatureData data = feature->GetData();
	std::ofstream file(filename.c_str());

	for(int line = 0; line < data.cols; line++)
	{
		for(int row = 0; row < data.rows; row++)
		{
			file << data.at<double>(row, line);
			
			if(row < data.rows-1)
			{
				file << ";";
			}
		}
		
		file << "\n";
	}

	file.close();
}


int main(int argc, char *argv[])
{
	STEP_BEGIN("preparing feature extractors");
	
	TubeProcessor::FeatureExtractorsPtr extractors = new TubeProcessor::FeatureExtractors();	
	//extractors->push_back(new RandomFeature::Extractor());	
	//extractors->push_back(new EmptyFeature::Extractor());
    //extractors->push_back(new SimpleSubtractionFeature::SingleExtractor());	
	//extractors->push_back(new SimpleSubtractionFeature::MultiExtractor());	
	extractors->push_back(new SimpleSubtractionFeature::MultiVectorExtractor());
	//extractors->push_back(new GradientFeature::SingleExtractor());	
	//extractors->push_back(new GradientFeature::MultiExtractor());	
	extractors->push_back(new GradientFeature::MultiVectorExtractor());
	
	STEP_END();



	STEP_BEGIN("computing features");
	
	Data data = getData(argc, argv, extractors);
	
	STEP_END();
	


	STEP_BEGIN("storing features");
	
	TubeCollection::Iterator tubeDataIt;
	TubeProcessor::TubeFeaturesCollection::iterator tubeFeaturesIt;

	for(tubeDataIt = data.tubes->Begin(), tubeFeaturesIt = data.features->begin(); tubeDataIt != data.tubes->End() && tubeFeaturesIt != data.features->end(); tubeDataIt++, tubeFeaturesIt++)
	{		
		TubePtr tube = *tubeDataIt;
		TubeProcessor::TubeFeaturesPtr features = *tubeFeaturesIt;

		for(TubeProcessor::TubeFeatures::iterator feature = features->begin(); feature != features->end(); feature++)
		{
			std::string filename = tube->GetFileName() + ".tube" + SummarizationFramework::Helpers::Strings::toString<int>(tube->GetId()) + "." + (*feature)->GetId() + ".csv";
			storeCsv(filename, *feature);
		}
	}
	
	STEP_END();

	return 0;
}

