/**
  * Logiciel de dessin bitmap.
  *
  * @author Nicolas JANEY, Universite de Franche-Comte, France, <a href="mailto:nicolas.janey@univ-fcomte.fr">nicolas.janey@univ-fcomte.fr</a>
  */

public class DessinBitmap {

  /**
    * Type structure de description d'une couleur
    */
  public static class Couleur {
    /**
     * Valeur de rouge.
     */
    public int r = 0;
    /**
     * Valeur de vert.
     */
    public int v = 0;
    /**
     * Valeur de bleu.
     */
    public int b = 0;
  }

  /**
    * Type structure de description d'une position 2D a coordonnees reelles
    */
  public static class Position2D {
    /**
     * Abscisse.
     */
    public double x = 0.0;
    /**
     * Ordonnee.
     */
    public double y = 0.0;
  }

  /**
    * Type structure de description d'un pixel
    */
  public static class Pixel {
    /**
     * Position.
     */
    public Position2D pos = new Position2D();
  }

  /**
    * Type structure de description d'un segment en 2D
    */
  public static class Segment2D {
    /**
     * Sommet initial.
     */
    public Position2D si = new Position2D();
    /**
     * Sommet final.
     */
    public Position2D sf = new Position2D();
  }

  /**
    * Type structure de description d'un rectangle en 2D
    */
  public static class Rectangle2D {
    /**
     * Position du centre.
     */
    public Position2D c = new Position2D();
    /**
     * Taille en x.
     */
    public double tx = 1.0;
    /**
     * Taille en y.
     */
    public double ty = 1.0;
    /**
     * Rotation.
     */
    public double rot = 0.0;
  }

  /**
    * Type structure de description d'un cercle en 2D
    */
  public static class Cercle2D {
    /**
     * Position du centre.
     */
    public Position2D c = new Position2D();
    /**
     * Rayon.
     */
    public double r = 1.0;
  }

  /**
    * Type structure de description d'une ellipse en 2D
    */
  public static class Ellipse2D {
    /**
     * Position du centre.
     */
    public Position2D c = new Position2D();
    /**
     * Demi grand axe en x.
     */
    public double a = 1.0;
    /**
     * Demi grand axe en y.
     */
    public double b = 1.0;
    /**
     * Rotation.
     */
    public double rot = 0.0;
  }

  /**
    * Type structure de description d'un polygone regulier en 2D
    */
  public static class PolygoneRegulier2D {
    /**
     * Position du centre.
     */
    public Position2D c = new Position2D();
    /**
     * Nombre de sommets.
     */
    public int n = 3;
    /**
     * Rotation.
     */
    public double r = 1.0;
    /**
     * Rotation.
     */
    public double rot = 0.0;
  }

  /**
    * Saisie clavier d'une couleur.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param c La couleur saisie.
    */
  public static void saisie(String message,Couleur c) {
    Ecran.afficherln(message);
    Ecran.afficher("Rouge : ");
    c.r = Clavier.saisirInt();
    Ecran.afficher("Vert  : ");
    c.v = Clavier.saisirInt();
    Ecran.afficher("Bleu  : ");
    c.b = Clavier.saisirInt();
  }

  /**
    * Saisie clavier d'une position en 2D.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param p La position saisie.
    */
  public static void saisie(String message,Position2D p) {
    Ecran.afficherln(message);
    Ecran.afficher("x : ");
    p.x = Clavier.saisirInt();
    Ecran.afficher("y : ");
    p.y = Clavier.saisirInt();
  }

  /**
    * Saisie clavier d'un pixel.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param p Le pixel saisi.
    */
  public static void saisie(String message,Pixel p) {
    Ecran.afficherln(message);
    saisie("Position",p.pos);
  }

  /**
    * Saisie clavier d'un segment 2D.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param s Le segment 2D saisi.
    */
  public static void saisie(String message,Segment2D s) {
    Ecran.afficherln(message);
    saisie("Sommet initial",s.si);
    saisie("Sommet final",s.sf);
  }

