#define _USE_MATH_DEFINES
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */
#include "Point.h"
#include "polarPoint.h"
#include "PeNB.h"
#include "MeNB.h"
#include "UE.h"
#include "maFileGen.h"
using namespace std;

maFileGen::maFileGen(string fileName, int freq, int BSpow, int RRHpow, int NuUE, int NuBS, int NuRRH, vector<UE> UEs, vector<PeNB> RRHs, vector<MeNB> BSs): 
fileName(fileName), freq(freq), BSpow(BSpow), RRHpow(RRHpow), NuUE(NuUE), NuBS(NuBS), NuRRH(NuRRH), UEs(UEs), RRHs(RRHs), BSs(BSs) {}


void maFileGen::printBSs(void)
{
	std::ofstream logfile(fileName.c_str(), std::ios_base::app);
	for(int i = 0; i < NuBS; i++) {
		logfile << "[MeNB" << BSs[i].getID() << "]" << endl;
		logfile <<"components : MeNBQ" << BSs[i].getID() << "@globalQueue MeNBP" << BSs[i].getID() << "@MeNB" << endl << endl;
        logfile <<"out : Out" << endl << "out : X2out" 
        << endl << "in : In" << endl << "in : X2in" << endl << endl;
        logfile << "Link : In In@MeNBQ" << BSs[i].getID() << endl;
        logfile << "Link : X2in X2in@MeNBQ" << BSs[i].getID() << endl;
        logfile << "Link : Req@MeNBP" << BSs[i].getID() << " Req@MeNBQ" << BSs[i].getID() << endl;
        logfile << "Link : Out@MeNBQ" << BSs[i].getID() << " In@MeNBP" << BSs[i].getID() << endl;
        logfile << "Link : X2out@MeNBP" << BSs[i].getID() << " X2out" << endl;             
        logfile << "Link : Out@MeNBP" << BSs[i].getID() << " Out" << endl;  
        logfile << "[MeNBP" << BSs[i].getID() << "]" << endl;
        logfile << "ID : " << BSs[i].getID() << endl;
        logfile << "currentX : " <<  nearbyint(BSs[i].getPosition().getX()) << endl;
        logfile<< "currentY : " << nearbyint(BSs[i].getPosition().getY()) << endl;
        logfile<< "frequency : " << BSs[i].getFreq() << endl;
        logfile<< "MeNBPower : " << BSs[i].getPower() << endl << endl;
        logfile<< "[MeNBQ" << BSs[i].getID() << "]" << endl;
        logfile<< "ID : " << BSs[i].getID() << endl << endl;

	}

	logfile.close();
	return;
}

void maFileGen::printRRHs(void)
{
	std::ofstream logfile(fileName.c_str(), std::ios_base::app);
     for(int i = 0; i < NuRRH; i++){
             logfile << "[PeNB" << RRHs[i].getID() << "]" << endl;
             logfile << "components : PeNBQ" << RRHs[i].getID() << "@globalQueue PeNBP" << RRHs[i].getID() << "@PeNB" << endl << endl;
             logfile <<"out : Out" << endl << "out : X2out" 
        	 << endl << "in : In" << endl << "in : X2in" << endl << endl;
             logfile << "Link : In In@PeNBQ" << RRHs[i].getID() << endl;
             logfile << "Link : X2in X2in@PeNBQ" << RRHs[i].getID() << endl;
             logfile << "Link : Req@PeNBP" << RRHs[i].getID() << " Req@PeNBQ" << RRHs[i].getID() << endl;
             logfile << "Link : Out@PeNBQ" << RRHs[i].getID() << " In@PeNBP" << RRHs[i].getID() << endl;
             logfile << "Link : X2out@PeNBP" << RRHs[i].getID() << " X2out" << endl;
             logfile << "Link : Out@PeNBP" << RRHs[i].getID() << " Out" << endl;             
             logfile << "[PeNBP" << RRHs[i].getID() << "]" << endl;
             logfile << "ID : " << RRHs[i].getID() << endl;
             logfile << "currentX : " << nearbyint(RRHs[i].getPosition().getX()) << endl;
             logfile << "currentY : " << nearbyint(RRHs[i].getPosition().getY()) << endl;
             logfile << "frequency : " << RRHs[i].getFreq() << endl;
             logfile << "PeNBPower : " << RRHs[i].getPower() << endl << endl;
             logfile << "[PeNBQ" << RRHs[i].getID() << "]" << endl;
             logfile << "ID : " << RRHs[i].getID() << endl << endl;
             }
      logfile.close();
     
     return;
}

