/*
 * Detect_Breapoints.cpp
 *
 *  Created on: Jun 19, 2015
 *      Author: fsedlaze
 */

#include "Detect_Breakpoints.h"
#include "../print/IPrinter.h"

void store_pos(vector<hist_str> &positions, long pos, std::string read_name) {
	for (size_t i = 0; i < positions.size(); i++) {
		if (abs(positions[i].position - pos) < Parameter::Instance()->min_length) {
			positions[i].hits++;
			positions[i].names.push_back(read_name);
			return;
		}
	}
	hist_str tmp;
	tmp.position = pos;
	tmp.hits = 1;
	tmp.names.push_back(read_name);
	positions.push_back(tmp);
}

std::string reverse_complement(std::string sequence) {
	std::string tmp_seq;
	for (std::string::reverse_iterator i = sequence.rbegin(); i != sequence.rend(); i++) {
		switch ((*i)) {
		case 'A':
			tmp_seq += 'T';
			break;
		case 'C':
			tmp_seq += 'G';
			break;
		case 'G':
			tmp_seq += 'C';
			break;
		case 'T':
			tmp_seq += 'A';
			break;
		default:
			tmp_seq += (*i);
			break;
		}
	}
	return tmp_seq;
}

Breakpoint * split_points(vector<std::string> names, std::map<std::string, read_str> support) {
	std::map<std::string, read_str> new_support;
	for (size_t i = 0; i < names.size(); i++) {
		new_support[names[i]] = support[names[i]];
	}
	position_str svs;
	svs.start.min_pos = 0; //just to initialize. Should not be needed anymore at this stage of the prog.
	svs.stop.max_pos = 0;
	svs.support = new_support;
	Breakpoint * point = new Breakpoint(svs, (*new_support.begin()).second.coordinates.second - (*new_support.begin()).second.coordinates.first);
	return point;
}

void detect_merged_svs(position_str point, RefVector ref, vector<Breakpoint *> & new_points) {
	new_points.clear(); //just in case!
	vector<hist_str> pos_start;
	vector<hist_str> pos_stop;
	for (std::map<std::string, read_str>::iterator i = point.support.begin(); i != point.support.end(); ++i) {
		store_pos(pos_start, (*i).second.coordinates.first, (*i).first);
		store_pos(pos_stop, (*i).second.coordinates.second, (*i).first);
	}

	int start_count = 0;
	for (size_t i = 0; i < pos_start.size(); i++) {
		//std::cout<<pos_start[i].hits <<",";
		if (pos_start[i].hits > Parameter::Instance()->min_support) {
			start_count++;

		}
	}
	int stop_count = 0;
	for (size_t i = 0; i < pos_stop.size(); i++) {
		//	std::cout << pos_stop[i].hits << ",";
		if (pos_stop[i].hits > Parameter::Instance()->min_support) {
			stop_count++;
		}
	}
	if (stop_count > 1 || start_count > 1) {
		//	std::cout << "\tprocessing merged TRA" << std::endl;
		if (start_count > 1) {
			new_points.push_back(split_points(pos_start[0].names, point.support));
			new_points.push_back(split_points(pos_start[1].names, point.support));
		} else {
			new_points.push_back(split_points(pos_stop[0].names, point.support));
			new_points.push_back(split_points(pos_stop[1].names, point.support));
		}
	}
}

std::string TRANS_type(char type) {
	string tmp;
	if (type & DEL) {
		tmp += "DEL";
	}
	if (type & INV) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "INV";
	}
	if (type & DUP) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "DUP";
	}
	if (type & INS) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "INS";
	}
	if (type & TRA) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "TRA";
	}
	if (type & NEST) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "NEST";
	}
	return tmp; // should not occur!
}

long get_ref_lengths(int id, RefVector ref) {
	long length = 0;

	for (size_t i = 0; i < (size_t) id && i < ref.size(); i++) {
		length += (long) ref[i].RefLength + (long) Parameter::Instance()->max_dist;
	}
	return length;
}