  /**
    * Saisie clavier d'un rectangle 2D.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param r Le rectangle 2D saisi.
    */
  public static void saisie(String message,Rectangle2D r) {
    Ecran.afficherln(message);
    saisie("Centre",r.c);
    Ecran.afficher("Largeur  : ");
    r.tx = Clavier.saisirInt();
    Ecran.afficher("Hauteur  : ");
    r.ty = Clavier.saisirInt();
    Ecran.afficher("Rotation : ");
    r.rot = Clavier.saisirDouble();
  }

  /**
    * Saisie clavier d'un cercle 2D.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param c Le cercle 2D saisi.
    */
  public static void saisie(String message,Cercle2D c) {
    Ecran.afficherln(message);
    saisie("Centre",c.c);
    Ecran.afficher("Rayon : ");
    c.r = Clavier.saisirDouble();
  }

  /**
    * Saisie clavier d'une ellipse 2D.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param e L'ellipse 2D saisie.
    */
  public static void saisie(String message,Ellipse2D e) {
    Ecran.afficherln(message);
    saisie("Centre",e.c);
    Ecran.afficher("Largeur  : ");
    e.a = Clavier.saisirInt();
    Ecran.afficher("Hauteur  : ");
    e.b = Clavier.saisirInt();
    Ecran.afficher("Rotation : ");
    e.rot = Clavier.saisirDouble();
  }

  /**
    * Saisie clavier d'un polygone regulier 2D.
    *
    * @param message Le message d'accompagnement a la saisie.
    * @param p Le polygone regulier 2D saisi.
    */
  public static void saisie(String message,PolygoneRegulier2D p) {
    Ecran.afficherln(message);
    saisie("Centre",p.c);
    Ecran.afficher("Rayon          : ");
    p.r = Clavier.saisirDouble();
    Ecran.afficher("Nombre sommets : ");
    p.n = Clavier.saisirInt();
    Ecran.afficher("Rotation       : ");
    p.rot = Clavier.saisirDouble();
  }

  /**
    * Calcul de la position obtenue par rotation d'une position autour d'une position.
    *
    * @param c Le centre de rotation.
    * @param x L'abscisse de la position a traiter par rapport au centre de rotation.
    * @param y L'ordonnee de la position a traiter par rapport au centre de rotation.
    * @param a L'angle de rotation (en radians).
    * @return La Position2D calculee.
    */
  public static Position2D positionAvecRotation(Position2D c,double x,double y,double a) {
    Position2D p = new Position2D();
    p.x = c.x + x*Math.cos(a)-y*Math.sin(a);
    p.y = c.y + x*Math.sin(a)+y*Math.cos(a);
    return p;
  }

  /**
    * Calcul de la suite de sommets d'un rectangle 2D.
    *
    * @param r Le rectangle 2D.
    * @return Le tableau de Position2D calcule.
    */
  public static Position2D [] sommets(Rectangle2D r) {
    Position2D [] t = new Position2D[4];
    t[0] = positionAvecRotation(r.c,r.tx/2.0,r.ty/2.0,r.rot);
    t[1] = positionAvecRotation(r.c,-r.tx/2.0,r.ty/2.0,r.rot);
    t[2] = positionAvecRotation(r.c,-r.tx/2.0,-r.ty/2.0,r.rot);
    t[3] = positionAvecRotation(r.c,r.tx/2.0,-r.ty/2.0,r.rot);
    return t;
  }

  /**
    * Calcul de la suite de sommets d'un cercle 2D.
    *
    * @param c Le cercle 2D.
    * @param n Le nombre de sommets a calculer.
    * @return Le tableau de Position2D calcule.
    */
  public static Position2D [] sommets(Cercle2D c,int n) {
    Position2D [] t = new Position2D[n];
    for ( int i = 0 ; i < n ; i++ ) {
      double a = i*2.0*Math.PI/n;
      t[i] = new Position2D();
      t[i].x = c.c.x+c.r*Math.cos(a);
      t[i].y = c.c.y+c.r*Math.sin(a); }
    return t;
  }

