#include <QtGui>
#include <QtCore>
// #include <QGLWidget>
#include <QtOpenGL>
#include <QIODevice>
#include <math.h>

#include "scene3D.h"
#include "surface3D.h"

static int num = 25; // номер треугольника
static int num2 = 0; // номер узла
static int Mtriangle_Sph_size;
static int last_part3;
//const static float pi=3.141593, rad=pi/180; // глобальная переменная

Scene3D::Scene3D(QWidget* pwgt/*= 0*/) : QGLWidget(pwgt), xRotate(-90), yRotate(0), zRotate(0), nScale(1), yTransl(-0.0)
{ 
   // передает дальше указатель на объект pwgt 
} 

/*virtual*/void Scene3D::initializeGL() // инициализация
{
   qglClearColor(Qt::white);         // цвет для очистки буфера изображения
   glEnable(GL_DEPTH_TEST);          // устанавливает режим разрешения проверки глубины фрагментов
   // glShadeModel(GL_FLAT);            // отключает режим сглаживания цветов 
   
   putbase(); // загружаю базу данных   
   last_part3=put_last_part3(); // определяю последний номер узла       
   Mtriangle_Sph_size=putMtriangle_Sph_size(); // определяю число Mtriangle_Sph_size треугольников;   
   triangle=new Surface3D [Mtriangle_Sph_size]; // создаю массив объектов triangle класса Surface3D длиной Mtriangle_Sph_size
                                                // одновременно создаю таблицу отображений для каждого треугольника
   Axes=1; // оси
   Point=1; // узел
   wheelSwitch=1; // на масштаб
}

/*virtual*/void Scene3D::resizeGL(int nWidth, int nHeight) // окно виджета
{ 
   glMatrixMode(GL_PROJECTION);                     // делает текущей матрицу проектирования (проекционная матрица)
   glLoadIdentity();                                // присваивает матрице проектирования единичную матрицу 
                                                    // перемножение матриц                                                 

//    glViewport(0, 0, (GLint)nWidth, (GLint)nHeight); // устанавливает видовое окно с размерами равными окну виджета
//    pScale=1;
   glViewport(0, ((GLint)nHeight-(GLint)nWidth)/2, (GLint)nWidth, (GLint)nWidth); //  !!!теперь сохраняются пропорции при изменении размера окна!!!
   pScale=(GLfloat)nHeight/(GLfloat)nWidth; // пропорциональный коефициент масштаба
   
   nHt=nHeight;
   nWt=nWidth;
  
   // glFrustum(-0.0, 0.0, -0.0, 0.0, 0.0, 0.0);      // параметры видимости перспективной проекции
   
   // плоскости отсечения: (левая, правая, верхняя, нижняя, передняя, задняя)
   
   glOrtho(-1.0, 1.0, -1.0, 1.0, -10.0, 1.0);        // параметры видимости ортогональной проекции
}

/*virtual*/void Scene3D::paintGL() // рисование
{ 
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // очистка буфера изображения и глубины 
                                                       // невидемые поверхности удалены

   glMatrixMode(GL_MODELVIEW);                         // задает положение и ориентацию матрице моделирования
   glLoadIdentity();                                   // устанавливает единичную матрицу моделирования
 
   glScalef(pScale*nScale*1.0f, pScale*nScale*1.0f, pScale*nScale*1.0f);
   
   glTranslatef(0.0, yTransl, 0.0);                    // сдвигает начало системы координат
      
   glRotatef(xRotate, 1.0, 0.0, 0.0); 
   glRotatef(yRotate, 0.0, 1.0, 0.0);
   glRotatef(zRotate, 0.0, 0.0, 1.0);   
   
   glEnable(GL_CULL_FACE); // строются только внешние поверхности треугольников
   
   for (GLuint i=0; i<Mtriangle_Sph_size; i++)
   { 
      glCallList(triangle[i].put_n_triangleList());  // передача в функцию glCallList номера дисплейного списка. 
                                                     // реализация таблицы отображений   
   }    
                      
   //glDeleteLists(nSceneObjects, 1); // удаляю таблицу отображений
   
   if (Point==1) drawPoint();
   if (Axes==1) drawAxes();
   //drawTriangle();
} 

void Scene3D::save() // запись
{
   R_save(triangle);
}

void Scene3D::load() // загрузка
{
   delete[] triangle;
   set_num(0);
   R_load(); // загружаю радиусы из save.dat
   triangle=new Surface3D [Mtriangle_Sph_size];
}

void Scene3D::create_new() // создать новую поверхность
{
   delete[] triangle;
   set_num(0);
   R_new(); // загружаю радиусы из base002.dat
   triangle=new Surface3D [Mtriangle_Sph_size];
}

/*virtual*/void Scene3D::mousePressEvent(QMouseEvent* pe) 
{ 
   ptrMousePosition = pe->pos(); // при нажатии пользователем кнопки мыши переменной ptrMousePosition будет 
                                 // присвоена координата указателя мыши 
   // ptrMousePosition = (*pe).pos();
   
   mousePar=1;                           
} 

