#include "StdAfx.h"
#include "wingraph.h"

#pragma warning(disable : 4996)



////////////////////////////////////////////////////////////////
// struktura pouzita pouze pro predani parametru novemu vlaknu
typedef struct{
	GRAPH *g;
	int iter;
	int method;
	bool *flag;
	HWND redraw;
}THREADPARAM;

static THREADPARAM thrParam;

WinGraph::WinGraph(HWND window){
	hWnd=window;
	penThen=CreatePen(PS_SOLID, 1, 0);
	penElse=CreatePen(PS_DASH, 1, RGB(0,0,255));
	penComp=CreatePen(PS_DOT, 1, RGB(255,0,0));
	graph.size.x=0;
	graph.size.y=0;
	graph.shift.x=0;
	graph.shift.y=0;
	graph.graph=NULL;
	graph.grid=80;
	graph.border=5;
	working=0;
}

WinGraph::~WinGraph(void){
	Cudd_GraphDelete(&graph);
}

////////////////////////////////////////////////////////////////
// funkce vola metodu pro ulozeni PS

void WinGraph::SavePs(TCHAR *path){
	char buffer[255];
	wcstombs(buffer, path, 260);
	Cudd_GraphSavePs(&graph ,buffer);
}

///////////////////////////////////////////////////////////////////
// procedura vlakna, vola optimalizaci a po skoncini prekresli okno

UINT ThrProc(LPVOID param){
	THREADPARAM *arg=(THREADPARAM*)param;
	RECT rect;
	Cudd_GraphOptimalize(arg->g, arg->method, arg->iter);
	*arg->flag=false;
	GetClientRect(arg->redraw, &rect);
	SendMessage(arg->redraw, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rect.right, rect.bottom));
	InvalidateRect(arg->redraw, NULL, true);
	return 0;
}

////////////////////////////////////////////////////////////////
// funkce vytvori BDD nacte soubor z PLA souboru vytvori graf
// a zavola optimalizaci podle druheho param. s poctem iteraci danou 
// tretim param.

bool WinGraph::LoadPLA(TCHAR *path, int method, int iter){
	static DdManager *manager;
	BddArray *pla;
	FILE *file = _wfopen(path, TEXT("r"));
	manager=Cudd_Init(0,0,CUDD_UNIQUE_SLOTS,CUDD_CACHE_SLOTS,0);
	graph.shift.x=0;
	graph.shift.y=0;
	pla = cudd_PlaLoad(manager, file);
	if(pla==NULL){
		return false;
	}
	fclose(file);
	if(graph.size.x!=0 || graph.size.y!=0) Cudd_GraphDelete(&graph);
	if(!Cudd_GraphMake(&graph, manager, pla->roots, pla->outputs, pla->inputs, pla->onames, pla->inames)){
		Cudd_GraphDelete(&graph);
		Cudd_Quit(manager);
		return false;
	}
	working = true;
	thrParam.g=&graph;
	thrParam.method=method;
	thrParam.iter=iter;
	thrParam.flag=&working;
	thrParam.redraw=hWnd;
	workThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)&ThrProc, &thrParam, 0, NULL);

	Cudd_Quit(manager);
	return true;
}

/////////////////////////////////
// funkce prekresli okno

