/*  Aplikace pro zpracovani dat.
 *  Toto je hlavni formular a obsluhuje se zde veskere vykreslovani a ovladani hlavniho okna
 *  Strucny popis ostatnich trid:
 *  
 *  Calculate.cs - funkce pro vypocty
 *  Drawing.cs - funkce pro vykreslovani os, mrizky..
 *  HoughTransform.cs - trida Houghova transformace
 *  Line.cs - trida usecka
 *  Mesh.cs - trida mracna bodu (objekt - scan). Obsluhuje vse co se tyka zobrazovanych bodu.
 *  Octree.cs - trida oktalovy strom
 *  Segment.cs - trida segmentu
 *  Surface.cs - trida plochy
 *  Vertex.cs - trida bodu
 * 
 */

#region Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;
using System.Globalization;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Audio;
using OpenTK.Math;
using OpenTK.Input;
using OpenTK.Platform;
using System.Diagnostics;

using System.Threading;

#endregion


namespace DataProcessing
{
    public partial class FormMain : Form
    {
        #region variable definitions

        //kolekce objektu(meshes), a jeji undo a redo clony.
        private ArrayList meshes = new ArrayList();
        private ArrayList undoMeshes = new ArrayList();
        private ArrayList redoMeshes = new ArrayList();

        //Prave zvoleny objekt (mesh)
        private Mesh selectedMesh; //= new Mesh();

        public Mesh SelectedMesh
        {
            get { return selectedMesh; }
            set { selectedMesh = value; }
        }

        // mouse
        private static bool isAlt = false;
        private static bool isControl = false;
        private float lastX, lastY; // predchazejici hodnoty kurzoru

        //promene pro ovladani pohledu
        private float xPos = 0.0f, yPos = 0.0f, zPos = 0, xRot = 35.0f, yRot = -45.0f; //pozice a natoceni kamery
        private float cRadius = 4000.0f; // vzdalenost kamery (zoom)        

        //pracovni mody: selektovani, selektovani bodu, posouvani objektu, otaceni objektu, spojovani objektu
        private bool selectionMode = true;
        private bool vertexSelection;


        //akce mysi: selektovani
        private bool selecting = false;

        //zobrazovat body, cary, plochy, segmenty
        private bool showVerts = true;
        private bool showLines = true;
        private bool showSurfaces = true;
        private bool showSegments = true;

        //zobrazovat mrizku, osy, bounding box, zarovnavat pohled na mesh po kliknuti na mesh
        private bool showGrid = true;
        private bool showWorldAxes = true;
        private bool showBoundingBoxes = true;
        private bool showLights = true;
        private bool centreToMesh = true;

        //typ kamery
        private string cameraType = "ortho";

        //Pocatecni a koncovy bod "select" obdelniku pri tazeni mysi
        private Point selectStart;
        private Point selectEnd;

        // Ogl okno: vyska, sirka, pomer stran
        private int glWidth;
        private int glHeight;
        private float viewRatio;

        // stopky pro mereni casu
        private Stopwatch stopWatch = new Stopwatch();

        // Formy pro editaci, octree, houghova transformace, semantika.
        private FormEditation editationForm = null;
        private FormHough houghForm = null;
        private FormOctree octreeForm = null;
        private FormSemantic semanticForm = null;
        private FormSurface surfaceForm = null;

        public FormEditation EditationForm
        {
            get { return editationForm; }
            set { editationForm = value; }
        }

        public FormHough HoughForm
        {
            get { return houghForm; }
            set { houghForm = value; }
        }

        public FormOctree OctreeForm
        {
            get { return octreeForm; }
            set { octreeForm = value; }
        }

        public FormSemantic SemanticForm
        {
            get { return semanticForm; }
            set { semanticForm = value; }
        }

        public FormSurface SurfaceForm
        {
            get { return surfaceForm; }
            set { surfaceForm = value; }
        }

        //barvy vykreslovani
        private bool invertedColors = true;
        private Color clearColor = Color.Black;


        #endregion define variables

        public FormMain()
        {
            InitializeComponent();

        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            //po nahrati nastav ogl okno
            SetupViewport();
        }

        //reset neboli "novy dokument"
        private void Reset()
        {
            if (selectedMesh != null)
            {
                UnSelectMesh();
            }

            meshes.Clear();
            undoMeshes.Clear();
            redoMeshes.Clear();

            xPos = 0.0f;
            yPos = 0.0f;
            zPos = 0.0f;
            xRot = 35.0f;
            yRot = -45.0f;
            cRadius = 4000.0f;

            toolBtnSelection.PerformClick();
            menuItemUndo.Enabled = false;
            menuItemRedo.Enabled = false;
        }

        private void glControl_Resize(object sender, EventArgs e)
        {
            SetupViewport();
            glControl.Invalidate();
        }

        //prekreslit ogl okno
        public void ReDraw()
        {
            glControl.Invalidate();
        }

        //nastaveni ogl okna
        private void SetupViewport()
        {
            glWidth = glControl.Width;
            glHeight = glControl.Height;
            viewRatio = (float)glWidth / (float)glHeight;

            GL.MatrixMode(MatrixMode.Projection);   //nastaví projekční matici
            GL.LoadIdentity();                      //Reset matice


            //nastavenipohledu
            if (cameraType == "perspective")
                Glu.Perspective(30.0, viewRatio, 1.0, 100000.0);

            if (cameraType == "ortho")
                GL.Ortho(-viewRatio * cRadius / 3.7, viewRatio * cRadius / 3.7, -cRadius / 3.7, cRadius / 3.7, -100000.0, 100000.0);

            GL.Viewport(0, 0, glWidth, glHeight);   //nastaveni okna aktuální nastavení
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();

            GL.ClearDepth(1.0);                     // Nastavení hloubkového bufferu
            GL.Enable(EnableCap.DepthTest);         // Povolí hloubkové testování
            GL.DepthFunc(DepthFunction.Lequal);     //Typ hloubkového testování  

            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Fastest);   // nejhorsi (nejrychlejsi) perspektivní korekce
            GL.Enable(EnableCap.ColorMaterial);
            GL.ClearColor(clearColor);
        }

