/******************************************************************************
 * Antialias Application                                                      *
 * Upraveny scribble s rozostrovanim obrazu - 4. zadanie (6. tyzden)          *
 * Richard Richter, 97703, pocitacova grafika                                 *
 ******************************************************************************/
#include "fox/fx.h"

#define MyImage FXImage

// Farby ktorymi sa da kreslit
#define BLUE	FXRGB(0,0,255)
#define RED	FXRGB(255,0,0)
#define GREEN	FXRGB(0,255,0)
#define BLACK	FXRGB(0,0,0)

// Par mojich konstant
#define MYW 1024	// moja sirka obrazku (musi byt < 2048)
#define MYH 768		// moja vyska obrazku (musi byt < 2048)

// Main Window
class AntiAliasWindow : public FXMainWindow {

	// Macro for class hierarchy declarations
	FXDECLARE(AntiAliasWindow)

private:
	FXHorizontalFrame *contents;	// Content frame
	FXVerticalFrame *canvasFrame;	// Canvas frame
	FXVerticalFrame *buttonFrame;	// Button frame
	FXCanvas *canvas;		// Canvas for image
	// Na aliasing potrebujeme dva obrazky - stary a novy
	MyImage *image;			// Image to draw into
	MyImage *aaimage;		// Antialiased image
	// Flagy pre povodny scribble ostavaju
	int mdflag;			// Mouse button down?
	int dirty;			// Canvas has been painted?
	// Tieto pribudaju pre pamat obrazu a farbu
	int changed;			//Image changed (not rendered)
	int colid;			//Identifikator farbicky
	// Povodny ostal
	FXColor drawColor;		// Color for the line
	
protected:
	AntiAliasWindow(){}

public:

	// Message handlers
	long onPaint(FXObject*,FXSelector,void*);
	long onMouseDown(FXObject*,FXSelector,void*);
	long onMouseUp(FXObject*,FXSelector,void*);
	long onMouseMove(FXObject*,FXSelector,void*);
	long onCmdClear(FXObject*,FXSelector,void*);
	long onUpdClear(FXObject*,FXSelector,void*);
	// Nove handlery
	long onColChange(FXObject*,FXSelector,void*);
	long onMakeAA(FXObject*,FXSelector,void*);
	
public:
	
	// Messages for our class
	enum{
		ID_CANVAS=FXMainWindow::ID_LAST,
		ID_CLEAR,
		// CHCOL a MAKEAA som pridal pre spravy od tlacitka zmeny farby a AA...
		ID_CHCOL,
		ID_MAKEAA,
		ID_LAST
		};
		
public:

	// AntiAliasWindow's constructor
	AntiAliasWindow(FXApp* a);

	// Initialize
	virtual void create();
	};



// Message Map for the AntiAlias Window class
FXDEFMAP(AntiAliasWindow) AntiAliasWindowMap[]={
	//________Message_Type___________ID_____________________________Message_Handler_______
	FXMAPFUNC(SEL_PAINT,		 AntiAliasWindow::ID_CANVAS,	AntiAliasWindow::onPaint),
	FXMAPFUNC(SEL_LEFTBUTTONPRESS,	 AntiAliasWindow::ID_CANVAS,	AntiAliasWindow::onMouseDown),
	FXMAPFUNC(SEL_LEFTBUTTONRELEASE, AntiAliasWindow::ID_CANVAS,	AntiAliasWindow::onMouseUp),
	FXMAPFUNC(SEL_MOTION,		 AntiAliasWindow::ID_CANVAS,	AntiAliasWindow::onMouseMove),
	FXMAPFUNC(SEL_COMMAND,		 AntiAliasWindow::ID_CLEAR,	AntiAliasWindow::onCmdClear),
	FXMAPFUNC(SEL_UPDATE,		 AntiAliasWindow::ID_CLEAR,	AntiAliasWindow::onUpdClear),
	// Mnou pridane spravy...
	FXMAPFUNC(SEL_COMMAND,		 AntiAliasWindow::ID_MAKEAA,	AntiAliasWindow::onMakeAA),
	FXMAPFUNC(SEL_COMMAND,		 AntiAliasWindow::ID_CHCOL,	AntiAliasWindow::onColChange),
	};

// Macro for the AntiAliasApp class hierarchy implementation
FXIMPLEMENT(AntiAliasWindow,FXMainWindow,AntiAliasWindowMap,ARRAYNUMBER(AntiAliasWindowMap))

