﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace TP8
{
    internal class ZoneDessin : Control
    {
        //Le modèle du MVC
        Modele modele;
        //La forme sélectionnée pour être déplacée
        FormeGeo forme_select = null;
        //Les précédentes coordonnées de la souris lors d'un déplacement
        int x_souris_pre, y_souris_pre;

        //Etat de l'interface (voir enum plus bas)
        public Etats etat;

        /* Points utiles quand on crée une nouvelle forme
         * p_deb : point d'origine : 
         *      - Pour le rectangle, c'est coin gauche haut
         *      - Pour le disque, c'est le centre
         * p_fin : point final :
         *      - Pour le rectange, c'est le coin droit bas
         *      - Pour le disque, c'est un point du périmètre 
         * p_tmp : point temporaire pour voir à quoi ressemblerait la forme
         * */
        Point p_deb;
        Point p_fin;
        Point p_tmp;

        //Indicateur signalant que l'utilisateur est en train de dessiner une nouvelle forme
        private bool isDrawing = false;

        public ZoneDessin()
        {
            this.DoubleBuffered = true;//Pour éviter les scintillements
            modele = new Modele();
            //On ajoute dans le modèle 3 formes pré-définis pour tester
            FRectangle rect1 = new FRectangle(40, 20, 50, 60);
            FRectangle rect2 = new FRectangle(100, 20, 40, 30);
            Disque disque1 = new Disque(100, 100, 25);
            modele.add(disque1);
            modele.add(rect1);
            modele.add(rect2);
            
        }
        protected override void OnPaintBackground(PaintEventArgs e)
        //Repeint le widget
        {
            //On commence par tout effacer
            e.Graphics.Clear(Color.White); 

            SolidBrush myBrush = new SolidBrush(Color.Black);

            //On affiche chacune des formes de la liste du modèle
            for (int i = 0; i < modele.getSize(); i++)
            {
                FormeGeo forme = modele.get(i);
                //La méthode de Graphics à invoquer dépend du type de forme
                if (forme.GetTypeForme() == Type_Forme.Rectangle)
                {
                    FRectangle rect = (FRectangle)forme;
                    e.Graphics.FillRectangle(myBrush, rect.getX(), rect.getY(), rect.getLargeur(), rect.getHauteur());
                }
                if (forme.GetTypeForme() == Type_Forme.Disque)
                {
                    Disque disque = (Disque)forme;
                    //Pour afficher un disque, il faut donner les coordonnées du point en haut à gauche
                    //et la taille du carré dans lequel s'inscrit le disque.
                    e.Graphics.FillEllipse(myBrush, disque.getX(), disque.getY(), 2 * disque.getRayon(), 2 * disque.getRayon());
                }
            }

            //Quand l'utilisateur dessine une nouvelle forme, on affiche une trace temporaire de la forme
            if (this.isDrawing)
            {
                Pen tmpPen = new Pen(Color.LightGreen);

                if (etat == Etats.CreerRectangle)
                {
                    e.Graphics.DrawRectangle(tmpPen, p_deb.X, p_deb.Y, p_tmp.X-p_deb.X, p_tmp.Y -p_deb.Y);
                }

                if (etat == Etats.CreerDisque)
                {
                    int rayon = (int)Math.Sqrt(Math.Pow(p_deb.X - p_tmp.X, 2) + Math.Pow(p_deb.Y - p_tmp.Y, 2));
                    e.Graphics.DrawEllipse(tmpPen, p_deb.X - rayon, p_deb.Y - rayon, 2 * rayon, 2 * rayon);
                }
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            //Quand l'utilisateur veut déplacer une forme
            if (etat == Etats.Deplacer)
            {
                /* On parcourt toutes les formes du modèle jusqu'à en trouver
                 * une qui contient le point où se trouve la souris.
                 * */
                int i = 0;
                bool trouve_forme = false;
                while (i < modele.getSize() && !trouve_forme)
                {
                    if (modele.get(i).estDedans(e.X, e.Y))
                    {
                        forme_select = modele.get(i);
                        trouve_forme = true;
                        x_souris_pre = e.X;
                        y_souris_pre = e.Y;
                    }
                    i++;
                }
            }

            //Quand l'utilisateur veut créer une nouvelle forme
            if (etat == Etats.CreerRectangle || etat == Etats.CreerDisque)
            {
                this.p_deb = new Point(e.Location.X, e.Location.Y);
                this.isDrawing = true;
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (etat == Etats.Deplacer)
            //Quand on relâche la souris, on arrête le déplacement de forme
            {
                forme_select = null;
            }

            if (etat == Etats.CreerRectangle)
                //On rajoute un rectangle dans la liste du modèle
            {
                this.p_fin = new Point(e.Location.X, e.Location.Y);
                FRectangle rect = new FRectangle(p_deb.X, p_deb.Y, p_fin.X - p_deb.X, p_fin.Y - p_deb.Y);
                this.modele.add(rect);
                this.isDrawing = false;
                this.Invalidate(); //on rafraichit la zone de dessin
            }

            if (etat == Etats.CreerDisque)
                //On rajoute un disque dans la liste du modèle
            {
                /* Le disque a pour centre le point d'origine et pour rayon la distance entre 
                 * le point d'origine (i.e. quand on a appuyé sur le bouton souris) 
                 * et le point final (i.e. quand on a relâché le bouton souris).
                 */
                this.p_fin = new Point(e.Location.X, e.Location.Y);
                int rayon = (int)Math.Sqrt( Math.Pow(p_deb.X-p_fin.X,2) + Math.Pow(p_deb.Y-p_fin.Y,2) );
                Disque disque = new Disque(p_deb.X-rayon, p_deb.Y-rayon,rayon);
                this.modele.add(disque);
                this.isDrawing = false;
                this.Invalidate(); //on rafraichit la zone de dessin
            }

        }

        /* Quand la souris bouge et qu'une forme est sélectionnée pour 
         * être déplacée, on calcule le delta de déplacement de la souris
         * et on déplace d'autant la forme
         */
        protected override void OnMouseMove(MouseEventArgs e)
        {
            //Quand l'utilisateur déplace une forme
            if (forme_select != null)
            {
                int dx = e.X - x_souris_pre;
                int dy = e.Y - y_souris_pre;
                forme_select.deplace(dx, dy);
                x_souris_pre = e.X;
                y_souris_pre = e.Y;
            }

            //Qaund l'utilisateur dessine une nouvelle forme
            if (this.isDrawing)
            {
                //On met à jour le point final de la trace temporaire
                this.p_tmp = new Point(e.Location.X, e.Location.Y); 
            }

            this.Invalidate(); //on rafraichit la zone de dessin
        }

    }

    /*Etats possibles de l'application décidés par l'utilisateur en cliquant
     * les boutons de l'interface.
     * Deplacer: l'utilisateur veut déplacer une forme
     * CreerRectangle: l'utilisateur veut dessiner un rectangle
     * CreerDisque: l'utilisateur veut dessiner un disque
     * */
    enum Etats { Deplacer, CreerRectangle, CreerDisque }
}