void WinGraph::Draw(){
	WCHAR buffer[MAXNODENAME];
	POINT point, rectBegin, rectEnd, rectChildBegin, rectChildEnd;
	PNODE akt;
	HDC hdc;
	RECT rect;
	PAINTSTRUCT ps;
	SIZE size;
	if(graph.graph==NULL) return ;
	hdc = BeginPaint(hWnd, &ps);
	GetClientRect(hWnd, &rect);
	if(working){
		mbstowcs(buffer, "Program is running, it may take a several minutes.", MAXNODENAME);
		GetTextExtentPoint(hdc, buffer, (int)wcslen(buffer), &size); 
		TextOut(hdc, (rect.right-size.cx)/2, rect.bottom/2, buffer, (int)wcslen(buffer));
		return ;
	}
	SelectObject(hdc, penThen);
	for(int j = 0;j<graph.size.y;j++){
		for(int i=0;i<graph.size.x;i++){
			if (graph.graph[i][j]!=NULL){
				akt=graph.graph[i][j];
				// Vykresli ctverec a vypise Index
				point.x=i;
				point.y=j;
				GetNameRect(&graph, point, &rectBegin, &rectEnd);				

				if(akt->type<=1){	
					if(akt->name!=NULL) mbstowcs(buffer, akt->name, MAXNODENAME);
					RoundRect(hdc, rectBegin.x, rectBegin.y, rectEnd.x, rectEnd.y, 10, 10);
					TextOut(hdc, rectBegin.x+(graph.border/2), rectBegin.y+(graph.border/2), buffer, (int)wcslen(buffer));
				}
				// nakresli caru k naslednikum		
				if (akt!=graph.one){
					if(((akt->type==1 || akt->type==4) && !akt->comp) || akt->type==2  ) SelectObject(hdc, penThen);
						else if(akt->comp) SelectObject(hdc, penComp);
							else SelectObject(hdc, penElse);
					point=akt->pElse->loc;
					MoveToEx(hdc, rectBegin.x+((rectEnd.x-rectBegin.x)/2), rectEnd.y, NULL);
					GetNameRect(&graph, point, &rectChildBegin, &rectChildEnd);					
					LineTo(hdc, rectChildBegin.x+((rectChildEnd.x-rectChildBegin.x)/2), rectChildBegin.y);
					SelectObject(hdc, penThen);
					if(akt->type) continue;
					point=akt->pThen->loc;
					GetNameRect(&graph, point, &rectChildBegin, &rectChildEnd);
					MoveToEx(hdc, rectBegin.x+((rectEnd.x-rectBegin.x)/2), rectEnd.y, NULL);
					LineTo(hdc, rectChildBegin.x+((rectChildEnd.x-rectChildBegin.x)/2), rectChildBegin.y);
					
				}
			}
		}
	}
	EndPaint(hWnd, &ps);
}

////////////////////////////////////////////////////////////////
// funkce se stara o horiz. a vert. posuvniky okna

void WinGraph::Scroll(int nBar, int to, unsigned int step){
	si.cbSize = sizeof(si);
	si.fMask = SIF_ALL;
	GetScrollInfo(hWnd, nBar, &si);
	if(nBar==SB_HORZ) iHorzPos=si.nPos;
	else if(nBar==SB_VERT) iVertPos=si.nPos;
	switch(to){
		case SB_LINELEFT:
			si.nPos-=step;
			break;
		case SB_LINERIGHT:
			si.nPos+=step;
			break;
		case SB_PAGELEFT:
			si.nPos-=si.nPage;
			break;
		case SB_PAGERIGHT:
			si.nPos+=si.nPage;
			break;
		case SB_THUMBPOSITION:
			si.nPos=si.nTrackPos;
			break;
		default:
			return ;
	}
	si.fMask=SIF_POS;
	SetScrollInfo(hWnd, nBar, &si, true);
	GetScrollInfo(hWnd, nBar, &si);
	if(nBar==SB_HORZ) graph.shift.x=si.nPos;
	else if(nBar==SB_VERT) graph.shift.y=si.nPos;
	InvalidateRect(hWnd, &drawRect, true);
}

/////////////////////////////////////
// procedura zmeni velikost posuvniku

void WinGraph::Resize(int wParam){
	if(working) return ;
	GetClientRect(hWnd, &drawRect);
	drawRect.top=29;
	if(wParam==SIZE_MAXIMIZED) {
		graph.shift.x=0;
		graph.shift.y=0;
	}
	si.cbSize=sizeof(SCROLLINFO);
	// Nastaveni rozsahu horizontalniho posuvniku
	si.fMask=SIF_RANGE | SIF_PAGE | SIF_POS;
	si.nMin=0;
	si.nMax=graph.size.x;
	si.nPos=graph.shift.x;
	si.nPage=(drawRect.right-drawRect.left)/graph.grid;
	SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
	// Nastaveni rozsahu vertikalniho posuvniku
	si.fMask=SIF_RANGE | SIF_PAGE | SIF_POS;
	si.nMin=0;
	si.nPos=graph.shift.y;
	si.nMax=graph.size.y;
	si.nPage=(drawRect.bottom-drawRect.top)/graph.grid;
	SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}

////////////////////////////////
// procedura zmeni meritko grafu

void WinGraph::Zoom(int scale){
	POINT a, b;
	unsigned int minzoom;
	graph.grid+=scale;
	if(graph.graph==NULL) return ;
	GetNameRect(&graph, graph.one->loc, &a, &b);
	minzoom=b.y-a.y+graph.border;
	if((signed int)graph.grid<minzoom) graph.grid=minzoom;
	if(graph.grid>minzoom*5) graph.grid=minzoom*5;
	Resize(SIZE_RESTORED);
	InvalidateRect(hWnd, &drawRect, true);
}
