using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using CsGL.OpenGL;

namespace CSGBuilder {
    public partial class MyView : OpenGLControl {
        // Jelzi, hogy mit kell rajzolni
        // 0:PointsOnly, 1:Wireframe, 2:ColorFill, 3:FlatShaded, 4:GouraudShaded
        public int drawindex = -1;
        //public Point3D cam;

        // A kirajzolandó modell
        public BRep mesh;

        // Tengelyek
        public Point gXAxis, gYAxis, gZAxis;
        public Point lXAxis, lYAxis, lZAxis;
        public Point trAxis, gCenter, lCenter;
        // Irány
        public char dir;

        // Melyik tengelyt kell rajzolni?
        public bool gAxes = true;   // globális
        public bool lAxes = true;   // lokális
        public bool dAxes = false;  // irány

        // Az X, Y, Z címke mérete
        public double labelSize;

        // Eltolás
        public double dx, dy;

        public MyView() {
            InitializeComponent();
        }

        // Kirajzolja a testet
        public override void glDraw() {
            // Törlés
            GL.glClearColor((float)(this.BackColor.R / 255.0), (float)(this.BackColor.G / 255.0), (float)(this.BackColor.B / 255.0), (float)(this.BackColor.A / 255.0));
            GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
            //GL.gluLookAt(cam.X, cam.Y, cam.Z, gCenter.X, gCenter.Y, 0.0, gYAxis.X, gYAxis.Y, 0);
            GL.glPushMatrix();
            Color c;
            switch (drawindex) {
                case 1:  // Wireframe
                    GL.glBegin(GL.GL_LINES);
                    // Élek kirajzolása
                    foreach (Edge e in mesh.Edges) {
                        c = e.Color;
                        GL.glColor3ub(c.R, c.G, c.B);
                        GL.glVertex2d(mesh.V2D[e.Sp].X + this.Width / 2 + dx, mesh.V2D[e.Sp].Y + this.Height / 2 + dy);
                        GL.glVertex2d(mesh.V2D[e.Ep].X + this.Width / 2 + dx, mesh.V2D[e.Ep].Y + this.Height / 2 + dy);
                    }
                    GL.glEnd();                    
                    GL.glDisable(GL.GL_DEPTH_TEST);
                    if (dAxes) { DrawDirAxes(); break; }
                    if (gAxes) DrawGlobalAxes();
                    if (lAxes) DrawLocalAxes();
                    GL.glEnable(GL.GL_DEPTH_TEST);
                    break;
                case 2:  // Color fill                    
                    GL.glDisable(GL.GL_DEPTH_TEST);
                    for (int i = 0; i < mesh.VisibleFaces.Count; i++) {
                        // Lap kirajzolása
                        Face f = mesh.VisibleFaces[i];
                        GL.glBegin(GL.GL_TRIANGLES);
                        c = f.Color;
                        GL.glColor3ub(c.R, c.G, c.B);
                        GL.glVertex2d(mesh.V2D[f[0]].X + this.Width / 2 + dx, mesh.V2D[f[0]].Y + this.Height / 2 + dy);
                        GL.glVertex2d(mesh.V2D[f[1]].X + this.Width / 2 + dx, mesh.V2D[f[1]].Y + this.Height / 2 + dy);
                        GL.glVertex2d(mesh.V2D[f[2]].X + this.Width / 2 + dx, mesh.V2D[f[2]].Y + this.Height / 2 + dy);
                        GL.glEnd();
                        // Lapot határoló vonalak kirajzolása
                        GL.glBegin(GL.GL_LINE_LOOP);
                        c = mesh.Edges[0].Color;
                        GL.glColor3ub(c.R, c.G, c.B);
                        GL.glVertex2d(mesh.V2D[f[0]].X + this.Width / 2 + dx, mesh.V2D[f[0]].Y + this.Height / 2 + dy);
                        GL.glVertex2d(mesh.V2D[f[1]].X + this.Width / 2 + dx, mesh.V2D[f[1]].Y + this.Height / 2 + dy);
                        GL.glVertex2d(mesh.V2D[f[2]].X + this.Width / 2 + dx, mesh.V2D[f[2]].Y + this.Height / 2 + dy);
                        GL.glEnd();
                    }
                    if (dAxes) { DrawDirAxes(); break; }
                    if (gAxes) DrawGlobalAxes();
                    if (lAxes) DrawLocalAxes();
                    GL.glEnable(GL.GL_DEPTH_TEST);
                    break;
                case 3:  // Flat shaded
                    GL.glDisable(GL.GL_DEPTH_TEST);
                    for (int i = 0; i < mesh.VisibleFaces.Count; i++) {
                        Face f = mesh.VisibleFaces[i];
                        // Árnyalási szín beállítáa
                        c = f.Color;
                        GL.glColor3d(c.R / 255.0 * mesh.Lights[i], c.G / 255.0 * mesh.Lights[i], c.B / 255.0 * mesh.Lights[i]);
                        // Lap kirajzolása
                        GL.glBegin(GL.GL_TRIANGLES);
                        GL.glVertex2d(mesh.V2D[f[0]].X + this.Width / 2 + dx, mesh.V2D[f[0]].Y + this.Height / 2 + dy);
                        GL.glVertex2d(mesh.V2D[f[1]].X + this.Width / 2 + dx, mesh.V2D[f[1]].Y + this.Height / 2 + dy);
                        GL.glVertex2d(mesh.V2D[f[2]].X + this.Width / 2 + dx, mesh.V2D[f[2]].Y + this.Height / 2 + dy);
                        GL.glEnd();
                    }
                    if (dAxes) { DrawDirAxes(); break; }
                    if (gAxes) DrawGlobalAxes();
                    if (lAxes) DrawLocalAxes();
                    GL.glEnable(GL.GL_DEPTH_TEST);
                    break;
                case 4:  // Gouraud shaded
                    GL.glDisable(GL.GL_DEPTH_TEST);
                    GL.glBegin(GL.GL_TRIANGLES);
                    for (int i = 0; i < mesh.VisibleFaces.Count; i++) {
                        Face f = mesh.VisibleFaces[i];
                        // Lap kirajolása a csúcspontbeli színeket interpolálva
                        c = f.Color;
                        double r = c.R / 255.0, g = c.G / 255.0, b = c.B / 255.0;
                        GL.glColor3d(r * mesh.Lights[f[0]], g * mesh.Lights[f[0]], b * mesh.Lights[f[0]]);
                        GL.glVertex2d(mesh.V2D[f[0]].X + this.Width / 2 + dx, mesh.V2D[f[0]].Y + this.Height / 2 + dy);
                        GL.glColor3d(r * mesh.Lights[f[1]], g * mesh.Lights[f[1]], b * mesh.Lights[f[1]]);
                        GL.glVertex2d(mesh.V2D[f[1]].X + this.Width / 2 + dx, mesh.V2D[f[1]].Y + this.Height / 2 + dy);
                        GL.glColor3d(r * mesh.Lights[f[2]], g * mesh.Lights[f[2]], b * mesh.Lights[f[2]]);
                        GL.glVertex2d(mesh.V2D[f[2]].X + this.Width / 2 + dx, mesh.V2D[f[2]].Y + this.Height / 2 + dy);
                    }
                    GL.glEnd();
                    if (dAxes) { DrawDirAxes(); break; }
                    if (gAxes) DrawGlobalAxes();
                    if (lAxes) DrawLocalAxes();
                    GL.glEnable(GL.GL_DEPTH_TEST);
                    break;
                case 0: // Points only
                    GL.glPointSize(3.0f);
                    GL.glBegin(GL.GL_POINTS);
                    Point p;
                    for (int i = 0; i < mesh.V2D.Count; i++ ) {
                        p = mesh.V2D[i];
                        c = mesh.Vertices[i].Color;
                        GL.glColor3f(c.R, c.G, c.B);
                        GL.glVertex2d(p.X + this.Width / 2 + dx, p.Y + this.Height / 2 + dy);
                    }
                    GL.glEnd();
                    GL.glPointSize(1.0f);
                    GL.glDisable(GL.GL_DEPTH_TEST);
                    if (dAxes) { DrawDirAxes(); break; }
                    if (gAxes) DrawGlobalAxes();
                    if (lAxes) DrawLocalAxes();
                    GL.glEnable(GL.GL_DEPTH_TEST);
                    break;
            }
            GL.glPopMatrix();
            GL.glFlush();
            SwapBuffer();
        }
        