/*virtual*/void Scene3D::mouseReleaseEvent(QMouseEvent* pe) 
{ 
   if (mousePar==1)
   {
      triangleSelection( (GLfloat) pe->x(), (GLfloat) pe->y());
   } 
   
   updateGL(); // обновление изображения                        
} 

/*virtual*/void Scene3D::mouseMoveEvent(QMouseEvent* pe) 
{

   xRotate += 180/nScale * (GLfloat) (pe->y() - ptrMousePosition.y()) / height(); // вычисление углов поворота
   zRotate += 180/nScale * (GLfloat) (pe->x() - ptrMousePosition.x()) / width();    
 
   updateGL(); // обновление изображения
   
   ptrMousePosition = pe->pos(); 
   mousePar=0;
}

/*virtual*/void Scene3D::keyPressEvent(QKeyEvent* pe)
{  
   switch (pe -> key())
   {
//          case Qt::Key_Plus:  
//          nScale = nScale*1.01;
//          break;
//          
//          case Qt::Key_Minus:  
//          nScale = nScale/1.01;
//          break;

         case Qt::Key_Equal:  // прибавления радиуса узла
         R_Plus(num2, triangle);       
         break;
         
         case Qt::Key_Minus: // убавление радиуса узла
         R_Minus(num2, triangle);
         break;

         case Qt::Key_Up:  
         xRotate += 1.0;
         break;
         
         case Qt::Key_Down:  
         xRotate -= 1.0;
         break;         
         
         case Qt::Key_Left:  
         zRotate += 1.0;
         break;
         
         case Qt::Key_Right:  
         zRotate -= 1.0;
         break;
         
         case Qt::Key_Space:  // возвращение значений по умолчанию
         nScale = 1;
         xRotate = -90;
         yRotate = 0;
         zRotate = 0;
         break;     
         
         case Qt::Key_Period:
         if (num2<last_part3) num2++;
         break;  
         
         case Qt::Key_Comma:
         if (num2>0) num2--;
         break;  
         
         case Qt::Key_A:
         if (Axes==0) Axes=1; else Axes=0;
         break;   
         
         case Qt::Key_S:
         if (Point==0) Point=1; else Point=0;
         break;  
         
         case Qt::Key_W:
         if(pe->modifiers() & Qt::ControlModifier)
         {
            if (wheelSwitch==0) wheelSwitch=1; else wheelSwitch=0;
         }
         break;    
         
         case Qt::Key_F9:
         save();
         break;  
         
         case Qt::Key_F10:
         load();
         break; 
         
         case Qt::Key_N:
         if(pe->modifiers() & Qt::ControlModifier)
         {
            create_new();
         }
         break;  
         
         case Qt::Key_Z:
         yTransl -= 0.05;
         break;  
         
         case Qt::Key_X:
         yTransl += 0.05;
         break;                                    
   }

   updateGL(); // обновление изображения
}

/*virtual*/void Scene3D::wheelEvent(QWheelEvent* pe) 
{ 
   if (wheelSwitch==1)
   {
      if ((pe->delta())>0) nScale = nScale*1.1; else if ((pe->delta())<0) nScale = nScale/1.1;   
   }
   else
   {      
      if ((pe->delta())>0)
      {
         R_Plus(num2, triangle);        
      } 
      else if ((pe->delta())<0) 
         {
            R_Minus(num2, triangle);      
         }   
   }
      
   updateGL(); // обновление изображения          
} 

void Scene3D::drawPoint()
{
   single_out_v_contour(num2, triangle); 
}

void Scene3D::drawTriangle()
{
   triangle[num].single_out(); 
}

void Scene3D::drawAxes()
{
   glLineWidth(1);
   // полярная ось
   glBegin(GL_LINES);
      glColor4f(0.00f, 0.00f, 1.00f, 1.0f);
      glVertex3f( 0,  0,  1);
      glVertex3f( 0,  0, -1);
   glEnd();     
   
   // дополнительные оси
   glBegin(GL_LINES);
      glColor4f(1.00f, 0.00f, 0.00f, 1.0f);
      glVertex3f( 0,  1,  0);
      glVertex3f( 0, -1,  0);
   glEnd();  
   
   glBegin(GL_LINES);
      glColor4f(0.00f, 0.00f, 1.00f, 1.0f);
      glVertex3f( 1,  0,  0);
      glVertex3f(-1,  0,  0);
   glEnd();
}