        // zde probiha vsechno vykreslovani v kazdem "framu"
        private void glControl_Paint(object sender, PaintEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);  //Smaže obrazovku a hloubkový buffer
            GL.LoadIdentity();

            // vykreslit obdelnik pri tazeni mysi?
            if (selecting)
                DrawRectangle();

            Drawing.DrawCentrePoint();

            //souradne osy
            if (showWorldAxes)
            {
                if (cameraType == "ortho")
                {
                    Drawing.DrawWorldAxes(xRot, yRot, cRadius, viewRatio, glWidth, glHeight, "ortho");
                }
                else
                {
                    Drawing.DrawWorldAxes(xRot, yRot, cRadius, viewRatio, glWidth, glHeight, "perspective");
                }
            }

            //posun a rotace kamery
            GL.Translate(0.0f, 0.0f, -cRadius);
            GL.Rotate(xRot, 1.0, 0.0, 0.0);
            GL.Rotate(yRot, 0.0, 1.0, 0.0);
            GL.Translate(-xPos, -yPos, -zPos);

            //mrizka
            if (showGrid)
                Drawing.DrawGrid();

            //pro kazdy mesh vykreslit vsechny jeho elementy
            foreach (Mesh mesh in meshes)
            {
                if (showVerts)
                    mesh.DrawVertices();

                if (showLines)
                    mesh.DrawLines();

                if (showLights)
                {
                    GL.Enable(EnableCap.Lighting);
                    GL.Enable(EnableCap.Light0);
                }

                if (showSurfaces)
                    mesh.DrawSurfaces();

                if (showSegments)
                    mesh.DrawSegments();

                if (showLights)
                {
                    GL.Disable(EnableCap.Lighting);
                    GL.Disable(EnableCap.Light0);
                }

                if (showBoundingBoxes)
                    mesh.DrawBoundingBox("wire");
            }

