/** @ (#) tm.java - 16/12/06 - 14/06/07
 *
 * @author Jean-Paul QUELEN
 * 
 * Trace la courbe de la probabilité qu'une personne ayant un test positif soit
 * malade en fonction de la fréquence de la maladie : en abscisse la fréquence,
 * en ordonnée la valeur diagnostique.
 * En paramètres :
 * - la sensibilité : probabilité d'avoir un test positif sachant qu'on est malade (variable pse)
 * - la spécificité : probabilité d'avoir un test négatif sachant qu'on n'est pas malade (variable psp)
 * modification et ajout de main() le 19/02/18
 */

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.util.*;

public class tm extends java.applet.Applet {
  static final long serialVersionUID = 180219L;
  Font f = new Font ("Arial", Font.PLAIN, 10);
  dessin d;
  double pse, psp, zoommax;
  double zoom = 10.0, arrondi = 1000.0;

 private double gparmd (String s, double d) {
    try {
      s = getParameter (s);
      d = Double.parseDouble (s);
    }
    catch (NumberFormatException e) {}
    catch (NullPointerException e) {}
    return d;
  }

  public void init ()
  { setFont (f);
  	setBackground (Color.lightGray);
    setLayout (new BorderLayout ());
    pse = psp= 0.99;
	zoommax = gparmd ("zoom", 99999.0);
    add (new controles (), BorderLayout.NORTH);
    add (d = new dessin (), BorderLayout.CENTER);
  }

  public String getAppletInfo ()
  { return "tm par j.-p. Quelen"; }
////////////////////////////////////////////////////////////////////////////////
protected class dessin extends Canvas implements MouseMotionListener {
  static final long serialVersionUID = 180219L;
  Image img;
  Graphics g;
  int w, h, xret, yret;
	
	public dessin ()
	{ addMouseMotionListener (this);
    setCursor (new Cursor (Cursor.CROSSHAIR_CURSOR));
  }

  public void update (Graphics g1)
  { paint (g1); }

  public void paint (Graphics g1)
  { 
// initialisation  : création d'un buffer pour l'image
  	int gw = getSize().width;
  	int gh = getSize().height;
  	if (img == null || (w != gw) || (h != gh))
    { w = gw;
      h = gh;
      img = createImage (w, h);
      g = img.getGraphics ();
    }

// efface l'image
    g.setColor (Color.white);
    g.fillRect (0, 0, w, h);

// axe et graduations
    int bord = 20;
    int hmb = h - bord;
    int wmb = w - bord;
    int hseg0 = 5;
    int esp = 15;
    int hseg1 = bord + hseg0;
    int wseg0 = hmb - hseg0;
    int wseg1 = hmb + hseg0;
    hseg0 = bord - hseg0;
    double wzero1 = (double)(wmb - bord) / 10.0;
    double hzero1 = (double)(hmb - bord) / 10.0;
    double dw = bord + wzero1;
    double dh = hmb - hzero1;
    g.setColor (Color.black);
    g.drawLine (bord, hmb, wmb, hmb); 
    g.drawLine (bord, bord, bord, hmb);
    for (int i = 1; i <= 10; i ++)
    { int idw = (int)dw;
    	g.drawLine (idw, wseg0, idw, wseg1);
    	g.drawString (new Double (i / zoom) . toString (), idw - 5, hmb + esp);
    	int idh = (int)dh;
     	g.drawLine (hseg0, idh, hseg1, idh);
    	g.drawString (new Double (i / 10.0) . toString (), bord - esp, idh);     	
     	dw += wzero1; dh -= hzero1;
    }

// Calcul et affichage de la courbe de la valeur prédictive positive
    g.setColor (Color.blue);
    double x = 0.0;
    int hmbb = hmb - bord;
    double incx = 10.0 / ((double)(wmb - bord) * zoom);
    int ax = bord;
    int ay = hmb;
    for (int i = bord; i <= wmb; i ++)
    { double psex = pse * x;
    	double y = psex / (psex + (1.0 - x) * (1 - psp));
    	int iy = hmb - (int)(y * (double)hmbb);
    	g.drawLine (ax, ay, i, iy);
    	ax = i;
    	ay = iy;
    	x += incx;
    }

// Calcul et affichage de la courbe de la valeur prédictive négative
    g.setColor (Color.green);
    x = 0.0;
    ax = bord;
    ay = bord;
    for (int i = bord; i <= wmb; i ++)
    { double pspx = psp * (1.0 - x);
    	double y = pspx / (pspx + x * (1.0 - pse));
    	int iy = hmb - (int)(y * (double)hmbb);
    	g.drawLine (ax, ay, i, iy);
    	ax = i;
    	ay = iy;
    	x += incx;
    }

//affichage coordonnées du réticule
    g.setColor (Color.black);
    g.drawString ("(" + sarrondi (incx * (xret - bord)) + "; " + sarrondi ((double)(hmb - yret) / (double)(hmbb)) + ")", 40, 20); 


// cadre
//    g.drawRect (0, 0, w - 1, h - 1);
    g1.drawImage (img, 0, 0, this);
  }