void maFileGen::printUEs(void)
{
	std::ofstream logfile(fileName.c_str(), std::ios_base::app);

	for (int i = 0; i < UEs.size(); i++) {
		logfile << "[UE" << UEs[i].getID() << "]" << endl;
        logfile << "components : UEQ" << UEs[i].getID() << "@globalQueue UEP" << UEs[i].getID() << "@UE" << endl << endl;
        logfile << "out : Out" << endl << "in : In" << endl << endl;
        logfile << "Link : In In@UEQ" << UEs[i].getID() << endl;
        logfile << "Link : Out@UEQ" << UEs[i].getID() << " In@UEP" << UEs[i].getID() << endl;
        logfile << "Link : Req@UEP" << UEs[i].getID() << " Req@UEQ" << UEs[i].getID() << endl;
        logfile << "Link : Out@UEP" << UEs[i].getID() << " Out" << endl << endl;             
        logfile << "[UEP" << UEs[i].getID() << "]" << endl;
        logfile << "ID : " << UEs[i].getID() << endl;
        logfile << "currentX : " << nearbyint(UEs[i].getPosition().getX()) << endl;
        logfile << "currentY : " << nearbyint(UEs[i].getPosition().getY()) << endl;
        logfile << "frequency : " << freq << endl;
        logfile << "sTime : " << UEs[i].getSTime() << endl;
        logfile << "etime : " << UEs[i].getETime() << endl;
        logfile << "endX : "  << nearbyint(UEs[i].getEndPosition().getX()) << endl;
        logfile << "endY : " << nearbyint(UEs[i].getEndPosition().getY()) << endl;
        logfile << "speed : " << UEs[i].getSpeed()<< endl << endl;
        logfile << "[UEQ" << UEs[i].getID() << "]" << endl;
        logfile << "ID : " << UEs[i].getID()<<endl<<endl;

	}
	logfile.close();
	return;
}

void maFileGen::generateBSLinks(void)
{
	std::ofstream logfile(fileName.c_str(), std::ios_base::app);
	if(NuBS > 1) {
	    for(int i = 0; i < NuBS; i++) {
		      
			for(int index = 0; index < NuRRH; index++) {
				if ((BSs[i].getID()) == (RRHs[i].getID()/10)) { logfile<<"Link : X2out@MeNB" << BSs[i].getID() << " X2in@PeNB" << RRHs[index].getID() << endl; } //double check
			}
		              
			for (int ii = 1; ii < (NuBS-i);ii++){
		        	logfile<<"Link : X2out@MeNB" << BSs[i].getID() << " X2in@MeNB" << BSs[i+ii].getID() << endl;
			}
			for (int ii = 1; ii < i+1 ; ii++){
				logfile << "Link : X2out@MeNB" << BSs[i].getID() << " X2in@MeNB" << BSs[i-ii].getID() << endl;
			} 	                            
		                	 	    	  
		    for(int j = 0; j < UEs.size(); j++) {
		        logfile << "Link : Out@MeNB" << BSs[i].getID() << " In@UE" << UEs[j].getID() << endl;    
		    }
		    logfile << endl;
	    }
	}
	else {

		for(int index = 0; index < NuRRH; index++) {
				logfile<<"Link : X2out@MeNB" << BSs[0].getID() << " X2in@PeNB" << RRHs[index].getID() << endl; //double check
		}

		for(int j = 0; j < UEs.size(); j++) {
		        logfile << "Link : Out@MeNB" << BSs[0].getID() << " In@UE" << UEs[j].getID() << endl;    
		}

		logfile << endl;
	}	

    logfile.close();
       
    return;
}