void Scene3D::triangleSelection(int xPos, int yPos) // выбрать треугольник с помощью координаты мыши
{
   GLuint selectBuffer[4]; // буфер выбора (буфер совпадений)
   GLint hits, viewport[4]; // счетчик соответствий и матрица поля просмотра
   
   glSelectBuffer(4,  selectBuffer); // настройка буффера выбора
   glGetIntegerv(GL_VIEWPORT, viewport); // извлекаю поле просмотра в viewport
   
   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   
   glRenderMode(GL_SELECT); // перехожу в режим выбора
   
   glInitNames(); // инициализируется и очищается стек имен
   glPushName(0); // в стек помещается значение 0 (обязательно должен храниться хотя бы один элемент)
   
   glLoadIdentity();
   
   gluPickMatrix(xPos, (viewport[3]*pScale-yPos), 1, 1, viewport); // новый объем под указателем мыши
   
   glViewport(0, ((GLint)nHt-(GLint)nWt)/2, (GLint)nWt, (GLint)nWt);
   glOrtho(-1.0, 1.0, -1.0, 1.0, -10.0, 1.0);
   
   glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
   for (GLuint i=0; i<Mtriangle_Sph_size; i++)
   { 
      glLoadName(i+1); // дается имя, которое помещается на вершину стека                                                               
      triangle[i].paint_no_color();
   } 
   
   hits=glRenderMode(GL_RENDER); // число совпадений и переход в режим рисования
   
   num=selectBuffer[3]-1; // номер выбранного треугольника
    
   if (hits!=0) pointSelection(xPos, yPos, num, viewport);
   
   glMatrixMode(GL_PROJECTION);
   glPopMatrix();   
   
   updateGL(); // обновление изображения
}

void Scene3D::pointSelection(int xPos, int yPos, int i, GLint *vp) // выбрать узел с помощью координаты мыши
{
   int n0, n1, n2;  
   GLdouble x0, y0, z0, x1, y1, z1, x2, y2, z2; // мировые координаты
   GLdouble x0Pos, y0Pos, x1Pos, y1Pos, x2Pos, y2Pos; // координаты точек в проекции экрана
   GLdouble z0Pos, z1Pos, z2Pos; // z-буффер (глубина)
   float l0, l1, l2; // расстояние от точки мыши до узлов в окне
   
   n0=triangle[i].putn0();  
   x0=triangle[i].putx0();
   y0=triangle[i].puty0();
   z0=triangle[i].putz0();
   
   n1=triangle[i].putn1();  
   x1=triangle[i].putx1();
   y1=triangle[i].puty1();
   z1=triangle[i].putz1(); 
   
   n2=triangle[i].putn2();
   x2=triangle[i].putx2();
   y2=triangle[i].puty2();
   z2=triangle[i].putz2();  
   
   GLdouble modelmatrix[16], projmatrix[16];
   glGetDoublev (GL_MODELVIEW_MATRIX, modelmatrix); // извлечь модельно-видовую матрицу в modelmatrix
   glGetDoublev (GL_PROJECTION_MATRIX, projmatrix); // извлечь матрицу проекции в projmatrix
   
   gluProject(x0, y0, z0, modelmatrix, projmatrix, vp, &x0Pos, &y0Pos, &z0Pos); 
   gluProject(x1, y1, z1, modelmatrix, projmatrix, vp, &x1Pos, &y1Pos, &z1Pos);
   gluProject(x2, y2, z2, modelmatrix, projmatrix, vp, &x2Pos, &y2Pos, &z2Pos);  
   
   l0=(xPos-x0Pos)*(xPos-x0Pos)+(yPos-y0Pos)*(yPos-y0Pos);
   l1=(xPos-x1Pos)*(xPos-x1Pos)+(yPos-y1Pos)*(yPos-y1Pos);
   l2=(xPos-x2Pos)*(xPos-x2Pos)+(yPos-y2Pos)*(yPos-y2Pos);
   
   num2=n0;
   if ((l0 <= l1) & (l0 <= l2)) num2=n0;
   if ((l1 <= l0) & (l1 <= l2)) num2=n1;
   if ((l2 <= l0) & (l2 <= l1)) num2=n2;

//    
//    // записываю в файл  
//    QFile file1("help.txt");
//    file1.open(QIODevice::WriteOnly);
//    QTextStream out1(&file1);
//    out1 << xPos << " " << yPos <<"\n";
//    out1 << x0Pos << " " << y0Pos <<"\n";
//    file1.close(); // файл можно и не закрывть, если он дальше не используется снова  
}

void Scene3D::R_Plus(int i, Surface3D* triangle)
{
   int tri0, tri1, tri2, tri3, tri4, tri5;
   redefine_v(i, (put_R(i, triangle)+1), triangle, &tri0, &tri1, &tri2, &tri3, &tri4, &tri5);       
   // перезаписываю таблицу отображений
   triangle[tri0].create_triangleList(1.0f);
   triangle[tri1].create_triangleList(1.0f); 
   triangle[tri2].create_triangleList(1.0f);
   triangle[tri3].create_triangleList(1.0f); 
   triangle[tri4].create_triangleList(1.0f);
   if (tri4 != tri5) triangle[tri5].create_triangleList(1.0f);
}

void Scene3D::R_Minus(int i, Surface3D* triangle)
{
   int tri0, tri1, tri2, tri3, tri4, tri5;
   redefine_v(i, (put_R(i, triangle)-1), triangle, &tri0, &tri1, &tri2, &tri3, &tri4, &tri5);       
   // перезаписываю таблицу отображений
   triangle[tri0].create_triangleList(1.0f);
   triangle[tri1].create_triangleList(1.0f); 
   triangle[tri2].create_triangleList(1.0f);
   triangle[tri3].create_triangleList(1.0f); 
   triangle[tri4].create_triangleList(1.0f);
   if (tri4 != tri5) triangle[tri5].create_triangleList(1.0f);
}