// Construct a AntiAliasWindow
AntiAliasWindow::AntiAliasWindow(FXApp *a):FXMainWindow(a,"AntiAlias Application",NULL,NULL,DECOR_ALL,0,0,800,600){
	
	contents=new FXHorizontalFrame(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 0,0,0,0);
	
	// LEFT pane to contain the canvas
	canvasFrame=new FXVerticalFrame(contents,FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,10,10);
	
	image=new MyImage(a,NULL,IMAGE_OWNED,MYW,MYH);
	aaimage=new MyImage(a,NULL,IMAGE_OWNED,MYW,MYH);

		// Label above the canvas
		new FXLabel(canvasFrame,"Canvas Frame",NULL,JUSTIFY_CENTER_X|LAYOUT_FILL_X);
	
		// Horizontal divider line
		new FXHorizontalSeparator(canvasFrame,SEPARATOR_GROOVE|LAYOUT_FILL_X);

		// Drawing canvas
		canvas=new FXCanvas(canvasFrame,this,ID_CANVAS,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT);

	// RIGHT pane for the buttons
	buttonFrame=new FXVerticalFrame(contents,FRAME_SUNKEN|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,10,10);

		// Label above the buttons	
		new FXLabel(buttonFrame,"Button Frame",NULL,JUSTIFY_CENTER_X|LAYOUT_FILL_X);
		
		// Horizontal divider line
		new FXHorizontalSeparator(buttonFrame,SEPARATOR_RIDGE|LAYOUT_FILL_X);

		// Button to change color (moj vlastny :-))
		new FXButton(buttonFrame,"Change Co&lor",NULL,this,ID_CHCOL,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);

		// Button to antialias image (tiez vlastny :-))
		new FXButton(buttonFrame,"&Antialias",NULL,this,ID_MAKEAA,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);

		// Button to clear
		new FXButton(buttonFrame,"&Clear",NULL,this,ID_CLEAR,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);

		// Exit button
		new FXButton(buttonFrame,"&Exit",NULL,getApp(),FXApp::ID_QUIT,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);

	// Initialize private variables
	drawColor=RED;
	mdflag=0;
	dirty=0;
	changed=0;
	colid=0;
	image->create();
	FXDCWindow dc(image);
	dc.setForeground(FXRGB(255,255,255));
	dc.fillRectangle(0,0,MYW,MYH);
	}
		
 

// Create and initialize 
void AntiAliasWindow::create(){

	// Create the windows
	FXMainWindow::create();
	
	// Make the main window appear
	show();
	}

// Mouse button was pressed somewhere
long AntiAliasWindow::onMouseDown(FXObject*,FXSelector,void*){
	canvas->grab();

	// While the mouse is down, we'll draw lines
	mdflag=1;
	return 1;
	}

// The mouse has moved, draw a line
long AntiAliasWindow::onMouseMove(FXObject*, FXSelector, void* ptr){
	FXEvent *ev=(FXEvent*)ptr;
	if(mdflag){
		
		// Get DC for the image
		FXDCWindow dc(image);
		// Set foreground color
		dc.setForeground(drawColor);
		// Draw line
		dc.drawLine(ev->last_x, ev->last_y, ev->win_x, ev->win_y);
		changed=1;
		// We have drawn something, so now the canvas is dirty
		dirty=1;
		}
	return 1;
	}

// The mouse button was released again
long AntiAliasWindow::onMouseUp(FXObject*,FXSelector,void* ptr){
	FXEvent *ev=(FXEvent*) ptr;
	canvas->ungrab();
	if(mdflag){
		FXDCWindow dc(image);
		dc.setForeground(drawColor);
		dc.drawLine(ev->last_x, ev->last_y, ev->win_x, ev->win_y);
		changed=1;
		// We have drawn something, so now the canvas is dirty
		dirty=1;
		// Mouse no longer down
		mdflag=0;
		}
	return 1;
	}

// Paint the canvas
long AntiAliasWindow::onPaint(FXObject*,FXSelector,void* ptr){
	FXEvent *ev=(FXEvent*)ptr;
	FXDCWindow dc(canvas,ev);
	image->render();
	dc.drawImage(image,0,0);
	changed=0;
	return 1;
	}

// Handle the antialias message
#define VP	4 //vaha bodu
#define NVP	2 //vaha suseda
#define DVP	1 //vaha diagonalneho suseda