bool should_be_stored(Breakpoint *& point) {
	point->calc_support(); // we need that before:
	//std::cout << "Stored: " << point->get_support() << " " << point->get_length() << std::endl;
	if (point->get_SVtype() & TRA) { // we cannot make assumptions abut support yet.
		point->set_valid((bool) (point->get_support() > 1)); // this is needed as we take each chr independently and just look at the primary alignment
	} else if (point->get_support() >= Parameter::Instance()->min_support) {
		point->predict_SV();
		point->set_valid((bool) (point->get_length() > Parameter::Instance()->min_length));
	}
	return point->get_valid();
}
void polish_points(std::vector<Breakpoint *> & points, RefVector ref) { //TODO might be usefull! but why does the tree not fully work??
	return;
	for (size_t i = 0; i < points.size(); i++) {
		if (points[i]->get_SVtype() & INS && (points[i]->get_length() == Parameter::Instance()->huge_ins)) {
			for (size_t j = 0; j < points.size(); j++) {
				if (i != j) {
					if (abs(points[i]->get_coordinates().start.min_pos - points[j]->get_coordinates().start.min_pos) < Parameter::Instance()->max_dist || abs(points[i]->get_coordinates().stop.max_pos - points[j]->get_coordinates().stop.max_pos) < Parameter::Instance()->max_dist) {
						std::cout << "HIT!: " << points[j]->get_coordinates().start.min_pos << " " << points[i]->get_coordinates().start.min_pos << " " << points[j]->get_coordinates().stop.max_pos << " " << points[i]->get_coordinates().stop.max_pos << " len: " << points[j]->get_length() << " " << points[i]->get_length() << std::endl;
						break;
					}
				}

			}
		}
	}
}

std::string TRANS_type2(char type) {
	string tmp;
	if (type & DEL) {
		tmp += "DEL";
	}
	if (type & INV) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "INV";
	}
	if (type & DUP) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "DUP";
	}
	if (type & INS) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "INS";
	}
	if (type & TRA) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "TRA";
	}
	if (type & NEST) {
		if (!tmp.empty()) {
			tmp += '/';
		}
		tmp += "NEST";
	}
	if (tmp.empty()) {
		cout << "ERR?" << endl;
	}
	return tmp; // should not occur!
}

