/*
 * Tools related to the MitoPacq, the CBIA time-lapse cell observation simulator.
 *
 * Copyright (C) 2005-2013   Centre for Biomedical Image Analysis (CBIA)
 *
 * This library-related tools is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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 GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <fstream>
#include <iostream>
#include <iomanip>
#include <stdlib.h>
#include <i3d/image3d.h>

#include "../scheduler.h"

using std::cout;
using std::endl;

template <class VT>
void Relabel(i3d::Image3d<VT> &img,const VT Lout,const VT Lin) {
	VT *p=img.GetFirstVoxelAddr();
	const VT *pL=img.GetFirstVoxelAddr() + img.GetImageSize();

	while (p != pL) {
		if (*p == Lout) *p=Lin;
		++p;
	}
}

int main(int argc, char **argv) {
    cout << argv[0] << " - part of the tools set of the OpticalFlow library \n"
         "Copyright (C) 2005-2013 Centre for Biomedical Image Analysis (CBIA)\n"
         "This is free software; see the source for copying conditions. "
         "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR "
         "A PARTICULAR PURPOSE.\n\n";

	//type of mask images
	typedef i3d::GRAY16 MV;

	if (argc != 3) {
		cout << "warning: incorrect params: use " << argv[0] << " from to    (inclusive)\n";
		return(1);
	}

	//names of the metadata text file
	//when preparing tracks for the first image from gtgen outputs
	//const char ORIGtracks[50]="../man_track.txt";
	const char ORIGtracks[50]="../../man_trackORIG.txt";
	//const char ORIGtracks[50]="../../man_trackORIG2.txt";
	const char FINALtracks[50]="man_track.txt";

	//span of processed mask files
	const size_t countFrom=atoi(argv[1]);
	const size_t countTo=atoi(argv[2]);
	cout << "going to process from " << countFrom << " to " << countTo << endl;

	//add IDs of cells which are supposed to be removed from
	//the GT masks, also their daughters will be removed as well
	//
	//FinalPreview images must be regenerated with the new masks
	//using the 'gtgen-ng orig_phant new_mask new_finalPreview'
	std::map<MV,bool> droppers;

	/*
	droppers[27]=true;
	droppers[121]=true;
	droppers[64]=true;
	droppers[139]=true;
	droppers[140]=true;
	droppers[141]=true;
	droppers[57]=true;
	droppers[143]=true;
	droppers[144]=true;
	droppers[145]=true;
	*/


	//now retrieve orig tracking information
	//iterate:
	//	read orig mask image
	//	find out which IDs are present in it
	//	check every found ID with the orig file
	//		and with internal structures
	//	save the possibly modified mask image
	//save the new tracking information

	std::map<MV, TrackRecord<MV> /**/ > tracks;
	std::map<MV,MV> remap;

	std::ifstream OLDtracks(ORIGtracks);
	if (!OLDtracks.is_open()) {
		cout << "warning: orig tracking information not present, can't read " << ORIGtracks << endl;
		return -1;
	}

	MV ID;
	MV maxID=0; //max being used ID

	while (OLDtracks >> ID) {
		//managed to start reading a new line, let's also read the rest of it;
		//
		//here is a reference to a track already existing in the tracks structure
		TrackRecord<MV> &myT=tracks[ID];
		if (ID > maxID) maxID=ID;

		OLDtracks >> myT.fromTimeStamp >> myT.toTimeStamp >> myT.parentID;
		//special marks:
		//will be changed just once when the cell is spotted for the first time
		myT.fromTimeStamp=99999;
		//will be changed every time the cell is spotted
		myT.toTimeStamp=0;

		//identity mapping at the begining
		remap[ID]=ID;
	}
	OLDtracks.close();

	cout << std::setfill('0');

	//MAIN CYCLE
	//note: indicator of employed default constructor is TrackRecord::toTimeStamp == 0
	for (size_t T=countFrom; T <= countTo; ++T) {
		//open input mask
		char fn[50];
		sprintf(fn,"man_track%.3lu.tif",T);
		cout << "reading: " << fn << endl;
		i3d::Image3d<MV> frame(fn);

		//learn what IDs are present in the current frame
		std::map<MV,int> presentIDs;
		for (size_t i=0; i < frame.GetImageSize(); ++i) {
			const MV seenID=frame.GetVoxel(i);
			if (seenID > 0) presentIDs[seenID]=1;
		}

		//process them
		std::map<MV,int>::iterator pIDs=presentIDs.begin();
		while (pIDs != presentIDs.end()) {
			//process discovered ID
			ID=pIDs->first;

			//does it have different name?
			MV nID=remap[ID];
			cout << "found ID=" << ID << ", which is remapped to ID=" << nID << endl;

			if (nID == 0) {
				//this ID is in image but not in the tracks structure...
				cout << "warning: DETECTED PROBLEM WITH ID=" << ID
				     << " BEING IN THE IMAGE BUT NOT IN THE STRUCTURES, QUITING...\n";
				exit(1);
				/*
				//setup a new track
				tracks[ID].fromTimeStamp=T;
				tracks[ID].toTimeStamp=T;
				tracks[ID].parentID=0;
				*/
			}
			
			if (tracks[nID].toTimeStamp < T-1) {
				//this ID has not been seen in the previous frame,
				//so the ID is returning from "out of scene visit"
				//and we may need to assing it a new ID

				//if fromTimeStamp == 99999 then despite the cell is returning, it
				//hasn't left from the scene because we have never seen it here before;
				//hence, no need to assign a _new_ ID
				if (tracks[nID].fromTimeStamp < 99999) {
					//we should assign a new ID
					nID= ++maxID;
					cout << "tracking event: ID=" << remap[ID] << " was last seen in frame "
					     << tracks[remap[ID]].toTimeStamp
					     << ", now re-appeared in frame " << T
					     << " and got new ID=" << nID << endl;
					remap[ID]=nID;

					//and create it a new track
					tracks[nID].fromTimeStamp=T;
					tracks[nID].toTimeStamp=T;
					tracks[nID].parentID=0;
					//note that if daughter cell returns from trip,
					//we no longer remember who was her mother;
					//it is just some new cell to us now

					//no new initial identity mapping for relabeled tracks
					//so that if the same ID suddenly appears in the image,
					//we can detect and report it
				}
			}

			//confirm we have seen this cell in this frame
			tracks[nID].toTimeStamp=T;
			if (nID != ID) {
				cout << "relabeling image: from ID=" << ID
				     << " to ID=" << nID << " in frame " << T << endl;
				Relabel(frame,ID,nID);
			}

			if (droppers[nID]) Relabel(frame,nID,(MV)0);

			//first time we meet?
			if (tracks[nID].fromTimeStamp==99999) {
				tracks[nID].fromTimeStamp=T;

				//also update ID of my mother in my track, if we have some
				//(note that it holds remap[0]==0)
				MV mID=remap[tracks[nID].parentID];

				if (mID) {
					//okay, I do have mother and her current name is mID,
					//but hey, what if I was born when my mother was out of
					//the scene, then we must forget this tracking information
					//and pretend like I have just came here from a trip as well
					//
					//if my mother was in the last scene, the T-1 is included
					//in her life-span...
					if ((tracks[mID].fromTimeStamp <= T-1)
					  && (T-1 <= tracks[mID].toTimeStamp))
						tracks[nID].parentID=mID;
					else
						tracks[nID].parentID=0;
				}

				//remove parent if mother was discarded
				if (droppers[mID]) tracks[nID].parentID=0;

				cout << "tracking event: ID=" << nID
				     << " was spotted for the first time in frame "
				     << T << ", parent has ID=" << mID << endl;
			}

			cout << "confirming presence of ID=" << nID << " in frame "
			     << std::setw(3) << T << endl;

			//next ID, please...
			pIDs++;
		}

		//report who was not found from those who we have seen in the last frame
		std::map<MV, TrackRecord<MV> /**/ >::iterator itTr;
		for (itTr = tracks.begin(); itTr != tracks.end(); itTr++)
			if ((itTr->second.fromTimeStamp < 99999) //was seen
			  && (itTr->second.toTimeStamp == T-1)) //lastly in the last frame
				cout << "tracking event: ID=" << itTr->first
				     << " is now missing in frame " << T << endl;

		//write polished final mask
		sprintf(fn,"man_track%.3lu.tif",T);
		cout << "writing: " << fn << endl;
		frame.SaveImage(fn);
	}


	std::ofstream NEWtracks(FINALtracks);

	 std::map<MV, TrackRecord<MV> /**/ >::iterator itTr;
	 for (itTr = tracks.begin(); itTr != tracks.end(); itTr++)
	 {
	 	if (itTr->second.fromTimeStamp == 99999) {
			cout << "warning: ID=" << itTr->first << " was never spotted in the images\n";
		} else if (!droppers[itTr->first]) {
			NEWtracks << itTr->first << " " 
				 << itTr->second.fromTimeStamp << " "
				 << itTr->second.toTimeStamp << " "
				 << itTr->second.parentID << endl;
			if (itTr->second.toTimeStamp - itTr->second.fromTimeStamp <= 3)
				cout << "warning: ID=" << itTr->first << " lived very shortly\n";
		}
	 }

	if (!NEWtracks.good()) cout << "warning: Error writing final tracks " << FINALtracks << endl;
	NEWtracks.close();

	return(0);
}