// Funkcia sa da riesit s readPixelom, ale je to NENORMALNE pomale
// Preto priame citanie dat - to ma ale aj hacik - napr. na Win
// nejdu plany za sebou ako RGB ale tusim ako BGR. ;-) takze pekne
// preindexovat: :-)
#define IRED	0
#define IGREEN	1
#define IBLUE	2
long AntiAliasWindow::onMakeAA(FXObject*,FXSelector,void*){
	FXImage *tmp;

	image->restore();
	aaimage->create();
	FXDCWindow tdc(aaimage);	//target DC
	int x,y;
	FXuchar *idata=image->getData();
	//FXuchar *odata=aaimage->getData();
	for (y=0;y<MYH;y++) {
		//printf("Riesim %d. riadok.\n",y);
		for (x=0;x<MYW;x++) {
			FXuchar *p=idata+(y*MYW+x)*3;
			int d=VP;
			int r=p[IRED]*VP;
			int g=p[IGREEN]*VP;
			int b=p[IBLUE]*VP;
			if (x) {
				FXuchar *p=idata+(y*MYW+x-1)*3;
				r+=p[IRED]*NVP;
				g+=p[IGREEN]*NVP;
				b+=p[IBLUE]*NVP;
				d+=NVP;
				}
			if (x<MYW-1) {
				FXuchar *p=idata+(y*MYW+x+1)*3;
				r+=p[IRED]*NVP;
				g+=p[IGREEN]*NVP;
				b+=p[IBLUE]*NVP;
				d+=NVP;
				}
			if (y) {
				FXuchar *p=idata+((y-1)*MYW+x)*3;
				r+=p[IRED]*NVP;
				g+=p[IGREEN]*NVP;
				b+=p[IBLUE]*NVP;
				d+=NVP;
				}
			if (y<MYH-1) {
				FXuchar *p=idata+((y+1)*MYW+x)*3;
				r+=p[IRED]*NVP;
				g+=p[IGREEN]*NVP;
				b+=p[IBLUE]*NVP;
				d+=NVP;
				}
			if (x && y) {
				FXuchar *p=idata+((y-1)*MYW+x-1)*3;
				r+=p[IRED]*DVP;
				g+=p[IGREEN]*DVP;
				b+=p[IBLUE]*DVP;
				d+=DVP;
				}
			if (x<(MYW-1) && y) {
				FXuchar *p=idata+((y-1)*MYW+x+1)*3;
				r+=p[IRED]*DVP;
				g+=p[IGREEN]*DVP;
				b+=p[IBLUE]*DVP;
				d+=DVP;
				}
			if (x && y<(MYH-1)) {
				FXuchar *p=idata+((y+1)*MYW+x-1)*3;
				r+=p[IRED]*DVP;
				g+=p[IGREEN]*DVP;
				b+=p[IBLUE]*DVP;
				d+=DVP;
				}
			if (x<(MYW-1) && y<(MYH-1)) {
				FXuchar *p=idata+((y+1)*MYW+x+1)*3;
				r+=p[IRED]*DVP;
				g+=p[IGREEN]*DVP;
				b+=p[IBLUE]*DVP;
				d+=DVP;
				}
			r/=d; g/=d; b/=d;
			//*(odata+(y*MYW+x)*3)=FXRGB(r,g,b);
			tdc.setForeground(FXRGB(r,g,b));
			tdc.drawPoint(x,y);
			}
		}
	image->destroy();
	tmp=aaimage;
	aaimage=image;
	image=tmp;
	changed=1;
	return 1;
	}

// Handle the clear message
long AntiAliasWindow::onCmdClear(FXObject*,FXSelector,void*){
	FXDCWindow dc(image);
	dc.setForeground(FXRGB(255,255,255));
	dc.fillRectangle(0,0,MYW,MYH);
	dirty=0;
	changed=1;
	return 1;
	}


// Handle the change color message
long AntiAliasWindow::onColChange(FXObject*,FXSelector,void*){
	(colid==3)?colid=0:colid++;
	switch(colid) {
		case 0: drawColor=RED;
			break;
		case 1: drawColor=BLUE;
			break;
		case 2: drawColor=GREEN;
			break;
		case 3: drawColor=BLACK;
			break;
		}
	return 1;
	}

// Updating Clear button
long AntiAliasWindow::onUpdClear(FXObject* sender,FXSelector,void*){

	if(dirty)
		sender->handle(this,MKUINT(FXWindow::ID_ENABLE,SEL_COMMAND),NULL);
	else
		sender->handle(this,MKUINT(FXWindow::ID_DISABLE,SEL_COMMAND),NULL);

	if (changed) {
		FXDCWindow dc(canvas);
		image->render();
		dc.drawImage(image,0,0);
		changed=0;
		}
	return 1;
	}

// Here we begin
int main(int argc,char *argv[]){
	// Make application
	FXApp application("AntiAlias","FoxApp");
	// Start app
	application.init(argc,argv);
	// AntiAlias window
	new AntiAliasWindow(&application);
	// Create the application's windows
	application.create();
	// Run the application
	return application.run();
	}