void detect_breakpoints(std::string read_filename, IPrinter *& printer) {
	estimate_parameters(read_filename);
	BamParser * mapped_file = 0;
	RefVector ref;
	if (read_filename.find("bam") != string::npos) {
		mapped_file = new BamParser(read_filename);
		ref = mapped_file->get_refInfo();
	} else {
		cerr << "File Format not recognized. File must be a sorted .bam file!" << endl;
		exit(EXIT_FAILURE);
	}
//Using PlaneSweep to comp coverage and iterate through reads:
//PlaneSweep * sweep = new PlaneSweep();
	//Using Interval tree to store and manage breakpoints:

	IntervallTree final;
	IntervallTree bst;

	TNode * root_final = NULL;
	int current_RefID = 0;

	TNode *root = NULL;
//FILE * alt_allel_reads;
	FILE * ref_allel_reads;
	if (Parameter::Instance()->genotype) {
		ref_allel_reads = fopen(Parameter::Instance()->tmp_genotyp.c_str(), "w");
		//	ref_allel_reads = fopen(Parameter::Instance()->tmp_genotyp.c_str(), "wb");
	}
	Alignment * tmp_aln = mapped_file->parseRead((uint16_t) Parameter::Instance()->min_mq);
	long ref_space = get_ref_lengths(tmp_aln->getRefID(), ref);
	long num_reads = 0;

	/*Genotyper * go;
	 if (Parameter::Instance()->genotype) {
	 go = new Genotyper();
	 }*/
	std::cout << "Start parsing... " << ref[tmp_aln->getRefID()].RefName << std::endl;

	while (!tmp_aln->getQueryBases().empty()) {

		if ((tmp_aln->getAlignment()->IsPrimaryAlignment() && tmp_aln->get_is_save()) && !(tmp_aln->getAlignment()->AlignmentFlag & 0x800)){

				//change CHR:
			if (current_RefID != tmp_aln->getRefID()) {

				std::cout << "\tSwitch Chr " << ref[tmp_aln->getRefID()].RefName << std::endl;	//" " << ref[tmp_aln->getRefID()].RefLength
				std::vector<Breakpoint *> points;
				bst.get_breakpoints(root, points);
				//polish_points(points, ref);

				/*	if (Parameter::Instance()->genotype) {
				 fclose(ref_allel_reads);
				 cout<<"\t\tGenotyping"<<endl;
				 go->update_SVs(points, ref_space);
				 cout<<"\t\tGenotyping finished"<<endl;
				 ref_allel_reads = fopen(Parameter::Instance()->tmp_genotyp.c_str(), "wb");
				 }*/

				std::cout << "\t\tReassessing breakpoints" << std::endl;
				for (int i = 0; i < points.size(); i++) {
					points[i]->calc_support();
				}
				int i = 0;
				int pos = 0;
				while (i < points.size()) {
					if (points[i]->get_support() > min(3, Parameter::Instance()->min_support)) {
						int dist = min(int(points[i]->get_coordinates().stop.most_support - points[i]->get_coordinates().start.most_support), Parameter::Instance()->max_dist);
						if (pos != i && abs(points[i]->get_coordinates().start.most_support - points[pos]->get_coordinates().start.most_support) < dist / 2 && abs(points[i]->get_coordinates().stop.most_support - points[pos]->get_coordinates().stop.most_support) < dist / 2 && points[i]->get_SVtype() == points[pos]->get_SVtype()) {
							//	cout << "potential hit: " << TRANS_type2(points[i]->get_SVtype()) << " " << TRANS_type2(points[i]->get_SVtype()) << " " << points[i]->get_coordinates().start.most_support << " " << points[pos]->get_coordinates().start.most_support << " " << points[i]->get_coordinates().stop.most_support << " " << points[pos]->get_coordinates().stop.most_support << "supp: " << points[i]->get_support() << " " << points[pos]->get_support() << endl;
							points[i]->integrate(points[pos]);
							points[pos]->set_valid(false);
						}
						pos = i;
					}
					i++;
				}

				for (int i = 0; i < points.size(); i++) {
					if (points[i]->get_valid()) {
						//invoke update over ref support!
						if (points[i]->get_SVtype() & TRA) {
							final.insert(points[i], root_final);
						} else {
							printer->printSV(points[i]);
						}
					}
				}
				std::cout << "\t\tContinue parsing" << std::endl;
				bst.clear(root);
				current_RefID = tmp_aln->getRefID();
				ref_space = get_ref_lengths(tmp_aln->getRefID(), ref);
			}

			if (strcmp(tmp_aln->getName().c_str(), Parameter::Instance()->read_name.c_str()) == 0) {
				cout << "Found read! " << endl;
			}

			//SCAN read:
			std::vector<str_event> aln_event;
			std::vector<aln_str> split_events;
			if (tmp_aln->getMappingQual() > Parameter::Instance()->min_mq) {
				//double score = tmp_aln->get_scrore_ratio();

				//

#pragma omp parallel // starts a new team
				{
#pragma omp sections
					{
#pragma omp section
						{
							//		clock_t begin = clock();
							//if ((score == -1 || score > Parameter::Instance()->score_treshold)) {
							aln_event = tmp_aln->get_events_Aln();
							if (strcmp(tmp_aln->getName().c_str(), Parameter::Instance()->read_name.c_str()) == 0) {
								cout << "Found read events: " << aln_event.size() << endl;
							}

							//}
							//		Parameter::Instance()->meassure_time(begin, " Alignment ");
						}
#pragma omp section
						{
							//	clock_t begin_split = clock();
							split_events = tmp_aln->getSA(ref);
							if (strcmp(tmp_aln->getName().c_str(), Parameter::Instance()->read_name.c_str()) == 0) {
								cout << "Found read split events: " << split_events.size() << endl;
							}
							//	Parameter::Instance()->meassure_time(begin_split, " Split reads ");
						}
					}
				}

				//	bool flag = (strcmp(tmp_aln->getName().c_str(), Parameter::Instance()->read_name.c_str()) == 0);

				//tmp_aln->set_supports_SV(aln_event.empty() && split_events.empty());

				//Store reference supporting reads for genotype estimation:
				if (Parameter::Instance()->genotype && (aln_event.empty() && split_events.empty())) {
					//write read:
					write_read(tmp_aln, ref_allel_reads);
				}

				//store the potential SVs:
				if (!aln_event.empty()) {
					add_events(tmp_aln, aln_event, 0, ref_space, bst, root, num_reads, false);
				}
				if (!split_events.empty()) {
					if (strcmp(tmp_aln->getName().c_str(), Parameter::Instance()->read_name.c_str()) == 0) {
						cout << "Found read split events: " << split_events.size() << endl;
					}
					add_splits(tmp_aln, split_events, 1, ref, bst, root, num_reads, false);
				}
			}
		}
		//get next read:
		mapped_file->parseReadFast((uint16_t) Parameter::Instance()->min_mq, tmp_aln);

		num_reads++;

		if (num_reads % 10000 == 0) {
			cout << "\t\t# Processed reads: " << num_reads << endl;
		}
	}
//filter and copy results:
	std::cout << "\tFinalizing  .." << std::endl;
//	cout << "SUMARIZE 2 " << endl;
	std::vector<Breakpoint *> points;
	bst.get_breakpoints(root, points);

	for (int i = 0; i < points.size(); i++) {
		points[i]->calc_support();
	}
	int i = 0;
	int pos = 0;
	while (i < points.size()) {
		if (points[i]->get_support() > 3) {
			int dist = min(int(points[i]->get_coordinates().stop.most_support - points[i]->get_coordinates().start.most_support), Parameter::Instance()->max_dist);
			if (pos != i && abs(points[i]->get_coordinates().start.most_support - points[pos]->get_coordinates().start.most_support) < dist / 2 && abs(points[i]->get_coordinates().stop.most_support - points[pos]->get_coordinates().stop.most_support) < dist / 2 && points[i]->get_SVtype() == points[pos]->get_SVtype()) {
				//	cout << "potential hit: " << TRANS_type2(points[i]->get_SVtype()) << " " << TRANS_type2(points[i]->get_SVtype()) << " " << points[i]->get_coordinates().start.most_support << " " << points[pos]->get_coordinates().start.most_support << " " << points[i]->get_coordinates().stop.most_support << " " << points[pos]->get_coordinates().stop.most_support << "supp: " << points[i]->get_support() << " " << points[pos]->get_support() << endl;
				points[i]->integrate(points[pos]);
				points[pos]->set_valid(false);
			}
			pos = i;
		}
		i++;
	}

	for (int i = 0; i < points.size(); i++) {
		if (points[i]->get_valid()) {
			//invoke update over ref support!
			if (points[i]->get_SVtype() & TRA) {
				final.insert(points[i], root_final);
			} else {
				printer->printSV(points[i]);
			}
		}
	}

	bst.clear(root);
	points.clear();
	final.get_breakpoints(root_final, points);
	//std::cout<<"Detect merged tra"<<std::endl;
	size_t points_size = points.size();
	for (size_t i = 0; i < points_size; i++) { // its not nice, but I may alter the length of the vector within the loop.
		if (points[i]->get_SVtype() & TRA) {
			vector<Breakpoint *> new_points;
			detect_merged_svs(points[i]->get_coordinates(), ref, new_points);
			if (!new_points.empty()) {							// I only allow for 1 split!!
				points[i] = new_points[0];
				points.push_back(new_points[1]);
			}
		}
	}
	//std::cout<<"fin up"<<std::endl;
	for (size_t i = 0; i < points.size(); i++) {
		if (points[i]->get_SVtype() & TRA) {
			points[i]->calc_support();
			points[i]->predict_SV();
		}
		if (points[i]->get_support() >= Parameter::Instance()->min_support && points[i]->get_length() > Parameter::Instance()->min_length) {
			printer->printSV(points[i]);
		}
	}
	//std::cout<<"Done"<<std::endl;/
	if (Parameter::Instance()->genotype) {
		fclose(ref_allel_reads);
	}
	delete mapped_file;
}