void maFileGen::generateRRHLinks(void)
{
	std::ofstream logfile(fileName.c_str(), std::ios_base::app);
    for(int i = 0; i < NuRRH; i++){

		for(int index = 0; index < NuBS;index++) {
	    	if (BSs[index].getID() == (RRHs[i].getID()/10)) { logfile <<"Link : X2out@PeNB" << RRHs[i].getID() << " X2in@MeNB" << BSs[index].getID() << endl; }
		}

		for (int ii = 1; ii < (NuRRH-i);ii++){
		        	logfile << "Link : X2out@PeNB" << RRHs[i].getID() << " X2in@PeNB" << RRHs[i+ii].getID() << endl;
		}
		
		for (int ii = 1; ii < i+1 ; ii++){
			logfile << "Link : X2out@PeNB" << RRHs[i].getID() << " X2in@PeNB" << RRHs[i-ii].getID() << endl;
		}

	    for(int j = 0; j < NuUE; j++) {
	        logfile << "Link : Out@PeNB" << RRHs[i].getID() << " In@UE" << UEs[j].getID() << endl;
	    }

	    logfile << endl;
        }
        logfile.close();
       
     return;
}

void maFileGen::generateUELinks(void)
{
	std::ofstream logfile(fileName.c_str(), std::ios_base::app);
	for(int j = 0; j < NuUE; j++) {
		for(int i = 0; i < NuBS; i++) {
 	        logfile << "Link : Out@UE" << UEs[j].getID() << " In@MeNB" << BSs[i].getID() << endl;
                                         
        }

        for(int i = 0; i < NuRRH; i++) {
 	        logfile << "Link : Out@UE" << UEs[j].getID() << " In@PeNB" << RRHs[i].getID() << endl;
                                         
        }
        logfile << endl;
	}
	logfile.close();
	return;	

}


int maFileGen::getNuBS(void)
{
	return NuBS;
}

int maFileGen::getNuRRH(void)
{
	return NuRRH;
}

int maFileGen::getNuUE(void)
{
	return NuUE;
}


string maFileGen::getFileName(void)
{
	return fileName;
}

double distance(Point point1, Point point2)
{
	return sqrt(((point1.getX() - point2.getX())*(point1.getX() - point2.getX())) + ((point1.getY() - point2.getY())*(point1.getY() - point2.getY())));
}

double findAngle(Point point1, Point point2)
{
	double dx = point1.getX() - point2.getX();
	double dy = point1.getY() - point2.getY(); 
	double d = distance(point1, point2);
	double theta = (180/M_PI)*acos(((dx*dx) + (d*d) - (dy*dy))/ (2*dx*d));
	if (theta >= 90 && theta < 180 ) {return (180 - theta);}
	else if(theta >= 180 && theta < 270) {return (270 - theta);} 
	else if(theta >= 270 && theta < 360) {return (360 - theta);}
	else if((int)dx == 0) {return 90;} 	
	return theta;
		 
}

//point1 is the center of the MeNB, point2 is the common point or another point. Make sure to put the center on point1
double findAngleOfPoints(Point point1, Point point2) 
{
	double angle;
	if ( (nearbyint(point1.getY()) == nearbyint(point2.getY())) ) {
		if ( (nearbyint(point1.getX()) < nearbyint(point2.getX())) ) {
			angle = 0;
		}

		if ( (nearbyint(point1.getX()) > nearbyint(point2.getX())) ) {
			angle = 180;
		}
		
	}
	else if( (nearbyint(point1.getX()) == nearbyint(point2.getX())) ) {
		if ( (nearbyint(point1.getY()) < nearbyint(point2.getY())) ) {
			angle = 90;
		}

		if ( (nearbyint(point1.getY()) > nearbyint(point2.getY())) ) {
			angle = 270;
		}
	}
	else if( (nearbyint(point1.getY()) < nearbyint(point2.getY())) && (nearbyint(point1.getX()) < nearbyint(point2.getX())) ) {
		angle = (180/M_PI)*acos((point2.getX() - point1.getX())/distance(point1, point2));
	}
	else if( (nearbyint(point1.getY()) < nearbyint(point2.getY())) && (nearbyint(point1.getX()) > nearbyint(point2.getX())) ) {
		angle = 180 - ((180/M_PI)*acos((point1.getX() - point2.getX())/distance(point1, point2)));
	}
	else if( (nearbyint(point1.getY()) > nearbyint(point2.getY())) && (nearbyint(point1.getX()) > nearbyint(point2.getX())) ) {
		angle = 180 + ((180/M_PI)*acos((point1.getX() - point2.getX())/distance(point1, point2)));
	}
	else if( (nearbyint(point1.getY()) > nearbyint(point2.getY())) && (nearbyint(point1.getX()) < nearbyint(point2.getX())) ) {
		angle = 360 - ((180/M_PI)*acos((point2.getX() - point1.getX())/distance(point1, point2)));
	}
	return angle;


}

