#include <CtrlLib/CtrlLib.h>

using namespace Upp;

class Sketcher: public RectTracker{
public:
	Color bkcolor;
	Vector<Point> route;
	
	int mode;
	enum{
		S_LINE=1,
		S_ROUTE,
		S_CIRCLE,
		S_ELLIPSE,
		S_RECTANGLE
	};
	
	Vector<Point> Sketch(int mode_, const Point& p){
		mode=mode_;
		route.Clear();
		route.Add(p);
		Track(Rect(p,p),0,0);
		if(route.GetCount()<2) route.Add(clone(route[0])); // Guarantee at least two points returned
		return clone(route);
	}
	
	Sketcher &Solid(){ RectTracker::Solid(); return *this; }
	Sketcher &Pattern(int p){ RectTracker::Pattern(p); return *this; }
	Sketcher &Width(int n){ RectTracker::Width(n); return *this; }
	Sketcher &SetColor(Color c){ RectTracker::SetColor(c); return *this; }
	Sketcher &SetBkColor(Color c){ bkcolor=c; return *this; }

	Vector<Point> SketchLine(const Point& p){ return Sketch(S_LINE,p); }
	Vector<Point> SketchRoute(const Point& p){ return Sketch(S_ROUTE,p); }
	Vector<Point> SketchCircle(const Point& p){ return Sketch(S_CIRCLE,p); }
	Vector<Point> SketchEllipse(const Point& p){ return Sketch(S_ELLIPSE,p); }
	Vector<Point> SketchRectangle(const Point& p){ return Sketch(S_RECTANGLE,p); }

	void DoMouseMove(Point p, dword){
		if(mode==S_ROUTE || route.GetCount()<2) route.Add(p);
		else route[1]=p;
		Refresh();
		sync(Rect(route[0],route[route.GetCount()-1]));
	}

	void Paint(Draw& w){
		w.DrawImage(0, 0, master_image);
		w.Clip(clip & GetMaster().GetSize());

		int style=width;

		Rect er;
		switch(mode){
			case S_CIRCLE:{
				Point dp=Point(route[route.GetCount()-1]-route[0]);
				int l=(int)hypot(dp.x,dp.y);
				er=Rect(route[0]-Point(l,l),route[0]+Point(l,l));
				break;
			}
			case S_ELLIPSE:{
				Point dp=Point(route[route.GetCount()-1]-route[0]);
				dp.x=abs(dp.x);
				dp.y=abs(dp.y);
				er=Rect(route[0]-dp,route[0]+dp);
				break;
			}
			case S_RECTANGLE:{
				er=Rect(route[0],route.GetCount()<2?route[0]:route[1]);
				break;
			}
		}

		if(pattern!=DRAWDRAGRECT_SOLID){
			Color color2 = bkcolor.IsNullInstance()? (IsDark(color) ? White() : Black()) : bkcolor;
			switch(mode){
				default:
					w.DrawPolyline(route,width,color2);
					break;
				case S_CIRCLE:
				case S_ELLIPSE:
					w.DrawEllipse(er,Color(),width,color2);
					break;
				case S_RECTANGLE:
					w.DrawLine(er.left,er.top,er.right,er.top,width,color2);
					w.DrawLine(er.right,er.top,er.right,er.bottom,width,color2);
					w.DrawLine(er.right,er.bottom,er.left,er.bottom,width,color2);
					w.DrawLine(er.left,er.bottom,er.left,er.top,width,color2);
					break;
			}

			switch(pattern){
				case DRAWDRAGRECT_NORMAL:
					style=PEN_DOT;
					break;
				case DRAWDRAGRECT_DASHED:
					style=PEN_DASHDOT;
					break;
				default:
					style=pattern;
					break;
			}
		}
		switch(mode){
			default:
				w.DrawPolyline(route,style,color);
				break;
			case S_CIRCLE:
			case S_ELLIPSE:
				w.DrawEllipse(er,Color(),style,color);
				break;
			case S_RECTANGLE:
				w.DrawLine(er.left,er.top,er.right,er.top,style,color);
				w.DrawLine(er.right,er.top,er.right,er.bottom,style,color);
				w.DrawLine(er.right,er.bottom,er.left,er.bottom,style,color);
				w.DrawLine(er.left,er.bottom,er.left,er.top,style,color);
				break;
		}

		w.End();
	}

	Sketcher(Ctrl &master): RectTracker(master){
		MinSize(Size(-100000,-100000));
		bkcolor.SetNull(); // automatic unless specified
	}
};


struct MyApp : TopWindow {
	Point pos;
	
	Vector<Vector<Tuple<double, Pointf>>> drawing;

	PenInfo	pen;

	void DoTracking(Point p, dword keyflags) {
		if(keyflags&K_SHIFT){
			RectTracker tracker(*this);
			tracker.sync= [=](Rect r) {  };
			tracker.MinSize(Size(-100000,-100000));
			tracker.Track(Rect(p,p));
		}
		else if(keyflags&K_CTRL){
			RectTracker tracker(*this);
			tracker.TrackLine(p.x,p.y);
		}
		else if(keyflags&K_ALT){
			Sketcher sketcher(*this);
			sketcher.SketchRoute(p);
		}
	}

	virtual bool Pen(Point p, const PenInfo& pn, dword keyflags) override {
		pen=pn;
		// 1. This is what I need to do:
		if(keyflags & (K_SHIFT|K_CTRL|K_ALT)){
			if(pn.action==PEN_DOWN) DoTracking(p, keyflags);
			return true;
		}
		
		// 2. This is available, but with 2 cm start-up delay
//		if(keyflags & (K_SHIFT|K_CTRL|K_ALT)) return false;
		
		switch(pn.action){
			case PEN_DOWN:
				drawing.Add().Add(MakeTuple(pn.pressure, p));
				break;
			case 0: // Move
				if(!!pn.pressure) drawing.Top().Add(MakeTuple(pn.pressure, p));
			case PEN_UP: // Finish
				Refresh();
				break;
		}
		return true;
	}

	virtual void LeftDown(Point p, dword keyflags) override {
		DoTracking(p, keyflags);
	}

	virtual void Paint(Draw& w0) override {
		DrawPainter w(w0, GetSize());
		w.Co();
		w.Clear(SColorPaper());
		
		w.LineCap(LINECAP_ROUND);
		for(const auto& stroke : drawing)
			if(stroke.GetCount())
				for(int i = 0; i < stroke.GetCount() - 1; i++) {
					w.Move(stroke[i].b);
					w.Line(stroke[i + 1].b);
					w.Stroke(DPI(20) * stroke[i].a, Black());
				}
		
		int fcy = GetStdFontCy();
		int y = 10;
		auto Text = [&] (const String& text) {
			w.DrawText(10, y, text);
			y += fcy;
		};
		Text(AsString(pos));
		Text(String() << "Pressure: " << pen.pressure);
		Text(String() << "Rotation: " << pen.rotation);
		Text(String() << "Tilt: " << pen.tilt);
		Text(String() << "Barrel: " << pen.barrel);
		Text(String() << "Inverted: " << pen.inverted);
		Text(String() << "Eraser: " << pen.eraser);
	}
};

GUI_APP_MAIN
{
	MyApp().Run();
}