            glControl.SwapBuffers();
        }

        //Funkce pro prevadeni pozice mysi do ogl koordinatu
        private Vector3 GetOglPos(int x, int y)
        {
            int[] viewport = new int[4];
            double[] modelview = new double[16];
            double[] projection = new double[16];
            Vector3 winPos = new Vector3();
            Vector3 scenePos = new Vector3();

            GL.GetDouble(GetPName.ModelviewMatrix, modelview);
            GL.GetDouble(GetPName.ProjectionMatrix, projection);
            GL.GetInteger(GetPName.Viewport, viewport);

            winPos.X = (float)x;
            winPos.Y = (float)viewport[3] - (float)y;

            GL.ReadPixels(selectEnd.X, (int)winPos.Y, 1, 1, PixelFormat.DepthComponent, PixelType.Float, winPos.Z);
            Glu.UnProject(winPos, modelview, projection, viewport, out scenePos);

            return scenePos;
        }

        //vykreslovani pri kliknuti nebo tazeni mysi("selecting modu"),
        private void RenderInPickingMode()
        {
            GL.InitNames();

            if (selectedMesh != null & vertexSelection)
            {
                selectedMesh.DrawInSubSelectionMode();
                return;
            }

            int i = 0;
            foreach (Mesh mesh in meshes)
            {
                GL.PushName(i);
                mesh.DrawInSelectionMode();
                GL.PopName();
                i++;
            }
        }

        static int bufSize = 1000000;
        int[] selectBuf = new int[bufSize];

        //zacatek vybirani
        private void StartPicking(int centreX, int centreY, int rectWidth, int rectHeight)
        {
            int width = glControl.Width;
            int height = glControl.Height;
            float ratio = (float)width / (float)height;

            int[] viewport = new int[4];

            GL.SelectBuffer(bufSize, selectBuf);
            GL.RenderMode(RenderingMode.Select);

            GL.MatrixMode(MatrixMode.Projection);
            GL.PushMatrix();
            GL.LoadIdentity();

            GL.GetInteger(GetPName.Viewport, viewport);
            Glu.PickMatrix(centreX, viewport[3] - centreY, rectWidth, rectHeight, viewport);

            if (cameraType == "perspective")
                Glu.Perspective(30.0, viewRatio, 1.0, 100000.0);

            if (cameraType == "ortho")
                GL.Ortho(-viewRatio * cRadius / 3.7, viewRatio * cRadius / 3.7, -cRadius / 3.7, cRadius / 3.7, -100000.0, 100000.0);

            GL.MatrixMode(MatrixMode.Modelview);
            GL.InitNames();
        }

        //Konec vybirani
        private void StopPicking()
        {
            int hits;

            // obnova puvodni projekcni matice
            GL.MatrixMode(MatrixMode.Projection);
            GL.PopMatrix();
            GL.MatrixMode(MatrixMode.Modelview);
            GL.Flush();

            // navrat do normalniho rendering mode
            hits = GL.RenderMode(RenderingMode.Render);

            // pokud mame zasahy tak je spracujem
            if (hits != 0)
            {
                ProcessHits(hits, selectBuf);
                return;
            }

        }

        //zpracovani zasahu mysi, vybere se zasazeny objekt, nebo jednotlive body
        private void ProcessHits(int hits, int[] buffer)
        {
            if (vertexSelection)
            {
                selectedMesh.SelectVertex(buffer[3]);

                if (hits > 1)                               // Bylo více než nula zásahů?
                {
                    for (int loop = 1; loop < hits; loop++) // Prochází všechny detekované zásahy
                    {
                        if (isAlt)
                        {
                            selectedMesh.UnSelectVertex(buffer[loop * 4 + 3]);
                        }
                        else
                        {
                            selectedMesh.SelectVertex(buffer[loop * 4 + 3]);
                        }
                    }
                }

                return;
            }

            int choose = buffer[3];     // Uloží jméno prvního objektu
            int depth = buffer[1];      // Uloží jeho hloubku

            if (hits > 1)
            {
                for (int loop = 1; loop < hits; loop++)
                {
                    if (buffer[loop * 4 + 1] < depth)
                    {
                        choose = buffer[loop * 4 + 3];
                        depth = buffer[loop * 4 + 1];
                    }
                }
            }
            SelectMesh((Mesh)meshes[choose]);
        }

        //pri vybrani objektu aktualizuje hodnoty ve formularich a aktivuje prislusna tlacitka
        private void SelectMesh(Mesh meshToSelect)
        {
            if (selectedMesh != null)
                selectedMesh.IsSelected = false;

            selectedMesh = meshToSelect;
            selectedMesh.IsSelected = true;

            if (editationForm != null)
                editationForm.Enable();

            if (houghForm != null)
                houghForm.Enable();

            if (octreeForm != null)
                octreeForm.Enable();

            if (semanticForm != null)
                semanticForm.Enable();

            if (surfaceForm != null)
                surfaceForm.Enable();

            toolBtnVertexSelection.Enabled = true;
            toolBtnCentreView.Enabled = true;
            menuItemCentreToMesh.Enabled = true;

            if (selectedMesh != null)
            {
                if (centreToMesh)
                {
                    float[] coords = selectedMesh.CentreCoords();
                    xPos = coords[0];
                    yPos = coords[1];
                    zPos = coords[2];
                }
            }

            UpdateValues();
        }

        //pri odznaceni objektu deaktivuje formulare a prislusna tlacitka
        private void UnSelectMesh()
        {
            selectedMesh.IsSelected = false;
            selectedMesh = null;

            if (editationForm != null)
                editationForm.Disable();

            if (houghForm != null)
                houghForm.Disable();

            if (octreeForm != null)
                octreeForm.Disable();

            if (semanticForm != null)
                semanticForm.Disable();

            if (surfaceForm != null)
                surfaceForm.Disable();

            toolBtnVertexSelection.Enabled = false;

            UpdateValues();
        }

        //fce pro aktualizaci hodnot ve formularich
        public void UpdateValues()
        {
            if (editationForm != null)
            {
                editationForm.UpdateValues();
            }

            if (houghForm != null)
            {
                houghForm.UpdateValues();
            }

            if (selectedMesh != null)
            {
                toolStripLabelVertices.Text = selectedMesh.Vertices.Count.ToString();
                toolStripLabelSegments.Text = selectedMesh.Segments.Count.ToString();
                toolStripLabelSurfaces.Text = selectedMesh.Surfaces.Count.ToString();
            }
            else
            {
                toolStripLabelVertices.Text = "0";
                toolStripLabelSegments.Text = "0";
                toolStripLabelSurfaces.Text = "0";
            }
        }

        //fce pro smazani oznacenych bodu nebo celeho objektu
        public void DeleteSelected()
        {
            Cursor.Current = Cursors.WaitCursor;

            SaveState();

            if (vertexSelection)
            {
                selectedMesh.DeleteSelected();
            }
            else
            {
                for (int i = meshes.Count - 1; i != -1; i--)
                {
                    Mesh mesh = (Mesh)meshes[i];
                    if (mesh.IsSelected)
                        UnSelectMesh();
                    meshes.RemoveAt(i);
                }
            }
            Cursor.Current = Cursors.Default;

            glControl.Invalidate();
            UpdateValues();
        }

        //fce, ktera zpracovava vsechno ovladani mysi
        private void glControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (selecting)
            {
                selectEnd.X = e.X;
                selectEnd.Y = e.Y;
                glControl.Invalidate();
                return;
            }

            int diffx = (int)(e.X - lastX);
            int diffy = (int)(e.Y - lastY);
            lastX = e.X;
            lastY = e.Y;

            if (Math.Abs(diffx) > 100 || Math.Abs(diffy) > 100)
                return;

            // ovladani kamery
            // Middle mouse button
            if (e.Button.Equals(MouseButtons.Middle) & isAlt)
            {
                xRot += (float)diffy;
                yRot += (float)diffx;
            }
            else if (e.Button.Equals(MouseButtons.Middle))
            {
                Cursor.Current = Cursors.SizeAll;

                if (diffy > 0)
                {
                    float xRotrad, yRotrad;
                    yRotrad = (yRot / 180 * 3.141592654f);
                    xRotrad = (xRot / 180 * 3.141592654f);
                    xPos += (float)(Math.Sin(yRotrad) * Math.Sin(xRotrad) * cRadius / 50);
                    zPos -= (float)(Math.Cos(yRotrad) * Math.Sin(xRotrad) * cRadius / 50);
                    yPos += (float)(Math.Cos(xRotrad) * cRadius / 50);
                }
                else if (diffy < 0)
                {
                    float xRotrad, yRotrad;
                    yRotrad = (yRot / 180 * 3.141592654f);
                    xRotrad = (xRot / 180 * 3.141592654f);
                    xPos -= (float)(Math.Sin(yRotrad) * Math.Sin(xRotrad) * cRadius / 50);
                    zPos += (float)(Math.Cos(yRotrad) * Math.Sin(xRotrad) * cRadius / 50);
                    yPos -= (float)(Math.Cos(xRotrad) * cRadius / 50);
                }

                if (diffx < 0)
                {
                    float yRotrad;
                    yRotrad = (yRot / 180 * 3.141592654f);
                    xPos += (float)(Math.Cos(yRotrad) * cRadius / 50);
                    zPos += (float)(Math.Sin(yRotrad) * cRadius / 50);
                }
                else if (diffx > 0)
                {
                    float yRotrad;
                    yRotrad = (yRot / 180 * 3.141592654f);
                    xPos -= (float)(Math.Cos(yRotrad) * cRadius / 50);
                    zPos -= (float)(Math.Sin(yRotrad) * cRadius / 50);
                }
            }

            glControl.Invalidate();
        }

        //fce zmacknuti tlacitka mysi znamena zacatek vybirani
        private void glControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button.Equals(MouseButtons.Left))
            {
                selectStart = new Point(e.X, e.Y);
                selectEnd = new Point(e.X, e.Y);
                selecting = true;

                if (!isControl & !isAlt)
                {
                    if (selectedMesh != null)
                    {
                        if (vertexSelection)
                        {
                            selectedMesh.UnSelectAll();
                            return;
                        }

                        if (selectionMode)
                        {
                            UnSelectMesh();
                        }
                    }
                }
            }
        }

        //fce uvolneni tlacitka mysi znamena konec vybirani
        private void glControl_MouseUp(object sender, MouseEventArgs e)
        {
            Point rectCentre;
            int rectWidth;
            int rectHeight;

            Cursor.Current = Cursors.Default;

            if (selecting)
            {
                calculateRectangle(selectStart, selectEnd, out rectCentre, out  rectWidth, out rectHeight);
                StartPicking(rectCentre.X, rectCentre.Y, rectWidth, rectHeight);
                RenderInPickingMode();
                StopPicking();
                glControl.Invalidate();
            }

            selecting = false;
        }

        //fce kolecka mysi pro zoom
        private void glControl_MouseWheel(Object sender, MouseEventArgs e)
        {
            int wheelDelta = 120;
            int wheelPos = 0;

            wheelPos += e.Delta;

            while (wheelPos >= wheelDelta)
            {
                cRadius -= cRadius * 0.2f;
                wheelPos -= wheelDelta;
            }

            while (wheelPos <= -120)
            {
                cRadius += cRadius * 0.2f;
                wheelPos += wheelDelta;
            }

            SetupViewport();
            glControl.Invalidate();
        }

        //fce pro vypocet "selection" obdelniku
        private void calculateRectangle(Point selectStart, Point selectEnd, out Point rectCentre, out int rectWidth, out int rectHeight)
        {
            rectCentre = new Point();

            rectWidth = selectEnd.X - selectStart.X;
            rectHeight = selectEnd.Y - selectStart.Y;
            rectCentre.X = selectStart.X + rectWidth / 2;
            rectCentre.Y = selectStart.Y + rectHeight / 2;
            rectWidth = Math.Abs(rectWidth);
            rectHeight = Math.Abs(rectHeight);

            if (rectWidth == 0)
                rectWidth = 5;

            if (rectHeight == 0)
                rectHeight = 5;
        }

        //fce pro vykresleni selection obdelniku
        private void DrawRectangle()
        {
            GL.Color3(0.5, 0.5, 0.5);
            GL.Translate(0.0f, 0.0f, -1.0f);
            Vector3 endPos = GetOglPos(selectEnd.X, selectEnd.Y);
            Vector3 startPos = GetOglPos(selectStart.X, selectStart.Y);

            GL.Begin(BeginMode.LineLoop);
            GL.Vertex3(startPos.X, startPos.Y, 0.0);
            GL.Vertex3(endPos.X, startPos.Y, 0.0);
            GL.Vertex3(endPos.X, endPos.Y, 0.0);
            GL.Vertex3(startPos.X, endPos.Y, 0.0);
            GL.End();
            GL.Translate(0.0f, 0.0f, 1.0f);

        }

        //fce pro otevreni souboru, nacita jednotlive radky, a vytvari body z nactenych souradnic a vytvori mesh (nebo vice meshu)
        private void openFileDialog_FileOk(object sender, CancelEventArgs e)
        {
            try
            {
                string fileName = openFileDialog.FileName;
                FileInfo file = new FileInfo(fileName);
                TextReader reader = file.OpenText();

                string line;
                int meshesCount = 0;

                meshesCount = int.Parse(reader.ReadLine());

                for (int i = 0; i < meshesCount; i++)
                {
                    int rows = int.Parse(reader.ReadLine());
                    int verticesCount = int.Parse(reader.ReadLine());

                    Mesh newMesh = new Mesh(rows);
                    meshes.Add(newMesh);

                    line = reader.ReadLine();
                    string[] position = line.Split(' ');

                    newMesh.PositionX = float.Parse(position[0]);
                    newMesh.PositionY = float.Parse(position[1]);
                    newMesh.PositionZ = float.Parse(position[2]);

                    for (int j = 0; j < verticesCount; j++)
                    {
                        line = reader.ReadLine();
                        string[] coordinate = line.Split(' ');

                        Vertex vertex = new Vertex(float.Parse(coordinate[0]), float.Parse(coordinate[1]), float.Parse(coordinate[2]), int.Parse(coordinate[3]), int.Parse(coordinate[4]));
                        newMesh.Vertices.Add(vertex);
                    }

                    int segmentsCount = int.Parse(reader.ReadLine());

                    for (int j = 0; j < segmentsCount; j++)
                    {
                        line = reader.ReadLine();
                        string[] dimensions = line.Split(' ');

                        Segment segment = new Segment(float.Parse(dimensions[0]), float.Parse(dimensions[1]), float.Parse(dimensions[2]), float.Parse(dimensions[3]), float.Parse(dimensions[4]), float.Parse(dimensions[5]));
                        newMesh.Segments.Add(segment);
                    }

                    newMesh.CalculateBoundingBox();
                    newMesh.SetColors(clearColor);
                }
                reader.Close();
                glControl.Invalidate();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Can´t open file!", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }

        //fce pro import souboru postupne nacita ulozene telegramy a zpracovava je
        private void importFileDialog_FileOk(object sender, CancelEventArgs e)
        {
            try
            {
                string fileName = importFileDialog.FileName;
                FileInfo file = new FileInfo(fileName);
                TextReader reader = file.OpenText();

                string line;
                string measuremetnMode;
                string data;
                int dataValuesCount;
                float verticalAngle;

                int meshesCount = int.Parse(reader.ReadLine());
                for (int i = 0; i < meshesCount; i++)
                {

                    int dataCount = int.Parse(reader.ReadLine());

                    Mesh newMesh = new Mesh(dataCount);

                    //nactu measurementMode
                    measuremetnMode = reader.ReadLine();

                    for (int j = 0; j < dataCount; j++)
                    {
                        //nactu #
                        line = reader.ReadLine();

                        //nactu uhel
                        verticalAngle = float.Parse(reader.ReadLine());

                        //nactu hlavicku
                        line = reader.ReadLine();

                        //nactu dataValuesCount
                        dataValuesCount = HexToInt(reader.ReadLine());

                        //nactu data
                        data = reader.ReadLine();

                        ArrayList tempVerticesList = ProcessVertices(data, dataValuesCount, measuremetnMode, verticalAngle, j);

                        foreach (Vertex vertex in tempVerticesList)
                        {
                           //vertex.Id = newMesh.Vertices.Count;
                           newMesh.Vertices.Add(vertex);
                        }

                        //nactu konec
                        line = reader.ReadLine();
                    }

                    newMesh.CalculateBoundingBox();
                    newMesh.SetColors(clearColor);
                    meshes.Add(newMesh);
                }
                reader.Close();

                glControl.Invalidate();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Can´t import file!", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }

        // prevod Hexoveho retezce na integer
        private int HexToInt(string hexString)
        {
            int number = Convert.ToInt32(hexString, 16);
            return number;
        }

        // Fce pro zpracovani jednotlivych bodu z namerenych dat. Nejprve zjisti vzdalenost bodu,
        // pokud je mimo max. hodnotu tak ho vypustime. Pote z aktualnich uhlu a vzdalenosti vypocteme souradnice x, y, z.
        public ArrayList ProcessVertices(string data, int dataValuesCount, string measurementMode, float verticalAngle, int row)
        {
            char hexChar1, hexChar2, hexChar3, hexChar4;
            int distance;
            double horizontalAngle;
            float x, y, z;
            ArrayList newVertices = new ArrayList();

            for (int i = 0; i < dataValuesCount; i++)
            {
                hexChar1 = data[i * 4];
                hexChar2 = data[i * 4 + 1];
                hexChar3 = data[i * 4 + 2];
                hexChar4 = data[i * 4 + 3];

                distance = HexToInt(hexChar3.ToString() + hexChar4.ToString() + hexChar1.ToString() + hexChar2.ToString());

                if (distance >= 8190)
                {
                    continue;
                }

                if (measurementMode == "mm")
                {
                    distance = distance / 10;
                }

                switch (dataValuesCount)
                {
                    case 101:
                        horizontalAngle = 40 + i;
                        break;

                    case 201:
                        horizontalAngle = 40 + i * 0.5;
                        break;

                    case 401:
                        horizontalAngle = 40 + i * 0.25;
                        break;

                    case 181:
                        horizontalAngle = i;
                        break;

                    case 361:
                        horizontalAngle = i * 0.5;
                        break;

                    default:
                        horizontalAngle = i * 0.5;
                        break;
                }

                y = (float)(Math.Sin(Functions.DegreesToRadians(verticalAngle)) * Math.Sin(Functions.DegreesToRadians((float)horizontalAngle)) * distance);
                x = (float)(Math.Cos(Functions.DegreesToRadians((float)horizontalAngle)) * distance);
                z = (float)-(Math.Sin(Functions.DegreesToRadians((float)horizontalAngle)) * Math.Cos(Functions.DegreesToRadians(verticalAngle)) * distance);

                int id = row * 1000 + i;
                Vertex vertex = new Vertex(x, y, z, row, id);
                newVertices.Add(vertex);
            }
            return newVertices;
        }

        //fce pro klavesove zkratky
        private void glControl_KeyDown(object sender, KeyEventArgs e)
        {
            isAlt = e.Alt;
            isControl = e.Control;

            if (e.KeyCode == Keys.Delete)
            {
                DeleteSelected();
                glControl.Invalidate();
            }

            //front
            if (e.KeyCode == Keys.F)
            {
                yRot = 0;
                xRot = 0;
                glControl.Invalidate();
            }

            //left
            if (e.KeyCode == Keys.L)
            {
                yRot = 90;
                xRot = 0;
                glControl.Invalidate();
            }

            //top
            if (e.KeyCode == Keys.T)
            {
                yRot = 0;
                xRot = 90;
                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.C)
            {
                if (selectedMesh != null)
                {
                    menuItemCentreToMesh.PerformClick();
                }
            }

            if (e.KeyCode == Keys.G)
            {
                if (showGrid)
                {
                    menuItemGrid.Checked = false;
                    showGrid = false;
                }
                else
                {
                    menuItemGrid.Checked = true;
                    showGrid = true;
                }

                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.A)
            {
                if (showWorldAxes)
                {
                    menuItemAxes.Checked = false;
                    showWorldAxes = false;
                }
                else
                {
                    menuItemAxes.Checked = true;
                    showWorldAxes = true;
                }

                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.B)
            {
                if (showBoundingBoxes)
                {
                    menuItemBoundingBoxes.Checked = false;
                    showBoundingBoxes = false;
                }
                else
                {
                    menuItemBoundingBoxes.Checked = true;
                    showBoundingBoxes = true;
                }

                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.P)
            {
                menuItemPerspective.PerformClick();

                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.O)
            {
                menuItemOrthogonal.PerformClick();

                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.S)
            {
                if (showLights)
                {
                    toolBtnLight.Checked = false;
                    showLights = false;
                }
                else
                {
                    toolBtnLight.Checked = true;
                    showLights = true;
                }

                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.I)
            {
                menuItemInvertColors.PerformClick();
            }

            if (e.KeyCode == Keys.D1)
            {
                toolBtnShowVerts.PerformClick();
                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.D2)
            {
                toolBtnShowLines.PerformClick();
                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.D3)
            {
                toolBtnShowSurfaces.PerformClick();
                glControl.Invalidate();
            }

            if (e.KeyCode == Keys.D4)
            {
                toolBtnShowSegments.PerformClick();
                glControl.Invalidate();
            }

        }

        private void glControl_KeyUp(object sender, KeyEventArgs e)
        {
            isAlt = e.Alt;
            isControl = e.Control;
        }

        //ulozeni aktualniho stavu pro undo a redo
        public void SaveState()
        {
            menuItemUndo.Enabled = true;
            menuItemRedo.Enabled = false;
            undoMeshes.Clear();

            foreach (Mesh mesh in meshes)
            {
                Mesh newMesh = new Mesh();

                newMesh.IsSelected = mesh.IsSelected;
                newMesh.Rows = mesh.Rows;

                newMesh.PreviousRotationX = mesh.CurrentRotationX;
                newMesh.PreviousRotationY = mesh.CurrentRotationY;
                newMesh.PreviousRotationZ = mesh.CurrentRotationZ;

                newMesh.CurrentRotationX = newMesh.PreviousRotationX;
                newMesh.CurrentRotationY = newMesh.PreviousRotationY;
                newMesh.CurrentRotationZ = newMesh.PreviousRotationZ;

                newMesh.PositionX = mesh.PositionX;
                newMesh.PositionY = mesh.PositionY;
                newMesh.PositionZ = mesh.PositionZ;

                foreach (Vertex vertex in mesh.Vertices)
                {
                    Vertex newVertex = new Vertex(vertex.X, vertex.Y, vertex.Z, vertex.Row, vertex.Id);

                    newMesh.Vertices.Add(newVertex);
                }

                foreach (Vertex vertex in mesh.HoughVertices)
                {
                    Vertex newHoughVertex = new Vertex(vertex.X, vertex.Y, vertex.Z, vertex.Row, vertex.Id);

                    newMesh.HoughVertices.Add(newHoughVertex);
                }

                foreach (Line line in mesh.Lines)
                {
                    Line newLine = new Line();

                    Vertex newStartPoint = new Vertex(line.StartPoint.X, line.StartPoint.Y, line.StartPoint.Z, line.StartPoint.Row, line.StartPoint.Id);
                    Vertex newEndPoint = new Vertex(line.EndPoint.X, line.EndPoint.Y, line.EndPoint.Z, line.EndPoint.Row, line.EndPoint.Id);

                    newLine.StartPoint = newStartPoint;
                    newLine.EndPoint = newEndPoint;
                    newLine.PointsCount = line.PointsCount;
                    newLine.Theta = line.Theta;
                    newLine.Radius = line.Radius;
                    newLine.Row = line.Row;

                    newMesh.Lines.Add(newLine);
                }

                foreach (Surface surface in mesh.Surfaces)
                {
                    Surface newSurface = new Surface();
                    newSurface.Normal = surface.Normal;
                    foreach (Line line in surface.Lines)
                    {
                        newSurface.Lines.Add(line);
                    }
                    newMesh.Surfaces.Add(newSurface);
                }

                foreach (Segment segment in mesh.Segments)
                {
                    Segment newSegment = new Segment(segment.MaxX, segment.MinX, segment.MaxY, segment.MinY, segment.MaxZ, segment.MinZ);
                    newSegment.Semantic = segment.Semantic;
                    newMesh.Segments.Add(newSegment);
                }

                newMesh.CalculateBoundingBox();
                undoMeshes.Add(newMesh);
            }
        }

        //zacatek akce (segmentace, octree, hough...)
        public void StartAction()
        {
            SaveState();
            ProgressBar.Style = ProgressBarStyle.Marquee;

            glControl.Enabled = false;
            stopWatch.Reset();
            stopWatch.Start();
        }

        //konec akce
        public void StopAction()
        {
            stopWatch.Stop();
            double seconds = stopWatch.Elapsed.TotalSeconds;
            labelTime.Text = "Elapsed time: " + seconds.ToString() + " s";

            glControl.Enabled = true;
            ProgressBar.Style = ProgressBarStyle.Blocks;
            UpdateValues();
        }

        #region Menu

        //ulozeni souboru
        private void menuItemSave_Click(object sender, EventArgs e)
        {
            if (meshes.Count > 0)
            {
                ArrayList fileLines = new ArrayList();

                string saved_file = "";
                //string lines = "";
                string line = "";

                line = meshes.Count.ToString();
                fileLines.Add(line);

                foreach (Mesh mesh in meshes)
                {
                    line = mesh.Rows.ToString();
                    fileLines.Add(line);

                    line = mesh.Vertices.Count.ToString();
                    fileLines.Add(line);

                    line = mesh.PositionX + " " + mesh.PositionY + " " + mesh.PositionZ;
                    fileLines.Add(line);

                    foreach (Vertex vertex in mesh.Vertices)
                    {
                        line = vertex.X + " " + vertex.Y + " " + vertex.Z + " " + vertex.Row + " " + vertex.Id;// "\r\n";
                        fileLines.Add(line);
                    }

                    line = mesh.Segments.Count.ToString();
                    fileLines.Add(line);

                    foreach (Segment segment in mesh.Segments)
                    {
                        line = segment.MaxX + " " + segment.MinX + " " + segment.MaxY + " " + segment.MinY + " " + segment.MaxZ + " " + segment.MinZ;// "\r\n";
                        fileLines.Add(line);
                    }
                }

                saveFileDialog.InitialDirectory = "C:";
                saveFileDialog.Title = "Save data";
                saveFileDialog.FileName = "";

                saveFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";

                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    saved_file = saveFileDialog.FileName;
                    System.IO.StreamWriter file = new System.IO.StreamWriter(saved_file);

                    foreach (string fileLine in fileLines)
                    {
                        file.WriteLine(fileLine);
                    }

                    file.Close();
                }
            }
        }

        private void menuItemOpen_Click(object sender, EventArgs e)
        {
            openFileDialog.ShowDialog();
        }

        private void menuItemImport_Click(object sender, EventArgs e)
        {
            importFileDialog.ShowDialog();
        }

        private void menuItemNew_Click(object sender, EventArgs e)
        {
            Reset();
            glControl.Invalidate();
        }

        //undo
        private void menuItemUndo_Click(object sender, EventArgs e)
        {
            menuItemUndo.Enabled = false;
            menuItemRedo.Enabled = true;

            redoMeshes = (ArrayList)meshes.Clone();
            meshes = (ArrayList)undoMeshes.Clone();
            foreach (Mesh mesh in meshes)
            {
                if (mesh.IsSelected)
                {
                    selectedMesh = mesh;
                    break;
                }
            }
            glControl.Invalidate();

            UpdateValues();
        }

        //redo
        private void menuItemRedo_Click(object sender, EventArgs e)
        {
            menuItemUndo.Enabled = true;
            menuItemRedo.Enabled = false;

            undoMeshes = (ArrayList)meshes.Clone();
            meshes = (ArrayList)redoMeshes.Clone();
            foreach (Mesh mesh in meshes)
            {
                if (mesh.IsSelected)
                {
                    selectedMesh = mesh;
                    break;
                }
            }
            glControl.Invalidate();

            UpdateValues();
        }

        private void menuItemPerspective_Click(object sender, EventArgs e)
        {
            cameraType = "perspective";
            SetupViewport();
            glControl.Invalidate();
        }

        private void menuItemOrthogonal_Click(object sender, EventArgs e)
        {
            cameraType = "ortho";
            SetupViewport();
            glControl.Invalidate();
        }

        private void menuItemTopView_Click(object sender, EventArgs e)
        {
            yRot = 0;
            xRot = 90;
            glControl.Invalidate();
        }

        private void menuItemFrontView_Click(object sender, EventArgs e)
        {
            yRot = 0;
            xRot = 0;
            glControl.Invalidate();
        }

        private void menuItemLeftView_Click(object sender, EventArgs e)
        {
            yRot = 90;
            xRot = 0;
            glControl.Invalidate();
        }

        private void menuItemGrid_Click(object sender, EventArgs e)
        {
            if (menuItemGrid.Checked)
            {
                showGrid = true;
            }
            else
            {
                showGrid = false;
            }

            glControl.Invalidate();
        }

        private void menuItemAxes_Click(object sender, EventArgs e)
        {
            if (menuItemAxes.Checked)
            {
                showWorldAxes = true;
            }
            else
            {
                showWorldAxes = false;
            }

            glControl.Invalidate();
        }

        private void menuItemBoundingBoxes_Click(object sender, EventArgs e)
        {

            if (menuItemBoundingBoxes.Checked)
            {
                showBoundingBoxes = true;
            }
            else
            {
                showBoundingBoxes = false;
            }

            glControl.Invalidate();
        }

        private void menuItemInvertColors_Click(object sender, EventArgs e)
        {
            if (!invertedColors)
            {
                invertedColors = true;

                clearColor = Color.Black;

                foreach (Mesh mesh in meshes)
                {
                    mesh.SetColors(clearColor);
                }
            }
            else
            {
                invertedColors = false;

                clearColor = Color.White;

                foreach (Mesh mesh in meshes)
                {
                    mesh.SetColors(clearColor);
                }
            }

            SetupViewport();
            ReDraw();
        }

        private void menuItemLights_Click(object sender, EventArgs e)
        {
            if (showLights)
            {
                showLights = false;

                menuItemLights.Checked = false;
                toolBtnLight.Checked = false;

                GL.Disable(EnableCap.Lighting);
                GL.Disable(EnableCap.Light0);
                glControl.Invalidate();
            }
            else
            {
                showLights = true;

                menuItemLights.Checked = true;
                toolBtnLight.Checked = true;

                glControl.Invalidate();
            }
        }

        //otvirani formularu
        private void menuItemEditation_Click(object sender, EventArgs e)
        {
            if (editationForm == null)
            {
                editationForm = new FormEditation();
                editationForm.mainForm = this;
                editationForm.Left = this.Left + 5;
                editationForm.Top = this.Top + 85;
                editationForm.Show();

                editationForm.Owner = this;
                if (selectedMesh != null)
                {
                    editationForm.Enable();
                    editationForm.UpdateValues();
                }
            }
            else
            {
                editationForm.BringToFront();
            }
        }

        private void menuItemSurface_Click(object sender, EventArgs e)
        {
            if (surfaceForm == null)
            {
                surfaceForm = new FormSurface();
                surfaceForm.mainForm = this;
                surfaceForm.Left = this.Left + 5;
                surfaceForm.Top = this.Top + 85;
                surfaceForm.Show();

                surfaceForm.Owner = this;
                if (selectedMesh != null)
                {
                    surfaceForm.Enable();
                }
            }
            else
            {
                surfaceForm.BringToFront();
            }
        }

        private void menuItemOctree_Click(object sender, EventArgs e)
        {
            if (octreeForm == null)
            {
                octreeForm = new FormOctree();
                octreeForm.mainForm = this;
                octreeForm.Left = this.Left + 5;
                octreeForm.Top = this.Top + 85;
                octreeForm.Show();

                octreeForm.Owner = this;
                if (selectedMesh != null)
                {
                    octreeForm.Enable();
                }
            }
            else
            {
                octreeForm.BringToFront();
            }
        }

        private void menuItemHough_Click(object sender, EventArgs e)
        {
            if (houghForm == null)
            {
                houghForm = new FormHough();
                houghForm.mainForm = this;
                houghForm.Left = this.Left + 5;
                houghForm.Top = this.Top + 85;
                houghForm.Show();

                houghForm.Owner = this;
                if (selectedMesh != null)
                {
                    houghForm.Enable();
                    houghForm.UpdateValues();
                }
            }
            else
            {
                houghForm.BringToFront();
            }
        }

        private void menuItemSemantic_Click(object sender, EventArgs e)
        {
            if (semanticForm == null)
            {
                semanticForm = new FormSemantic();
                semanticForm.mainForm = this;
                semanticForm.Left = this.Left + 5;
                semanticForm.Top = this.Top + 85;
                semanticForm.Show();

                semanticForm.Owner = this;
                if (selectedMesh != null)
                {
                    semanticForm.Enable();
                }
            }
            else
            {
                semanticForm.BringToFront();
            }
        }

        private void menuItemCentreToMesh_Click(object sender, EventArgs e)
        {
            if (centreToMesh)
            {
                centreToMesh = false;
                menuItemCentreToMesh.Checked = false;
                toolBtnCentreView.Checked = false;
            }
            else
            {
                centreToMesh = true;
                menuItemCentreToMesh.Checked = true;
                toolBtnCentreView.Checked = true;
            }
        }
        #endregion Menu

        #region Tool Buttons - tlacitka na panelu
        //panel nastroju, bud se odkazuje na polozky menu, nebo provadi jednoduch zmeny bool

        private void toolBtnEditation_Click(object sender, EventArgs e)
        {
            menuItemEditation.PerformClick();
        }

        private void toolBtnOctree_Click(object sender, EventArgs e)
        {
            menuItemOctree.PerformClick();
        }

        private void toolBtnHough_Click(object sender, EventArgs e)
        {
            menuItemHough.PerformClick();
        }

        private void toolBtnSemantics_Click(object sender, EventArgs e)
        {
            menuItemSemantic.PerformClick();
        }

        private void toolBtnSurface_Click(object sender, EventArgs e)
        {
            menuItemSurface.PerformClick();
        }

        private void toolBtnSelection_CheckedChanged(object sender, EventArgs e)
        {
            if (toolBtnSelection.Checked)
            {
                toolBtnVertexSelection.Checked = false;

                selectionMode = true;
                vertexSelection = false;
                glControl.Invalidate();

                toolBtnSelection.CheckOnClick = false;
                toolBtnVertexSelection.CheckOnClick = true;
            }
        }

        private void toolBtnVertexSelection_CheckedChanged(object sender, EventArgs e)
        {
            if (toolBtnVertexSelection.Checked)
            {
                toolBtnSelection.Checked = false;

                selectionMode = false;
                vertexSelection = true;
                glControl.Invalidate();

                toolBtnSelection.CheckOnClick = true;
                toolBtnVertexSelection.CheckOnClick = false;
            }
            else
            {
                if (selectedMesh != null)
                {
                    selectedMesh.UnSelectAll();
                }
            }
        }

        private void toolBtnShowVerts_CheckedChanged(object sender, EventArgs e)
        {
            if (toolBtnShowVerts.Checked)
            {
                showVerts = true;
                glControl.Invalidate();
            }
            else
            {
                showVerts = false;
                glControl.Invalidate();
            }
        }

        private void toolBtnShowLines_CheckedChanged(object sender, EventArgs e)
        {
            if (toolBtnShowLines.Checked)
            {
                showLines = true;
                glControl.Invalidate();
            }
            else
            {
                showLines = false;
                glControl.Invalidate();
            }
        }

        private void toolBtnShowSurfaces_CheckedChanged(object sender, EventArgs e)
        {
            if (toolBtnShowSurfaces.Checked)
            {
                showSurfaces = true;
                glControl.Invalidate();
            }
            else
            {
                showSurfaces = false;
                glControl.Invalidate();
            }
        }

        private void toolBtnShowSegments_CheckStateChanged(object sender, EventArgs e)
        {
            if (toolBtnShowSegments.Checked)
            {
                showSegments = true;
                glControl.Invalidate();
            }
            else
            {
                showSegments = false;
                glControl.Invalidate();
            }
        }

        private void toolBtnResetView_Click(object sender, EventArgs e)
        {
            xPos = 0.0f;
            yPos = 0.0f;
            zPos = 0.0f;
            xRot = 35.0f;
            yRot = -45.0f;
            cRadius = 4000.0f;
            ReDraw();
        }

        private void toolBtnCentreView_Click(object sender, EventArgs e)
        {
            menuItemCentreToMesh.PerformClick();
            ReDraw();
        }

        private void toolBtnTopView_Click(object sender, EventArgs e)
        {
            menuItemTopView.PerformClick();
        }

        private void toolBtnLeftView_Click(object sender, EventArgs e)
        {
            menuItemLeftView.PerformClick();
        }

        private void toolBtnFrontView_Click(object sender, EventArgs e)
        {
            menuItemFrontView.PerformClick();
        }

        private void toolBtnInvert_Click(object sender, EventArgs e)
        {
            menuItemInvertColors.PerformClick();
        }

        private void toolBtnLight_Click(object sender, EventArgs e)
        {
            menuItemLights.PerformClick();
        }

        #endregion Tool Buttons
    }
}