polarPoint toPolarPoint(Point point)
{
	double radius = sqrt((point.getX()*point.getX())+(point.getY()*point.getY()));
	double angle = atan(point.getY()/point.getX());
	polarPoint point1(radius,angle);
	return point1;
}

Point toPoint(polarPoint point)
{
	double x = point.getRadius()*cos(point.getAngle());
	double y = point.getRadius()*sin(point.getAngle());
	Point point1(x,y);
	return point1;
}

double findAngleOfChord(MeNB bs, Point commonPoint)
{
	double a = distance(commonPoint, bs.getPosition());	
	double theta = (180/M_PI)*2*acos(a/bs.getRadius());
 	if (theta >= 90 && theta < 180 ) {return (180 - theta);}
	else if(theta >= 180 && theta < 270) {return (180 + theta);} 
	else if(theta >= 270 && theta < 360) {return (360 - theta);}

	return theta;
}


vector<Point> centersBSs(double BSRadius, int NuBS)
{
	vector<Point> centers({Point(3*BSRadius, 3*BSRadius)});
	if (NuBS != 1){
		for (int a = 0; a < (NuBS - 1); a++) {
			centers.push_back(Point((3*BSRadius + (2*BSRadius-10)*cos((M_PI/6) + a*(M_PI/3))), (3*BSRadius + (2*BSRadius-10)*sin((M_PI/6) + a*(M_PI/3)))));
		}
	}
	return centers;
}

vector<Point> centersRRHs(MeNB bss, double RRHRadius, int NuRRH, double BSRadius, int NuBS)
{
	MeNB bs(bss);
	Point centersBS = bs.getPosition();	
	vector<Point> centersRRH;
	
	vector <int> randomD({75,215});//for r = 75 and 3 PeNBs
	//vector <int> randomD({100,290});//for r = 100 and 3 PeNBs
	vector <int> randomA({0,20,40,60,80,100,120,140,160,180,200,220,240,260,280,300,320,340});
	//vector <int> randomA({30,45,60});//for r = 100 and 3 PeNBs
	//vector <int> randomA({30,40,50});//for r = 75 and 3 PeNBs
	vector <int> randomA2({0,40,80,120,160,200,240,280,320});
	/*vector <Point> randomV({Point(randomD[0],randomA[0]),
		Point(randomD[1],randomA[1]),Point(randomD[0],randomA[2])});*/
	vector <Point> randomV;
	
	for (int i = 0; i < randomA.size(); i++) {
			randomV.push_back(Point(randomD[0],randomA[i]));
	}

	for (int i = 0; i < randomA2.size(); i++) {
			randomV.push_back(Point(randomD[1],randomA2[i]+10));
	}

	

	int indexV;
	int sizeOfV; 
	
	

	for (int i = 0; i < NuRRH; i++) {
		sizeOfV = randomV.size();
		indexV = rand() % sizeOfV; 		
				
		Point point1((centersBS.getX() + ((BSRadius-(randomV[indexV].getX()))*cos((M_PI/180)*randomV[indexV].getY()))), 
			(centersBS.getY() + ((BSRadius-(randomV[indexV].getX()))*sin((M_PI/180)*randomV[indexV].getY()))));
		centersRRH.push_back(point1);
		randomV.erase(randomV.begin() + indexV);

	}
							
		
	for (int i = 0; i < NuRRH; i++) {
		cout << "centersRRH: " << "(" << centersRRH[i].getX() << "," << centersRRH[i].getY()<< ")" << endl;
	}

	return centersRRH;

}