void add_events(Alignment *& tmp, std::vector<str_event> events, short type, long ref_space, IntervallTree & bst, TNode *&root, long read_id, bool add) {

	bool flag = (strcmp(tmp->getName().c_str(), Parameter::Instance()->read_name.c_str()) == 0);
	for (size_t i = 0; i < events.size(); i++) {
		//	if (events[i - 1].length > Parameter::Instance()->min_segment_size || events[i].length > Parameter::Instance()->min_segment_size) {
		position_str svs;
		read_str read;
		if (events[i].is_noise) {
			read.type = 2;
		} else {
			read.type = 0;
		}
		read.SV = events[i].type;
		read.sequence = events[i].sequence;

		if (flag) {
			std::cout << "ADD EVENT " << tmp->getName() << " " << tmp->getRefID() << " " << events[i].pos << " " << events[i].pos + ref_space << " " << abs(events[i].length) << std::endl;
		}
		svs.start.min_pos = (long) events[i].pos + ref_space;
		svs.stop.max_pos = svs.start.min_pos + events[i].length;

		if (tmp->getStrand()) {
			read.strand.first = (tmp->getStrand());
			read.strand.second = !(tmp->getStrand());
		} else {
			read.strand.first = !(tmp->getStrand());
			read.strand.second = (tmp->getStrand());
		}
		//	start.support[0].read_start.min = events[i].read_pos;

		read.read_strand.first = tmp->getStrand();
		read.read_strand.second = tmp->getStrand();
		if (flag) {
			std::cout << tmp->getName() << " " << tmp->getRefID() << " " << svs.start.min_pos << " " << svs.stop.max_pos << " " << svs.stop.max_pos - svs.start.min_pos << std::endl;
		}

		if (svs.start.min_pos > svs.stop.max_pos) {
			//can this actually happen?
			read.coordinates.first = svs.stop.max_pos;
			read.coordinates.second = svs.start.min_pos;
		} else {
			read.coordinates.first = svs.start.min_pos;
			read.coordinates.second = svs.stop.max_pos;
		}

		svs.start.max_pos = svs.start.min_pos;
		svs.stop.min_pos = svs.stop.max_pos;

		if (svs.start.min_pos > svs.stop.max_pos) { //incase they are inverted
			svs_breakpoint_str pos = svs.start;
			svs.start = svs.stop;
			svs.stop = pos;
			pair<bool, bool> tmp = read.strand;
			read.strand.first = tmp.second;
			read.strand.second = tmp.first;
		}

		//TODO: we might not need this:
		if (svs.start.min_pos > svs.stop.max_pos) {
			read.coordinates.first = svs.stop.max_pos;
			read.coordinates.second = svs.start.min_pos;
		} else {
			read.coordinates.first = svs.start.min_pos;
			read.coordinates.second = svs.stop.max_pos;
		}

		//	read.id = read_id;
		svs.support[tmp->getName()] = read;
		svs.support[tmp->getName()].length = events[i].length;
		Breakpoint * point = new Breakpoint(svs, events[i].length);
		if (add) {
			bst.insert_existant(point, root);
		} else {
			bst.insert(point, root);
		}
		if (flag) {
			cout << "DONE MERGE" << endl;
		}
		//std::cout<<"Print:"<<std::endl;
		//bst.print(root);
	}
//	}
}

