// GlutExamples.cpp: определяет точку входа для консольного приложения.
//

#include  <stdlib.h>
#define _USE_MATH_DEFINES
#include  <math.h>
#include  <time.h>
#include  <string.h>
#include "gl/glut.h"

#pragma comment(lib, "freeglut.lib")

int ButtonStatus = 0;

double* a;
int N = 0;
int Ncur = -1;//зхвачена точка
double eps = 20;

int* edges;
int M = 0;
int istart = -1;

void init()
{
	glViewport(0, 0, 500, 500);//встановлює розміри області відображення
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glMatrixMode(GL_PROJECTION);//Задає режим проектування - ортографічна проекція
	glLoadIdentity();//Ініціалізація матриць проектування одиничними матрицями
	glOrtho(-250.0, 250.0, -250.0, 250.0, -1.0, 1.0);  //Задає параметри проектування (систему координат)
	glMatrixMode(GL_MODELVIEW);
}
void displayText(float x, float y, char* s) {
	int j = strlen(s);
	glRasterPos2f(x, y);
	for (int i = 0; i < j; i++) {
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, s[i]);
	}
}
void circle(int x0, int y0, int r) {
	glBegin(GL_LINE_LOOP);
	for (int i = 0; i < 360; i++) {
		float x = x0 + r * cos(i*M_PI/180);
		float y = y0 + r * sin(i*M_PI/180);
		glVertex2f(x, y);
	}
	glEnd();
}

void display()
{
	char buf[80];
	double xx, yy;

	glClear(GL_COLOR_BUFFER_BIT);
	glLoadIdentity();
	glEnable(GL_POINT_SMOOTH);//точки згладженні. (якщо Disable - то квадратні)

	//Формування графічної інформації
	for (int i = 0; i < N; i++)
	{
		xx = (int)a[2 * i];
		yy = (int)a[2 * i + 1];

		//Малювання примітивів
		//glBegin(GL_POINTS);//Інші режими: GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON 
		//glVertex2d(xx, yy);// Вершина примітиву. В данному разі - точка.
		//glEnd();
		
		circle(xx, yy, eps);
		
		_itoa_s(i+1, buf, 80, 10);
		displayText(xx + 16, yy + 20, buf);
	}

	//Draw edges
	glBegin(GL_LINES);
	for (int i = 0; i < M; i++)
	{
		xx = (int)a[2 * edges[2 * i]];
		yy = (int)a[2 * edges[2 * i] + 1];
		glVertex2d(xx, yy);

		xx = (int)a[2 * edges[2 * i + 1]];
		yy = (int)a[2 * edges[2 * i + 1] + 1];
		glVertex2d(xx, yy);
	}
	glEnd();

	glFlush();
	//glutSwapBuffers();//Зміна графічних сторінок (корисно при анімації - проти "миготіння")
}

bool isin(int x, int y, int& n)
{
	n = -1;
	for (int i = 0; i < N; i++)
		if (sqrt(((double)x - a[2 * i]) * (x - a[2 * i]) + (y - a[2 * i + 1]) * (y - a[2 * i + 1])) < eps)
		{
			n = i;
			return true;
		}
	return false;
}
void mouse(int button, int state, int x, int y)
{
	double xx, yy;
	int n = -1;//індекс точки
	//Random r = new Random();
	srand(time(0));
	switch (button)
	{
	case GLUT_RIGHT_BUTTON:
		if (state == GLUT_DOWN)
		{
			xx = x - 250;//Зв'язок координат миші та координат Opengl
			yy = 500 - y - 250;
			if (isin((int)xx, (int)yy, n))
			{
				istart = n;
				ButtonStatus = 1;
			}
			
		}else
			if (state == GLUT_UP)
			{
				xx = x - 250;//Зв'язок координат миші та координат Opengl
				yy = 500 - y - 250;
				if (ButtonStatus == 1 && istart!=-1 && isin(xx, yy, n))
				{
					edges[2 * M] = istart;
					edges[2 * M + 1] = n;
					M++;
					ButtonStatus == 0;
					istart = -1;
				}

			}
		break;
	case GLUT_MIDDLE_BUTTON:
		if (state == GLUT_DOWN)
		{
			//glutIdleFunc(NULL); 
		}
		break;
	case GLUT_LEFT_BUTTON:
		if (state == GLUT_DOWN)
		{
			xx = x - 250;//Зв'язок координат миші та координат Opengl
			yy = 500 - y - 250;
			if (!isin(xx, yy, n))
			{
				//Нова точка
				a[2 * N] = (double)xx;
				a[2 * N + 1] = (double)yy;
				N++;
				glutPostRedisplay();
			}
			else
			{
				//Зхвачено вже існуючу точку
				ButtonStatus = 1;
				Ncur = n;
				//Поновлюємо координати
				a[2 * n] = (double)xx;
				a[2 * n + 1] = (double)yy;

			}
		}
		else
			if (state == GLUT_UP)
			{
				//Перетаскування завершено
				ButtonStatus = -1;
			}
		break;
	default:
		break;
	}
}
void MouseMotion(int x, int y)
{
	if (ButtonStatus == 1)
	{
		double xx, yy;
		xx = x - 250;//Зв'язок координат миші та координат Opengl
		yy = 500 - y - 250;
		a[2 * Ncur] = xx;
		a[2 * Ncur + 1] = yy;
		glutPostRedisplay();
	}
}
void keyb(unsigned char key, int x, int y)
{
	const unsigned char esc = 27;
	if (key == esc)
		exit(0);
}