  private String sarrondi (double d)
  { return new Double (Math.floor (d * arrondi) / arrondi) . toString ();
  }

  public void mouseDragged (MouseEvent e)
  { e.consume ();
  	xret = e.getX ();
  	yret = e.getY ();
  	repaint ();
  }

  public void mouseMoved (MouseEvent e)
  { e.consume ();
  	xret = e.getX ();
  	yret = e.getY ();
  	repaint ();
  }

}
////////////////////////////////////////////////////////////////////////////////
protected class controles extends Panel implements ActionListener, AdjustmentListener {
  static final long serialVersionUID = 180219L;
  GridBagLayout gbl;
  GridBagConstraints gbc;
  Font f;
  TextField tpse, tpsp;
  Button ok, bzoom;
  Scrollbar scrse, scrsp;

  private Label ajoutl (String s)
  { Label lbl = new Label (s, Label.RIGHT);
    lbl.setBackground (Color.lightGray);
    lbl.setFont (f);
    gbl.setConstraints(lbl, gbc);
    return lbl;
  }

  private TextField ajouti (int n)
  { TextField tf = new TextField (Integer.toString (n), 10);
    tf.setFont (f);
    gbl.setConstraints (tf, gbc);
    return tf;
  }

  private TextField ajoutd (double d)
  { TextField tf = new TextField (new Double (d) . toString (), 10);
    tf.setFont (f);
    gbl.setConstraints (tf, gbc);
    return tf;
  }

  private Button ajoutb (String s)
  { Button bouton = new Button (s);
  	bouton.addActionListener (this);
    bouton.setFont (f);
    gbl.setConstraints (bouton, gbc);
    return bouton;
  }

	public controles ()
	{ f = new Font ("Arial", Font.PLAIN, 10);
		gbl = new GridBagLayout ();
    gbc = new GridBagConstraints ();
    setLayout (gbl);
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weightx = 1.0;
    gbc.weighty = 1.0;
    add (ajoutl ("Sensibilité = "));
    add (tpse = ajoutd (pse));
    add (ajoutl (" Spécificité = "));
    add (tpsp = ajoutd (psp));
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    add (ok = ajoutb ("ok"));
    gbc.gridwidth = 2;
    add (scrse = ajouts (pse));
    add (scrsp = ajouts (psp));
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    add (bzoom = ajoutb ("zoom"));
  }

  private Scrollbar ajouts (double d)
  { Scrollbar scr = new Scrollbar(Scrollbar.HORIZONTAL, (int)(d * 100.0), 10, 0, 110);
    scr.setBackground (Color.white);
    gbl.setConstraints(scr, gbc);
    scr.addAdjustmentListener (this);
    return scr;
  }

  public void adjustmentValueChanged (AdjustmentEvent evt)
  { if (evt.getSource () == scrse)
  	{ pse = (double)(scrse.getValue ()) * 0.01;
  		tpse.setText (new Double(pse).toString ());
    }
    else if (evt.getSource () == scrsp)
  	{ psp = (double)(scrsp.getValue ()) * 0.01;
  		tpsp.setText (new Double(psp).toString ());
    }
    d.repaint ();
  }

  private double s2d (double d, TextField tf)
  { double dd = d;
  	try { dd = new Double(tf.getText()).doubleValue (); }
    catch (NumberFormatException nfe) { }
    if ((dd >= 0.0) && (dd <= 1.0)) d = dd;
    tf.setText (new Double(d).toString ());
    return d;
  }

  public void actionPerformed (ActionEvent evt)
  { if ((evt.getSource () == ok) || (evt.getSource () == bzoom))
 	  { pse = s2d (pse, tpse);
 	  	psp = s2d (psp, tpsp);
 	  	scrse.setValue ((int)(pse * 100.0));
 	  	scrsp.setValue ((int)(psp * 100.0));
 	  }
 	  if (evt.getSource () == bzoom)
 	  {  zoom *= 10.0;
 	     if (zoom > zoommax) zoom = 10.0;
 	     arrondi = zoom * 100.0;
 	  }
    d.repaint();
  }
}

////////////////////////////////////////////////////////////////////////////////

    public static void main (String [] args) {

        tm a = new tm();
        a.init ();
        a.start ();

        Frame f = new Frame ("tm");
        f.addWindowListener (new fermer ());
        f.add (a);
        f.setSize (500, 400);
        f.setVisible (true);
    }

// Permet la fermeture de la fenêtre contenant l'applet
    protected static final class fermer extends WindowAdapter {
        public void windowClosing (WindowEvent e) {
            System.exit (0);
        }
    }

}