void add_splits(Alignment *& tmp, std::vector<aln_str> events, short type, RefVector ref, IntervallTree& bst, TNode *&root, long read_id, bool add) {
	bool flag = false;		//(strcmp(tmp->getName().c_str(), Parameter::Instance()->read_name.c_str()) == 0);

	if (flag) {
		cout << "SPLIT: " << std::endl;
		for (size_t i = 0; i < events.size(); i++) {
			std::cout << events[i].pos << " stop: " << events[i].pos + events[i].length << " " << events[i].RefID << " READ: " << events[i].read_pos_start << " " << events[i].read_pos_stop;
			if (events[i].strand) {
				cout << " +" << endl;
			} else {
				cout << " -" << endl;
			}
		}
	}

	for (size_t i = 1; i < events.size(); i++) {
		//	if (events[i - 1].length > Parameter::Instance()->min_segment_size || events[i].length > Parameter::Instance()->min_segment_size) {
		position_str svs;
		//position_str stop;
		read_str read;
		read.sequence = "NA";
		//read.name = tmp->getName();
		read.type = type;
		read.SV = 0;
		read.read_strand.first = events[i - 1].strand;
		read.read_strand.second = events[i].strand;

		//stop.support.push_back(read);
		if (events[i].RefID == events[i - 1].RefID) { //IF different chr -> tra
			if (events[i - 1].strand == events[i].strand) { //IF same strand -> del/ins/dup
				if (events[i - 1].strand) {
					read.strand.first = events[i - 1].strand;
					read.strand.second = !events[i].strand;
				} else {
					read.strand.first = !events[i - 1].strand;
					read.strand.second = events[i].strand;
				}
				//	int len1 = 0;
				//int len2 = 0;
				svs.read_start = events[i - 1].read_pos_stop; // (short) events[i - 1].read_pos_start + (short) events[i - 1].length;
				svs.read_stop = events[i].read_pos_start;
				if (events[i - 1].strand) {
					svs.start.min_pos = events[i - 1].pos + events[i - 1].length + get_ref_lengths(events[i - 1].RefID, ref);
					svs.stop.max_pos = events[i].pos + get_ref_lengths(events[i].RefID, ref);
				} else {
					svs.start.min_pos = events[i].pos + events[i].length + get_ref_lengths(events[i].RefID, ref);
					svs.stop.max_pos = events[i - 1].pos + get_ref_lengths(events[i - 1].RefID, ref);
				}

				if (flag) {
					cout << "Debug: SV_Size: " << (svs.start.min_pos - svs.stop.max_pos) << " tmp: " << (svs.stop.max_pos - svs.start.min_pos) << " Ref_start: " << svs.start.min_pos - get_ref_lengths(events[i].RefID, ref) << " Ref_stop: " << svs.stop.max_pos - get_ref_lengths(events[i].RefID, ref) << " readstart: " << svs.read_start << " readstop: " << svs.read_stop << std::endl;
				}

				if ((svs.stop.max_pos - svs.start.min_pos) > Parameter::Instance()->min_length * -1 && ((svs.stop.max_pos - svs.start.min_pos) + (Parameter::Instance()->min_length) < (svs.read_stop - svs.read_start) && (svs.read_stop - svs.read_start) > (Parameter::Instance()->min_length * 2))) {
					if (!events[i].cross_N || (double) ((svs.stop.max_pos - svs.start.min_pos) + Parameter::Instance()->min_length) < ((double) (svs.read_stop - svs.read_start) * Parameter::Instance()->avg_ins)) {
						svs.stop.max_pos += (svs.read_stop - svs.read_start); //TODO check!
						if (Parameter::Instance()->print_seq) {
							svs.read_stop = events[i].read_pos_start;
							svs.read_start = events[i - 1].read_pos_stop;


							if (svs.read_stop > tmp->getAlignment()->QueryBases.size()) {
								cerr << "BUG: split read ins! " << svs.read_stop << " " << tmp->getAlignment()->QueryBases.size() << " " << tmp->getName() << endl;
							}
							if (!events[i - 1].strand) {
								std::string tmp_seq = reverse_complement(tmp->getAlignment()->QueryBases);

								read.sequence = reverse_complement(tmp_seq.substr(svs.read_start, svs.read_stop - svs.read_start));
							} else {
								read.sequence = tmp->getAlignment()->QueryBases.substr(svs.read_start, svs.read_stop - svs.read_start);
							}
							if (flag) {
								cout << "INS: " << endl;
								cout << "split read ins! " << events[i - 1].read_pos_stop << " " << events[i].read_pos_start << " " << " " << tmp->getAlignment()->QueryBases.size() << " " << tmp->getName() << endl;
								cout << "Seq+:" << read.sequence << endl;
							}
						}
						read.SV |= INS;
					} else {
						read.SV |= 'n';
					}

				} else if ((svs.start.min_pos - svs.stop.max_pos) * -1 > (svs.read_stop - svs.read_start) + (Parameter::Instance()->min_length)) {
					if (!events[i].cross_N || (double) (svs.start.min_pos - svs.stop.max_pos) * Parameter::Instance()->avg_del * -1.0 > (double) ((svs.read_stop - svs.read_start) + (Parameter::Instance()->min_length))) {
						read.SV |= DEL;
						if (flag) {
							cout << "DEL2" << endl;
						}
					} else {
						read.SV |= 'n';
					}

				} else if ((svs.start.min_pos - svs.stop.max_pos) > Parameter::Instance()->min_length && (svs.read_start - svs.read_stop) < Parameter::Instance()->min_length) { //check with respect to the coords of reads!
					if (flag) {
						cout << "DUP: " << endl;
					}
					read.SV |= DUP;
				} else {
					if (flag) {
						cout << "N" << endl;
					}
					read.SV = 'n';
				}
			} else { // if first part of read is in a different direction as the second part-> INV

				read.strand.first = events[i - 1].strand;
				read.strand.second = !events[i].strand; //TODO think about this! potential not!

				bool is_overlapping = overlaps(events[i - 1], events[i]);
				if (is_overlapping && (events[i - 1].length > Parameter::Instance()->min_segment_size || events[i].length > Parameter::Instance()->min_segment_size)) {
					if (flag) {
						std::cout << "Overlap curr: " << events[i].pos << " " << events[i].pos + events[i].length << " prev: " << events[i - 1].pos << " " << events[i - 1].pos + events[i - 1].length << " " << tmp->getName() << std::endl;
					}
					read.SV |= NEST;

					if (events[i - 1].strand) {
						svs.start.min_pos = events[i - 1].pos + events[i - 1].length + get_ref_lengths(events[i - 1].RefID, ref);
						svs.stop.max_pos = (events[i].pos + events[i].length) + get_ref_lengths(events[i].RefID, ref);
					} else {
						svs.start.min_pos = events[i - 1].pos + get_ref_lengths(events[i - 1].RefID, ref);
						svs.stop.max_pos = events[i].pos + get_ref_lengths(events[i].RefID, ref);
					}

					if (svs.start.min_pos > svs.stop.max_pos) {
						long tmp = svs.start.min_pos;
						svs.start.min_pos = svs.stop.max_pos;
						svs.stop.max_pos = tmp;
					}
				} else if (!is_overlapping) {
					read.SV |= INV;
					if (events[i - 1].strand) {
						svs.start.min_pos = events[i - 1].pos + events[i - 1].length + get_ref_lengths(events[i - 1].RefID, ref);
						svs.stop.max_pos = (events[i].pos + events[i].length) + get_ref_lengths(events[i].RefID, ref);
					} else {
						svs.start.min_pos = events[i - 1].pos + get_ref_lengths(events[i - 1].RefID, ref);
						svs.stop.max_pos = events[i].pos + get_ref_lengths(events[i].RefID, ref);
					}
				}
			}

		} else { //if not on the same chr-> TRA
			read.strand.first = events[i - 1].strand;
			read.strand.second = !events[i].strand;
			if (events[i - 1].strand == events[i].strand) {
				//check this with + - strands!!

				if (events[i - 1].strand) {
					svs.start.min_pos = events[i - 1].pos + events[i - 1].length + get_ref_lengths(events[i - 1].RefID, ref);
					svs.stop.max_pos = events[i].pos + get_ref_lengths(events[i].RefID, ref);
				} else {
					svs.start.min_pos = events[i - 1].pos + get_ref_lengths(events[i - 1].RefID, ref);
					svs.stop.max_pos = events[i].pos + events[i].length + get_ref_lengths(events[i].RefID, ref);
				}
			} else {
				if (events[i - 1].strand) {
					svs.start.min_pos = events[i - 1].pos + events[i - 1].length + get_ref_lengths(events[i - 1].RefID, ref);
					svs.stop.max_pos = events[i].pos + events[i].length + get_ref_lengths(events[i].RefID, ref);
				} else {
					svs.start.min_pos = events[i - 1].pos + get_ref_lengths(events[i - 1].RefID, ref);
					svs.stop.max_pos = events[i].pos + get_ref_lengths(events[i].RefID, ref);
				}
			}
			read.SV |= TRA;
		}

		if (read.SV != 'n') {
			if (flag) {
				std::cout << "SPLIT: " << TRANS_type(read.SV) << " start: " << svs.start.min_pos - get_ref_lengths(events[i].RefID, ref) << " stop: " << svs.stop.max_pos - get_ref_lengths(events[i].RefID, ref);
				if (events[i - 1].strand) {
					std::cout << " +";
				} else {
					std::cout << " -";
				}
				if (events[i].strand) {
					std::cout << " +";
				} else {
					std::cout << " -";
				}
				std::cout << " " << tmp->getName() << std::endl;
				std::cout << "READ: " << svs.read_start << " " << svs.read_stop << " " << svs.read_start - svs.read_stop << std::endl;
			}
			//std::cout<<"split"<<std::endl;

			svs.start.max_pos = svs.start.min_pos;
			svs.stop.min_pos = svs.stop.max_pos;
			if (svs.start.min_pos > svs.stop.max_pos) {
				//maybe we have to invert the directions???
				svs_breakpoint_str pos = svs.start;
				svs.start = svs.stop;
				svs.stop = pos;

				pair<bool, bool> tmp = read.strand;

				read.strand.first = tmp.second;
				read.strand.second = tmp.first;
			}

			//TODO: we might not need this:
			if (svs.start.min_pos > svs.stop.max_pos) {
				read.coordinates.first = svs.stop.max_pos;
				read.coordinates.second = svs.start.min_pos;
			} else {
				read.coordinates.first = svs.start.min_pos;
				read.coordinates.second = svs.stop.max_pos;
			}

			//pool out?
			read.id = read_id;
			svs.support[tmp->getName()] = read;
			svs.support[tmp->getName()].length = abs(read.coordinates.second - read.coordinates.first);
			Breakpoint * point = new Breakpoint(svs, abs(read.coordinates.second - read.coordinates.first));
			//std::cout<<"split ADD: " << <<" Name: "<<tmp->getName()<<" "<< svs.start.min_pos- get_ref_lengths(events[i].RefID, ref)<<"-"<<svs.stop.max_pos- get_ref_lengths(events[i].RefID, ref)<<std::endl;
			if (add) {
				bst.insert_existant(point, root);
			} else {
				bst.insert(point, root);
			}
			//	std::cout<<"Print:"<<std::endl;
			//	bst.print(root);
		}
	}
	//}
}