void reshape(int w, int h)
{
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-250.0, 250.0, -250.0, 250.0, -1.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}


int main(int argc, char* argv[])
{
	a = new double[600];
	edges = new int[200];

	glutInit(&argc, argv);//ініціалізує бібліотеку GLUT

	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	/* визначає, яку колірну модель слід використовувати: режим RGBA або режим індексації кольору.
	* Можна також визначити, чи прагнете ви працювати з буфером кадру вікна з одинарної або з подвійний буферизацією.
	* (Якщо ви працюєте в режимі індексації кольору, то ви, можливо, захочете завантажити деякі кольори в
	* таблицю компонентів кольору; для того щоб зробити це, скористайтеся командою glutSetColor().)
	* Нарешті, можна використовувати цю функцію для того, щоб вказати, що ви прагнете зв'язати з даним вікном
	* буфери глибини, трафарету й/або буфер-накопичувач. Наприклад, якщо ви бажаєте використовувати вікно з
	* подвійною буферизацією, колірною моделлю RGBA і буфером глибини, то для цього можна викликати розглянуту
	* функцію з наступними параметрами:
	* glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH).*/


	glutInitWindowSize(500, 500);//визначає розмір створюваного вікна в пікселях.
	glutInitWindowPosition(0, 0);//визначає місце розташування лівого верхнього кута створюваного вікна на екрані монітора.

	glutCreateWindow("hello");
	/* створює вікно з контекстом open Ця підпрограма повертає унікальний ідентифікатор для нового вікна.
	* Майте на увазі: доти, поки викликається підпрограма glutMainLoop(), це вікно ще не відображається на екрані.*/

	init();

	glutDisplayFunc(display);
	/* являє собою першу й найбільш важливу функцію зворотного виклику за подією.
	* Щоразу, коли бібліотека GLUT визначає, що вміст даного вікна має бути поновлений,
	* виконується функція зворотного виклику, зареєстрована підпрограмою glutDisplayFunc().
	* Тому ви повинні помістити всі підпрограми, які необхідні для перемальовування сцени,
	* у дану функцію зворотного виклику відображення.
	* Якщо ваша програма змінює вміст вікна, то іноді ви маєте викликати підпрограму glutPostRedisplay(void),
	* яка змушує підпрограму glutMainLoop() викликати зареєстровану функцію зворотного виклику відображення
	* при наступному зручному випадку
	*/

	glutReshapeFunc(reshape);//вказує на те, яке саме дія повинна бути виконана при зміні розміру вікна

	glutMouseFunc(mouse);//реєструє функцію, яка буде реагувати на події, що пов'язані з мишкою
	glutMotionFunc(MouseMotion);
	glutKeyboardFunc(keyb);//реєструє функцію, яка буде реагувати на події, що пов'язані з клавіатурою

	//glutIdleFunc(display);//реєструє функцію, що буде виконуватися постійно (у фоні)

	glutMainLoop();
	/* При цьому відображаються всі вікна, які були створені, і в цих вікнах тепер працює візуалізація.
	* Починається обробка подій, і викликається зареєстрована функція зворотного виклику відображення.
	* Увійшовши одного разу в цей цикл, з нього не виходять ніколи!*/

	return 0;
}