        // Kirajzolja a globális koordinátarendszert
        public void DrawGlobalAxes() {
            // tengelyek rajzolása
            GL.glColor3f(Color.Yellow.R, Color.Yellow.G, Color.Yellow.B);
            GL.glBegin(GL.GL_LINES);
            GL.glVertex2d(gCenter.X, gCenter.Y);
            GL.glVertex2d(gXAxis.X, gXAxis.Y);
            GL.glVertex2d(gCenter.X, gCenter.Y);
            GL.glVertex2d(gYAxis.X, gYAxis.Y);
            GL.glVertex2d(gCenter.X, gCenter.Y);
            GL.glVertex2d(gZAxis.X, gZAxis.Y);
            GL.glEnd();
            // címkék rajzolása
            // X rajzolás - két vonallal
            GL.glBegin(GL.GL_LINES);
            GL.glVertex2d(gXAxis.X - labelSize, gXAxis.Y - labelSize);
            GL.glVertex2d(gXAxis.X + labelSize, gXAxis.Y + labelSize);
            GL.glVertex2d(gXAxis.X - labelSize, gXAxis.Y + labelSize);
            GL.glVertex2d(gXAxis.X + labelSize, gXAxis.Y - labelSize);
            GL.glEnd();
            // Y rajzolás - három vonallal
            GL.glBegin(GL.GL_LINES);
            GL.glVertex2d(gYAxis.X - labelSize, gYAxis.Y + labelSize);
            GL.glVertex2d(gYAxis.X, gYAxis.Y);
            GL.glVertex2d(gYAxis.X + labelSize, gYAxis.Y + labelSize);
            GL.glVertex2d(gYAxis.X, gYAxis.Y);
            GL.glVertex2d(gYAxis.X, gYAxis.Y - labelSize);
            GL.glVertex2d(gYAxis.X, gYAxis.Y);
            GL.glEnd();
            // Z rajzolás - 3 vonallal
            GL.glBegin(GL.GL_LINES);
            GL.glVertex2d(gZAxis.X - labelSize, gZAxis.Y + labelSize);
            GL.glVertex2d(gZAxis.X + labelSize, gZAxis.Y + labelSize);
            GL.glVertex2d(gZAxis.X + labelSize, gZAxis.Y + labelSize);
            GL.glVertex2d(gZAxis.X - labelSize, gZAxis.Y - labelSize);
            GL.glVertex2d(gZAxis.X - labelSize, gZAxis.Y - labelSize);
            GL.glVertex2d(gZAxis.X + labelSize, gZAxis.Y - labelSize);
            GL.glEnd();
        }

