// -lsfml-window -lsfml-graphics -lsfml-system -lsfml-audio

#include <iostream>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <cstdlib>
#include <cmath>
#include <fstream>
using namespace std;
using namespace sf;

struct Punto {
	int x[5],y[5];
	float a;
	float ai,ad;
	Punto() {a=.5; ai=-1; ad=2;}
};

int main(int argc, char *argv[]) {
	
	int cp=0, sel=0, seli=1;
	Punto pt[100];
	
	Image imapa;
	imapa.LoadFromFile("mapa.png");
	Sprite smapa;
	smapa.SetImage(imapa);

	ifstream fin("route.dat");
	if (fin.is_open()) {
		float x1,y1,x2,y2,a,ai,ad;
		while(fin>>x1>>y1>>x2>>y2>>a>>ai>>ad) {
			pt[cp].x[1]=x1;
			pt[cp].y[1]=y1;
			pt[cp].x[2]=x2;
			pt[cp].y[2]=y2;
			pt[cp].ai=ai;
			pt[cp].ad=ad;
			pt[cp].a=a;
			cp++;
		}
		fin.close();
	}
	if (cp<4) { 
		cp=4;
		pt[0].x[1]=2*imapa.GetWidth()/3;
		pt[0].y[1]=imapa.GetHeight()/2;
		pt[1].x[1]=imapa.GetWidth()/2;
		pt[1].y[1]=imapa.GetHeight()/3;
		pt[2].x[1]=imapa.GetWidth()/3;
		pt[2].y[1]=imapa.GetHeight()/2;
		pt[3].x[1]=imapa.GetWidth()/2;
		pt[3].y[1]=2*imapa.GetHeight()/3;
		pt[0].x[2]=2*imapa.GetWidth()/3-imapa.GetWidth()/10;
		pt[0].y[2]=imapa.GetHeight()/2;
		pt[1].x[2]=imapa.GetWidth()/2;
		pt[1].y[2]=imapa.GetHeight()/3+imapa.GetHeight()/10;
		pt[2].x[2]=imapa.GetWidth()/3+imapa.GetWidth()/10;
		pt[2].y[2]=imapa.GetHeight()/2;
		pt[3].x[2]=imapa.GetWidth()/2;
		pt[3].y[2]=2*imapa.GetHeight()/3-imapa.GetHeight()/10;
	}
	
	int cx=imapa.GetWidth()/2, cy=imapa.GetHeight()/2;
	
	FloatRect r(0,0,800,600);
	View v;
	RenderWindow w(VideoMode(800,600),"Map editor");
	w.SetView(v);
	w.SetFramerateLimit(60);
	float z=1, dz=1.1;
	bool panning=false; int px, py; 
	bool moving=false; int amx, amy; bool full_moving=false;
	int mx = w.GetInput().GetMouseX();
	int my = w.GetInput().GetMouseY();
	Shape s;
	s.EnableFill(true);
	s.EnableOutline(true);
	Color colin(255,255,0,100);
	Color colout(0,0,255,255);
	s.SetOutlineWidth(1);
	for (int i=0;i<4;i++) s.AddPoint(0,0,colin,colout);
	
	Shape sa;
	sa.EnableFill(false);
	sa.EnableOutline(true);
	Color colal(125,125,125,200);
	sa.SetOutlineWidth(2);
	for (int i=0;i<4;i++) sa.AddPoint(0,0,colal,colal);
	
	Shape ss;
	ss.EnableOutline(true);
	ss.EnableFill(false);
	ss.SetOutlineWidth(3);
	Color colsel(255,0,255,255);
	ss.AddPoint(0,-5,colsel,colsel);
	ss.AddPoint(-5,0,colsel,colsel);
	ss.AddPoint(0,+5,colsel,colsel);
	ss.AddPoint(+5,0,colsel,colsel);
	
	Shape sm;
	sm.EnableOutline(true);
	sm.EnableFill(false);
	sm.SetOutlineWidth(3);
	Color colmid(255,0,0,255);
	sm.AddPoint(0,-2,colmid,colmid);
	sm.AddPoint(-2,0,colmid,colmid);
	sm.AddPoint(0,+2,colmid,colmid);
	sm.AddPoint(+2,0,colmid,colmid);
	
	while(w.IsOpened()) {
		sf::Event e;
		while(w.GetEvent(e)) {
			if(e.Type == e.Closed) {
				w.Close();	
			} else if(e.Type == e.KeyPressed) {
				if (e.Key.Code=='s') {
					ofstream fout("route.dat",ios::trunc);
					for (int i=0;i<cp;i++) {
						fout<<pt[(sel+i)%cp].x[1]<<" ";
						fout<<pt[(sel+i)%cp].y[1]<<" ";
						fout<<pt[(sel+i)%cp].x[2]<<" ";
						fout<<pt[(sel+i)%cp].y[2]<<" ";
						fout<<pt[(sel+i)%cp].a<<" ";
						fout<<pt[(sel+i)%cp].ai<<" ";
						fout<<pt[(sel+i)%cp].ad<<endl;
					}
					fout.close();
				} else if (e.Key.Code=='a') {
					for (int i=cp;i>sel;i--) {
						pt[i]=pt[i-1];
					}
					cp++;
					for (int i=1;i<3;i++) {
						pt[sel+1].x[i]=(pt[sel].x[i]+pt[(sel+2)%cp].x[i])/2;
						pt[sel+1].y[i]=(pt[sel].y[i]+pt[(sel+2)%cp].y[i])/2;
					}
					sel++;
				} else if (e.Key.Code=='r') {
					for (int i=0;i<cp/2;i++) {
						Punto aux=pt[i]; 
						pt[i]=pt[cp-i-1];
						pt[cp-i-1]=aux;
					}
					sel=cp-sel-1;
				} else if (e.Key.Code=='d') {
					if (cp>4) {
						cp--;
						for (int i=sel;i<cp;i++) {
							pt[i]=pt[i+1];
						}
						sel--; if (sel<0) sel=cp-1;
					}
				}
			} else if(e.Type == e.MouseMoved) {
				mx=e.MouseMove.X;
				my=e.MouseMove.Y;
				amx=cx-400/z+mx/z;
				amy=cy-300/z+my/z;
			} else if(e.Type == e.MouseButtonPressed) {
				if (e.MouseButton.Button==Mouse::Left) {
					sel=0; seli=1; int ad,md=abs(amx-pt[0].x[1])+abs(amy-pt[0].y[1]);
					for (int i=0;i<cp;i++) {
						for (int j=0;j<5;j++) {
							ad=abs(amx-pt[i].x[j])+abs(amy-pt[i].y[j]);
							if (ad<md) { sel=i; seli=j; md=ad; }
						}
					}
					moving=true; full_moving=false;
				} else if (e.MouseButton.Button==Mouse::Right) {
					sel=0; seli=1; int ad,md=abs(amx-pt[0].x[1])+abs(amy-pt[0].y[1]);
					for (int i=0;i<cp;i++) {
						for (int j=0;j<3;j++) {
							ad=abs(amx-pt[i].x[j])+abs(amy-pt[i].y[j]);
							if (ad<md) { sel=i; seli=j; md=ad; }
						}
					}
					full_moving=true; moving=false; px=amx; py=amy;
				} else if (e.MouseButton.Button==Mouse::Middle) {
					px=mx; py=my; panning=true;
				}
			} else if(e.Type == e.MouseButtonReleased) {
				if (e.MouseButton.Button==Mouse::Left) {
					moving=false;
				} else if (e.MouseButton.Button==Mouse::Right) {
					full_moving=false;
				} else if (e.MouseButton.Button==Mouse::Middle) {
					panning=false; 
				}
			} else if(e.Type == e.MouseWheelMoved) {
				if (e.MouseWheel.Delta<0)
					z*=dz;
				else 
					z/=dz;
			}
		}
		
		if (panning) {
			cx+=(px-mx)/z; px=mx; 
			cy+=(py-my)/z; py=my;
		}
		
		if (moving) {
			if (seli==0 || seli>2) {
				float dx=pt[sel].x[1]-pt[sel].x[2];
				float dy=pt[sel].y[1]-pt[sel].y[2];
				float dmx=amx-pt[sel].x[2];
				float dmy=amy-pt[sel].y[2];
				float m2=dx*dx+dy*dy;
				float pe=(dmx*dx+dmy*dy)/(m2);
				float fx=pt[sel].x[2]+pe*dx;
				float fy=pt[sel].y[2]+pe*dy;
				float m1=(fx-pt[sel].x[2])*(fx-pt[sel].x[2])+(fy-pt[sel].y[2])*(fy-pt[sel].y[2]);
				float a=pe<0?-sqrt(m1/m2):sqrt(m1/m2);
				if (seli==3) {
					pt[sel].ai=a;
				} else if (seli==4) {
					pt[sel].ad=a;
				} else {
					if (a<0.1) a=0.1;
					else if (a>0.9) a=0.9;
					pt[sel].a=a;
				}
			} else {
				pt[sel].x[seli]=amx;
				pt[sel].y[seli]=amy;
			}
		}
		
		if (full_moving) {
			for (int i=1;i<3;i++) {
				pt[sel].x[i]-=px-amx;
				pt[sel].y[i]-=py-amy;
			}
			px=amx; py=amy;
		}
		
		v.SetFromRect(r);
		v.SetCenter(cx,cy);
		v.Zoom(z);
		w.Clear(Color(0,0,0,255));
		w.Draw(smapa);
		
		for (int i=0;i<cp;i++) {
			pt[i].x[0]=pt[i].a*pt[i].x[1]+(1-pt[i].a)*pt[i].x[2];
			pt[i].y[0]=pt[i].a*pt[i].y[1]+(1-pt[i].a)*pt[i].y[2];
			pt[i].x[3]=pt[i].ai*pt[i].x[1]+(1-pt[i].ai)*pt[i].x[2];
			pt[i].y[3]=pt[i].ai*pt[i].y[1]+(1-pt[i].ai)*pt[i].y[2];
			pt[i].x[4]=pt[i].ad*pt[i].x[1]+(1-pt[i].ad)*pt[i].x[2];
			pt[i].y[4]=pt[i].ad*pt[i].y[1]+(1-pt[i].ad)*pt[i].y[2];
		}
		
		for (int i=0;i<cp;i++) {
			sa.SetOutlineWidth(i==sel?3:1);
			int j=(i+1)%cp;
			sa.SetPointPosition(0,pt[i].x[3],pt[i].y[3]);
			sa.SetPointPosition(1,pt[i].x[4],pt[i].y[4]);
			sa.SetPointPosition(3,pt[j].x[3],pt[j].y[3]);
			sa.SetPointPosition(2,pt[j].x[4],pt[j].y[4]);
			w.Draw(sa);
		}
		
		for (int i=0;i<cp;i++) {
			s.SetOutlineWidth(i==sel?3:1);
			int j=(i+1)%cp;
			s.SetPointPosition(0,pt[i].x[1],pt[i].y[1]);
			s.SetPointPosition(1,pt[i].x[2],pt[i].y[2]);
			s.SetPointPosition(3,pt[j].x[1],pt[j].y[1]);
			s.SetPointPosition(2,pt[j].x[2],pt[j].y[2]);
			w.Draw(s);
		}
		
		for (int i=0;i<cp;i++) {
			sm.SetX(pt[i].x[0]);
			sm.SetY(pt[i].y[0]);
			w.Draw(sm);
		}
		
		ss.SetX(pt[sel].x[seli]);
		ss.SetY(pt[sel].y[seli]);
		w.Draw(ss);
		
		w.Display();
	}
	return 0;
}