  /**
    * Calcul de la suite de sommets d'une ellipse 2D.
    *
    * @param e L'ellipse 2D.
    * @param n Le nombre de sommets a calculer.
    * @return Le tableau de Position2D calcule.
    */
  public static Position2D [] sommets(Ellipse2D e,int n) {
    Position2D [] t = new Position2D[n];
    for ( int i = 0 ; i < n ; i++ ) {
      double a = i*2.0*Math.PI/n;
      t[i] = positionAvecRotation(e.c,e.a*Math.cos(a),e.b*Math.sin(a),e.rot); }
    return t;
  }

  /**
    * Calcul de la suite de sommets d'un polygone regulier 2D.
    *
    * @param p Le polygone regulier 2D.
    * @return Le tableau de Position2D calcule.
    */
  public static Position2D [] sommets(PolygoneRegulier2D p) {
    Position2D [] t = new Position2D[p.n];
    for ( int i = 0 ; i < p.n ; i++ ) {
      double a = i*2.0*Math.PI/p.n;
      t[i] = positionAvecRotation(p.c,p.r*Math.cos(a),p.r*Math.sin(a),p.rot); }
    return t;
  }

  /**
    * Affichage en boucle fermee de segements de droite d'un tableau de position 2D.
    *
    * @param t Le tableau de positions 2D a afficher graphiquement.
    */
  public static void affichageTableauSommets(Position2D [] t) {
    for ( int i = 0 ; i < t.length ; i++ ) {
      EcranGraphique.drawLine((int) t[i].x,(int) t[i].y,
                              (int) t[(i+1)%t.length].x,(int) t[(i+1)%t.length].y); }
  }

  /**
    * Affichage graphique d'un pixel.
    *
    * @param p Le pixel a afficher.
    * @param c La couleur d'affichage.
    */
  public static void affichageGraphique(Pixel p,Couleur c) {
    EcranGraphique.setColor(c.r,c.v,c.b);
    EcranGraphique.drawPixel((int) p.pos.x,(int) p.pos.y);
  }

  /**
    * Affichage graphique d'un segment 2D.
    *
    * @param s Le segment 2D a afficher.
    * @param c La couleur d'affichage.
    */
  public static void affichageGraphique(Segment2D s,Couleur c) {
    EcranGraphique.setColor(c.r,c.v,c.b);
    EcranGraphique.drawLine((int) s.si.x,(int) s.si.y,(int) s.sf.x,(int) s.sf.y);
  }

  /**
    * Affichage graphique d'un rectangle 2D.
    *
    * @param r Le rectangle 2D a afficher.
    * @param c La couleur d'affichage.
    */
  public static void affichageGraphique(Rectangle2D r,Couleur c) {
    EcranGraphique.setColor(c.r,c.v,c.b);
    affichageTableauSommets(sommets(r));
  }

  /**
    * Affichage graphique d'un cercle 2D.
    *
    * @param cl Le cercle 2D a afficher.
    * @param c La couleur d'affichage.
    */
  public static void affichageGraphique(Cercle2D cl,Couleur c) {
    EcranGraphique.setColor(c.r,c.v,c.b);
    affichageTableauSommets(sommets(cl,180));
  }

  /**
    * Affichage graphique d'une ellipse 2D.
    *
    * @param e L'ellipse 2D a afficher.
    * @param c La couleur d'affichage.
    */
  public static void affichageGraphique(Ellipse2D e,Couleur c) {
    EcranGraphique.setColor(c.r,c.v,c.b);
    affichageTableauSommets(sommets(e,180));
  }

  /**
    * Affichage graphique d'un polygone regulier 2D.
    *
    * @param p Le polygone regulier 2D a afficher.
    * @param c La couleur d'affichage.
    */
  public static void affichageGraphique(PolygoneRegulier2D p,Couleur c) {
    EcranGraphique.setColor(c.r,c.v,c.b);
    affichageTableauSommets(sommets(p));
  }