vector<int> setRRHsID(vector<Point> BScenter, vector<Point> RRHcenter, double BSRadius) 
{

	vector<int> ID;
	int k = 0;
	for (int i = 0; i < BScenter.size(); i++) {
		k = 1;
		for (int j = 0; j < RRHcenter.size(); j++) {
			double d = distance(BScenter[i],RRHcenter[j]);
			//cout << "d: " << d << endl;
			if(d < BSRadius) { 
				ID.push_back((100*(i+1)) + k);
				//cout << "k: " << k << endl;
				k++;
			}
			
		}
	}
	return ID;
}


vector<Point> setUEsPosition(vector<int> idUE, vector<MeNB> BSs) 
{
	
	int counter = 0;
	vector<int> id(idUE);
	vector<MeNB> bss(BSs);
	int NuBS = bss.size();
	cout <<"Test NuBS "<<NuBS<<endl;
	int numberofUEsPresent = 0;
	vector<Point> UEPosition;
	for(int i = 0; i < id.size(); i++) {

		numberofUEsPresent++;
		if(NuBS > 1) { counter = rand() % NuBS; } //choose random MeNB
		else counter = 0; //only one MeNB, no intersections or MeNB-MeNB common areas.
		cout << "RANDOM AREA IS " << counter << endl;
		int bsRadius = bss[counter].getRadius();
		int RandomD = rand() % bsRadius;
		cout << "Test RandomD " << RandomD <<endl;
		//int RandomD = 0;
		int RandomAngle = rand() % 360;
		cout << "Test RandomAngle " << RandomAngle << endl;
		UEPosition.push_back( Point( (bss[counter].getPosition().getX() +  RandomD*cos((M_PI/180)*RandomAngle)), 
			(bss[counter].getPosition().getY() +  RandomD*sin((M_PI/180)*RandomAngle)) ));
		cout << "UEPosition[" << i << "]: " << "(" << UEPosition[i].getX() << "," << UEPosition[i].getY()<<")" << endl;

		}
		return UEPosition;
	}

vector<Point> setUEsEndPosition(vector<int> idUE, vector<MeNB> BSs) 
{
	
	int counter_ = 0;
	vector<int> id(idUE);
	vector<MeNB> bss(BSs);
	int NuBS = bss.size();
	cout <<"Test NuBS "<<NuBS<<endl;
	int numberofUEsPresent = 0;
	vector<Point> UEendPosition;
	for(int i = 0; i < id.size(); i++) {

		numberofUEsPresent++;
		if(NuBS > 1) { counter_ = rand() % NuBS; } //choose random MeNB
		else counter_ = 0; //only one MeNB, no intersections or MeNB-MeNB common areas.
		cout << "RANDOM AREA IS " << counter_ << endl;
		int bsRadius = bss[counter_].getRadius();
		int RandomD_ = rand() % bsRadius;
		cout << "Test RandomD_ " << RandomD_ << endl;
		//int RandomD = 0;
		int RandomAngle_ = rand() % 360;
		cout << "Test RandomAngle_ " << RandomAngle_ << endl;
		UEendPosition.push_back( Point( (bss[counter_].getPosition().getX() +  RandomD_*cos((M_PI/180)*RandomAngle_)), 
			(bss[counter_].getPosition().getY() +  RandomD_*sin((M_PI/180)*RandomAngle_)) ));
		cout << "UEendPosition[" << i << "]: " << "(" << UEendPosition[i].getX() << "," << UEendPosition[i].getY()<<")" << endl;

		}
		return UEendPosition;
	}


	