void estimate_parameters(std::string read_filename) {
	if (Parameter::Instance()->skip_parameter_estimation) {
		return;
	}
	cout << "Estimating parameter..." << endl;
	BamParser * mapped_file = 0;
	RefVector ref;
	if (read_filename.find("bam") != string::npos) {
		mapped_file = new BamParser(read_filename);
		ref = mapped_file->get_refInfo();
	} else {
		cerr << "File Format not recognized. File must be a sorted .bam file!" << endl;
		exit(EXIT_FAILURE);
	}

	Alignment * tmp_aln = mapped_file->parseRead(Parameter::Instance()->min_mq);
	double num = 0;
	double avg_score = 0;
	double avg_mis = 0;
	double avg_indel = 0;
	double avg_diffs_perwindow = 0;
	vector<int> mis_per_window; //histogram over #differences
	vector<int> scores;
//	std::string curr, prev = "";
	double avg_dist = 0;
	double tot_avg_ins = 0;
	double tot_avg_del = 0;
	while (!tmp_aln->getQueryBases().empty() && num < 1000) {	//1000
		//	std::cout<<"test "<<tmp_aln->getName()<<std::endl;
		if (rand() % 100 < 20 && ((tmp_aln->getAlignment()->IsPrimaryAlignment()) && (!(tmp_aln->getAlignment()->AlignmentFlag & 0x800)))) {				//}&& tmp_aln->get_is_save()))) {
			//1. check differences in window => min_treshold for scanning!
			//2. get score ration without checking before hand! (above if!)
			double dist = 0;
			double avg_del = 0;
			double avg_ins = 0;
			vector<int> tmp = tmp_aln->get_avg_diff(dist, avg_del, avg_ins);
			//	std::cout<<"Debug:\t"<<avg_del<<" "<<avg_ins<<endl;
			tot_avg_ins += avg_ins;
			tot_avg_del += avg_del;
			//
			avg_dist += dist;
			double avg_mis = 0;
			for (size_t i = 0; i < tmp.size(); i++) {
				while (tmp[i] + 1 > mis_per_window.size()) { //adjust length
					mis_per_window.push_back(0);
				}
				avg_mis += tmp[i];
				mis_per_window[tmp[i]]++;
			}
			//	std::cout <<avg_mis/tmp.size()<<"\t";
			//get score ratio
			double score = round(tmp_aln->get_scrore_ratio());
			//	std::cout<<score<<"\t"<<std::endl;;
			if (score > -1) {
				while (score + 1 > scores.size()) {
					scores.push_back(0);
				}
				scores[score]++;
			}
			num++;
		}

		mapped_file->parseReadFast(Parameter::Instance()->min_mq, tmp_aln);
	}
	if (num == 0) {
		std::cerr << "Too few reads detected in " << Parameter::Instance()->bam_files[0] << std::endl;
		exit(EXIT_FAILURE);
	}
	vector<int> nums;
	size_t pos = 0;
	Parameter::Instance()->max_dist_alns = floor(avg_dist / num) / 2;
	Parameter::Instance()->window_thresh = 50;			//25;
	if (!mis_per_window.empty()) {
		for (size_t i = 0; i < mis_per_window.size(); i++) {

			for (size_t j = 0; j < mis_per_window[i]; j++) {
				nums.push_back(i);
			}
		}
		pos = nums.size() * 0.95; //the highest 5% cutoff
		if (pos > 0 && pos <= nums.size()) {
			Parameter::Instance()->window_thresh = std::max(Parameter::Instance()->window_thresh, nums[pos]); //just in case we have too clean data! :)
		}
		nums.clear();
	}

	for (size_t i = 0; i < scores.size(); i++) {
		for (size_t j = 0; j < scores[i]; j++) {
			nums.push_back(i);
		}
	}
	pos = nums.size() * 0.05; //the lowest 5% cuttoff
	Parameter::Instance()->score_treshold = 2; //nums[pos]; //prev=2

	//cout<<"test: "<<tot_avg_ins<<" "<<num<<endl;
	//cout<<"test2: "<<tot_avg_del<<" "<<num<<endl;
	Parameter::Instance()->avg_del = tot_avg_del / num;
	Parameter::Instance()->avg_ins = tot_avg_ins / num;

	std::cout << "\tMax dist between aln events: " << Parameter::Instance()->max_dist_alns << std::endl;
	std::cout << "\tMax diff in window: " << Parameter::Instance()->window_thresh << std::endl;
	std::cout << "\tMin score ratio: " << Parameter::Instance()->score_treshold << std::endl;
	std::cout << "\tAvg DEL ratio: " << Parameter::Instance()->avg_del << std::endl;
	std::cout << "\tAvg INS ratio: " << Parameter::Instance()->avg_ins << std::endl;

}

bool overlaps(aln_str prev, aln_str curr) {

	double ratio = 0;
	double overlap = 0;
	if (prev.pos + Parameter::Instance()->min_length < curr.pos + curr.length && prev.pos + prev.length - Parameter::Instance()->min_length > curr.pos) {
		overlap = min((curr.pos + curr.length), (prev.pos + prev.length)) - max(prev.pos, curr.pos);
		ratio = overlap / (double) min(curr.length, prev.length);
	}
//	std::cout<<overlap<<" "<<ratio<<std::endl;
	return (ratio > 0.4 && overlap > 200);
}