  /**
    * Affichage graphique d'une croix.
    *
    * @param x L'abscisse du centre de la croix.
    * @param y L'ordonnee du centre de la croix.
    */
  public static void croix(int x,int y,Cercle2D cl,Couleur c) {
    EcranGraphique.setColor(c.r,c.v,c.b);
    EcranGraphique.drawLine(x-10,y,x+10,y);
    EcranGraphique.drawLine(x,y-10,x,y+10);
    affichageGraphique(cl,c);
  }

  /**
    * Saisie d'un cercle 2D interactivement a la souris.
    *
    * @param cl Le cercle a saisir interactivement.
    */
  public static void saisieInteractiveSouris(Cercle2D cl) {
    while ( EcranGraphique.getMouseState() == 0 );
    EcranGraphique.setXorMode(true);
    int xOld = EcranGraphique.getMouseX();
    int yOld = EcranGraphique.getMouseY();
    Couleur blanc = new Couleur();
    blanc.r = 255;
    blanc.v = 255;
    blanc.b = 255;
    cl.c.x = xOld;
    cl.c.y = yOld;
    cl.r = 0.0;
    croix(xOld,yOld,cl,blanc);
    while ( EcranGraphique.getMouseState() == 1 ) {
      int x = EcranGraphique.getMouseX();
      int y = EcranGraphique.getMouseY();
      if ( ( x != xOld ) || ( y != yOld ) ) {
        croix(xOld,yOld,cl,blanc);
        xOld = x;
        yOld = y;
        cl.r = Math.sqrt(Math.pow(x-cl.c.x,2.0)+Math.pow(y-cl.c.y,2.0));
        croix(x,y,cl,blanc);
      EcranGraphique.flush(); } }
    croix(xOld,yOld,cl,blanc);
    EcranGraphique.flush();
    EcranGraphique.setXorMode(false);
  }

  /**
    * Gestion du menu principal de choix de l'action a realiser.
    *
    * @return L'entier saisi.
    */
  public static int resultatMenuPrincipal() {
    int v;
    Ecran.sautDeLigne();
    Ecran.afficherln(" 1: Choix de la couleur de trace");
    Ecran.afficherln(" 2: Afficher un pixel");
    Ecran.afficherln(" 3: Afficher un segment de droite");
    Ecran.afficherln(" 4: Afficher un rectangle");
    Ecran.afficherln(" 5: Afficher un cercle");
    Ecran.afficherln(" 6: Afficher une ellipse");
    Ecran.afficherln(" 7: Afficher un polygone regulier");
    Ecran.afficherln(" 8: Afficher n objets tires au hasard");
    Ecran.afficherln(" 9: Choisir la couleur d'effacement de la fenetre");
    Ecran.afficherln("10: Effacer la fenetre");
    Ecran.afficherln("11: Tracer dynamiquement un cercle");
    Ecran.afficherln(" 0: Quitter");
    Ecran.afficher("Que voulez vous faire ? ");
    v = Clavier.saisirInt();
    Ecran.sautDeLigne();
    return v;
  }

  /**
    * Programme principal.
    *
    * @param args Le tableau des parametres de lancement.
    */