int main()
{

	srand (time(NULL));
	int freq = 900, BSpow = 43, RRHpow=30, NuUE=2, NuUEStart = (NuUE/4) ;
	int NuBS = 1, NuRRH = 3; 
	double BSRadius = 500, RRHRadius = 75;
	string fileName = "2UEs1cell2.ma";
	int randomID; 
	int countUEs = 0;
	vector<int> randomStart;
	
    	int p=0, indexUE;

	vector<int> idRRH;
	vector<int> idBS; //it defines at which MeNB is the PeNB.
	vector<int> idBSofRRH;
	vector<int> idUEInitial;
	vector<int> idUE;

	vector<Point> BScenter(centersBSs(BSRadius,NuBS));
	vector<Point> RRHcenter;
	vector<MeNB> bss;	
	vector<PeNB> rrhs;
	vector<UE> ues; 
	

  	

	/*set UEs id*/
	for (int a = 0; a < 4000; a++) {
		idUEInitial.push_back(0);
	}

	while(true) {
        randomID = rand() % 2998 + 1001;//in the range 101 to 399
        if(idUEInitial[randomID] == 0) {
            idUEInitial[randomID] = randomID;
                countUEs++;
            cout <<  "idUEInitial" << "[" << randomID << "]" << ": " << idUEInitial[randomID] << endl;   
        }
        if(countUEs == NuUE)
        	break; 
    }

    for (int i = 0; i < 4000; i++) {
    	if (idUEInitial[i] != 0) { idUE.push_back(idUEInitial[i]); }

    }

    

    for (int i = 0; i < NuUE; i++) { randomStart.push_back(0); }

    while(p<NuUEStart) {
        indexUE = rand() % (NuUE-1)+0;
        if(randomStart[indexUE] != 0)
            continue;        
         randomStart[indexUE] = 12000;
         p++;  
    }

 
		

	/*set vector<MeNB> BSs*/
	for (int a = 0; a < NuBS; a++) {
		bss.push_back(MeNB(Point(BScenter[a]),BSRadius,a+1,BSpow,freq));
	}
	cout << bss.size() <<endl;
	for (int index = 0; index < bss.size(); index++) {
		cout << "bss[" << index << "]: " << bss[index].getID() << "(" << bss[index].getPosition().getX() <<"," << bss[index].getPosition().getY()<<")" << endl;
		
	}

	/*set vector<PeNB> RRHs*/
	RRHcenter = centersRRHs(bss[0], RRHRadius, NuRRH, BSRadius, NuBS); //assignment of the RRHs' center
	cout << RRHcenter.size() << endl;

	
	//cout << bss.size() <<endl;

	idRRH = setRRHsID(BScenter, RRHcenter, BSRadius);

	for (int a = 0; a < idRRH.size(); a++) {
		cout << "idRRH: " << idRRH[a] << endl;
	}

	for (int a = 0; a < NuRRH; a++) {
		rrhs.push_back(PeNB(Point(RRHcenter[a]),RRHRadius,idRRH[a],RRHpow,freq));
	}

			
	vector<Point> UEPosition(setUEsPosition(idUE, bss));

	for (int index = 0; index < UEPosition.size(); index++) {
		cout << "UEPosition[" << index << "]: " << "(" << UEPosition[index].getX() << "," << UEPosition[index].getY()<<")" << endl;
		
	}

	vector<Point> UEendPosition(setUEsEndPosition(idUE, bss));

	for (int index = 0; index < UEendPosition.size(); index++) {
		cout << "UEendPosition[" << index << "]: " << "(" << UEendPosition[index].getX() << "," << UEendPosition[index].getY()<<")" << endl;
		
	}

	for (int i = 0; i < idUE.size(); i++) {
		ues.push_back(UE(UEPosition[i],UEendPosition[i], idUE[i], randomStart[i], 0, 30));		
	}
	cout << "ues.size(): " << ues.size() << endl;

	for (int i = 0; i < ues.size(); i++) {		
		cout << "ues[" << i << "]" << "("<< ues[i].getPosition().getX() << "," << ues[i].getPosition().getY() << ")" << endl;
		cout << "ues[" << i << "]" << "ID: " << ues[i].getID() << endl;
	}




	std::ofstream logfile(fileName.c_str(), std::ios_base::out);
	logfile.clear();
	logfile << "[top]" << endl;
    logfile << "components : ";
    for(int i = 0; i < NuBS; i++) {logfile << "MeNB" << bss[i].getID() << " ";}
    for(int i = 0; i < NuRRH; i++) {logfile << "PeNB" << rrhs[i].getID() << " ";}
    for(int i = 0; i < NuUE; i++) {
    	logfile << "UE" << idUE[i] << " ";
    }

    logfile << endl << endl;
    logfile.close();
	maFileGen file(fileName, freq, BSpow, RRHpow, NuUE, NuBS, NuRRH, ues, rrhs, bss);
	file.generateBSLinks();
	file.generateRRHLinks();
	file.generateUELinks();
	file.printBSs();
	file.printRRHs();
	file.printUEs();

	return 0;
}