        // Kirajzolja a lokális koordinátarendszert
        public void DrawLocalAxes() {
            GL.glBegin(GL.GL_LINES);
            GL.glColor3f(Color.Red.R, Color.Red.G, Color.Red.B);
            GL.glVertex2d(lCenter.X, lCenter.Y);
            GL.glVertex2d(lXAxis.X, lXAxis.Y);
            GL.glColor3f(Color.Green.R, Color.Green.G, Color.Green.B);
            GL.glVertex2d(lCenter.X, lCenter.Y);
            GL.glVertex2d(lYAxis.X, lYAxis.Y);
            GL.glColor3f(Color.Blue.R, Color.Blue.G, Color.Blue.B);
            GL.glVertex2d(lCenter.X, lCenter.Y);
            GL.glVertex2d(lZAxis.X, lZAxis.Y);
            GL.glEnd();
        }

        // Kirajzolja a transzformáció irányába mutató tengelyt
        public void DrawDirAxes() {
            switch (dir) {
                case 'X': GL.glColor3f(Color.Red.R, Color.Red.G, Color.Red.B); break;
                case 'Y': GL.glColor3f(Color.Green.R, Color.Green.G, Color.Green.B); break;
                case 'Z': GL.glColor3f(Color.Blue.R, Color.Blue.G, Color.Blue.B); break;
            }
            GL.glBegin(GL.GL_LINES);
            GL.glVertex2d(lCenter.X, lCenter.Y);
            GL.glVertex2d(trAxis.X, trAxis.Y);
            GL.glEnd();
        }

        // OpenGL inicializáció
        protected override void InitGLContext() {
            GL.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            GL.glShadeModel(GL.GL_SMOOTH);
            GL.glEnable(GL.GL_DEPTH_TEST);
            GL.glDepthFunc(GL.GL_LESS);
            GL.glLineWidth(2.0f);
            GL.glEnable(GL.GL_BLEND);
            GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
            GL.glEnable(GL.GL_POINT_SMOOTH);
            GL.glHint(GL.GL_POINT_SMOOTH_HINT, GL.GL_NICEST);
            GL.glEnable(GL.GL_LINE_SMOOTH);
            GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST);
            GL.glEnable(GL.GL_POLYGON_SMOOTH);
            GL.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST);
        }
      
        // Méretezéskor módosítja az ablakot
        protected override void OnSizeChanged(EventArgs e) {
            base.OnSizeChanged(e);
            GL.glMatrixMode(GL.GL_PROJECTION);
            GL.glLoadIdentity();
            GL.gluOrtho2D(0.0, Size.Width, 0.0, Size.Height);
            //GL.gluPerspective(60, 4.0 / 3.0, -100, 100);
        }

    }
}