  public static void main(String [] args) {
    int n;
    int tx;
    int ty;
    int choix;
    Pixel px = new Pixel();
    Segment2D sg = new Segment2D();
    Rectangle2D rt = new Rectangle2D();
    Cercle2D cl = new Cercle2D();
    Ellipse2D el = new Ellipse2D();
    PolygoneRegulier2D pr = new PolygoneRegulier2D();
  /* Couleurs de trace et d'effacement              */
    Couleur ct = new Couleur();
    ct.r = 0;
    ct.v = 0;
    ct.b = 0;
    Couleur ce = new Couleur();
    ce.r = 255;
    ce.v = 255;
    ce.b = 255;
  /* Initialisation de la fenetre d'affichage       */
    Ecran.afficherln("Taille de la fenetre");
    Ecran.afficher("Tx : ");
    tx = Clavier.saisirInt();
    Ecran.afficher("Ty : ");
    ty = Clavier.saisirInt();
    EcranGraphique.init(50,50,tx,ty,tx-40,ty-80,"Dessin bitmap");
    EcranGraphique.setClearColor(255,255,255);
    EcranGraphique.clear();
    EcranGraphique.flush();
    choix = resultatMenuPrincipal();
    while ( choix != 0 ) {
      switch (choix) {
        case 1 :
          saisie("Couleur de trace?",ct);
          break;
        case 2 :
          saisie("Votre pixel?",px);
          affichageGraphique(px,ct);
          EcranGraphique.flush();
          break;
        case 3 :
          saisie("Votre segment?",sg);
          affichageGraphique(sg,ct);
          EcranGraphique.flush();
          break;
        case 4 :
          saisie("Votre rectangle?",rt);
          affichageGraphique(rt,ct);
          EcranGraphique.flush();
          break;
        case 5 :
          saisie("Votre cercle?",cl);
          affichageGraphique(cl,ct);
          EcranGraphique.flush();
          break;
        case 6 :
          saisie("Votre ellipse?",el);
          affichageGraphique(el,ct);
          EcranGraphique.flush();
          break;
        case 7 :
          saisie("Votre polygone regulier?",pr);
          affichageGraphique(pr,ct);
          EcranGraphique.flush();
          break;
        case 8 :
          Ecran.afficher("Nombre d'objets : ");
          n = Clavier.saisirInt();
          for ( int i = 0 ; i < n ; i++ ) {
            int type =(int) (Math.random()*6.0);
            switch(type) {
              case 0 :
                px.pos.x = Math.random()*tx;
                px.pos.y = Math.random()*ty;
                affichageGraphique(px,ct);
                break;
              case 1 :
                sg.si.x = Math.random()*tx;
                sg.si.y = Math.random()*ty;
                sg.sf.x = Math.random()*tx;
                sg.sf.y = Math.random()*ty;
                affichageGraphique(sg,ct);
                break;
              case 2 :
                rt.c.x = Math.random()*tx;
                rt.c.y = Math.random()*ty;
                rt.tx = 3.0+Math.random()*500.0;
                rt.ty = 3.0+Math.random()*500.0;
                rt.rot = Math.random()*2.0*Math.PI;
                affichageGraphique(rt,ct);
                break;
              case 3 :
                cl.c.x = Math.random()*tx;
                cl.c.y = Math.random()*ty;
                cl.r = 3.0+Math.random()*200.0;
                affichageGraphique(cl,ct);
                break;
              case 4 :
                el.c.x = Math.random()*tx;
                el.c.y = Math.random()*ty;
                el.a = 3.0+Math.random()*200.0;
                el.b = 3.0+Math.random()*200.0;
                el.rot = Math.random()*2.0*Math.PI;
                affichageGraphique(el,ct);
                break;
              case 5 :
                pr.c.x = Math.random()*tx;
                pr.c.y = Math.random()*ty;
                pr.n = 3+(int) (Math.random()*10.0);
                pr.r = 3.0+Math.random()*200.0;
                pr.rot = Math.random()*2.0*Math.PI;
                affichageGraphique(pr,ct);
                break; } }
          EcranGraphique.flush();
          break;
        case 9 :
          saisie("Couleur d'effacement?",ce);
          break;
        case 10 :
          EcranGraphique.setClearColor(ce.r,ce.v,ce.b);
          EcranGraphique.clear();
          EcranGraphique.flush();
          break;
        case 11 :
          saisieInteractiveSouris(cl);
          affichageGraphique(cl,ct);
          EcranGraphique.flush();
          break; }
      choix = resultatMenuPrincipal(); }
    Ecran.afficherln("Return pour quitter");
    Clavier.saisirString();
    EcranGraphique.exit();
  }    
}