// Titel:   PolyPlot v 1.1 applet
// Datum:   26 november 1997
//          13 juni 1998 appletversie (AvD)
// Auteurs: Arthur van Dam & Tijmen Collignon
//
// Beschrijving: Dit programma berekent en plot polynomen
// door aangeklikte coordinaten mbv de 'LaGrange'-methode.
// 27 juli 2001: cleaned up multi-language support - AvD

import java.awt.*;
import java.applet.Applet;
import java.lang.*;
import java.util.Hashtable;

public class Polyplotp extends Applet
{
    TextField formule, coordinates, punten;
    PolyCanv scherm;
    Polynoom p;
    Punt punt;
    Choice schaalkeuze;
    PolyplotLangSupport langSupport;

    public void init()
    {
        Panel knoppen, uitvoer;
        setBackground(Color.white);
        resize(510,450);

        langSupport = new PolyplotLangSupport(getParameter("lang"));

        punt        = new Punt();           // punt object nu al aanmaken om mee te geven aan klasse Polynoom.
        p           = new Polynoom(punt);
        knoppen     = new Panel();
        uitvoer     = new Panel();
        scherm      = new PolyCanv(langSupport, p, punt, this);  // Scherm heeft dezelfde Polynoom onder handen, dezelfde punt-object,
                                                    // en moet TextFields in klass Frame kunnen aanpassen.
        punten      = new TextField();
        formule     = new TextField(70);
        coordinates = new TextField(8);
        schaalkeuze = new Choice();         // Keuzelijst voor de schaal.

        schaalkeuze.addItem(" 1:1");
        schaalkeuze.addItem(" 2:1");
        schaalkeuze.addItem(" 5:1");
        schaalkeuze.addItem("10:1");
        schaalkeuze.addItem("25:1");
        schaalkeuze.addItem("50:1");
        schaalkeuze.select(3);

	      formule.    setText(langSupport.get("msg_welcome"));
        formule.    setEditable(false);
        coordinates.setEditable(false);
        punten.     setEditable(false);

        uitvoer.setLayout( new BorderLayout());         // Borderlayout wordt gebruikt voor goed inpassen TextFields.
            uitvoer.add("North", formule);
            uitvoer.add("East", coordinates);
            uitvoer.add("West", schaalkeuze);
            uitvoer.add("Center", punten);
        knoppen.setLayout(new GridLayout(2, 1, 0, 0));
            knoppen.add(new Button(langSupport.get("cmd_clear")));
            knoppen.add(new Button(langSupport.get("cmd_interpolate")));

        GridBagLayout      gbl = new GridBagLayout();
        GridBagConstraints gbc = new GridBagConstraints();
        
        Panel total = new Panel();
	total.setLayout(gbl);
	gbc.ipadx=1;
	gbl.setConstraints(knoppen, gbc);
        total.add(knoppen);

	gbc.fill=GridBagConstraints.HORIZONTAL;
	gbl.setConstraints(uitvoer, gbc);
        add(uitvoer);
        
        
	add("North", total);
	add("Center", scherm);
    }

    public boolean action(Event ev, Object obj)
    {
        String s;
        int n = schaalkeuze.getSelectedIndex();
	try                                             // Kijkt even of Button is ingedrukt, anders
        {
            s = ((Button)ev.target).getLabel();
        }
        catch(ClassCastException e)
        {
            s = schaalkeuze.getSelectedItem();          // was het een Keuze Item.
        }
        if(s==langSupport.get("cmd_clear"))                                  // Nu is de String bekend, dus alle mogelijkheden langslopen
        {
            p.Reset();
            scherm.wissen();
        }
        else if(s==langSupport.get("cmd_interpolate") && punt.aantal > 0)    // Zonder punten niet tekenen
        {
            p.Reset();                                  // Arrays op nul
            p.Lagrange();                               // Polynoom p opnieuw opstellen
            formule.setText("" + p);              // Na berekening p, formule opstellen
            scherm.plotter = true;                      // Paint laten weten dat er getekend mag worden
            scherm.repaint();
        }
        else{
	switch(n){
	    case 0 : scherm.schaal = 1; break;
            case 1 : scherm.schaal = 2; break;
            case 2 : scherm.schaal = 5; break;
            case 3 : scherm.schaal = 10; break;
            case 4 : scherm.schaal = 25; break;
            case 5 : scherm.schaal = 50; break;
	}
 	scherm.plotter = true;
	scherm.repaint();
	}
	   return true;
    }
}

class PolyCanv extends Canvas
{
    Polyplotp plot;
    Polynoom p;
    Punt punt, plotpunt;                        // plotpunt: apart punt-object voor tekenen volledige polynoom
    final int x_midden = 255, y_midden = 200;   // constanten voor midden van PolyCanv (en lokatie oorsprong)
    int schaal;
    boolean plotter;                            // Aan de hand hiervan weet Paint of hij naast de punten ook de Polynoom
                                                // mag tekenen
    PolyplotLangSupport langSupport;

    PolyCanv(PolyplotLangSupport langSupport, Polynoom huidigp, Punt huidigpunt, Polyplotp huidigplot)
    {
        resize(510,400);
        setBackground(new Color(195, 195, 195));
        plotter = false;
        plot = huidigplot;
        p    = huidigp;
        punt = huidigpunt;
        schaal = 10;
        this.langSupport = langSupport;
        repaint();
    }

    public void paint(Graphics g)
    {
        Font schaalfont = new Font((getFont()).getName(), Font.PLAIN,10);
        g.setFont(schaalfont);

        g.drawLine(0, y_midden, 2*x_midden-1, y_midden); // Draw axes
        g.drawLine(x_midden, 0, x_midden, 2*y_midden-1);

        for(int h = x_midden%50; h<=(2*x_midden-1); h+=50)
        {
            g.drawLine(h, y_midden+3, h, y_midden-3);                       // Zet schaalverdeling met op schaal
            g.drawString(""+(h-x_midden)/schaal, h-10, y_midden+15);        // aangepaste cijfers (x-as)
        }
        for(int v = y_midden%50; v<=(2*y_midden-1); v+=50)
        {
            g.drawLine(x_midden-3, v, x_midden+3, v);                       // idem voor y-as
            if(v != y_midden)
                g.drawString(""+(y_midden - v)/schaal, x_midden+5, v+5);
        }
        for(int i=0; i<punt.aantal; i++)
        {
            g.setColor(Color.red);
            g.drawLine((int)(schaal*punt.xs[i])+ x_midden - 2, y_midden - (int)(schaal*punt.ys[i]) - 2, (int)(schaal*punt.xs[i]) + x_midden + 2, y_midden - (int)(schaal*punt.ys[i]) + 2);
            g.drawLine((int)(schaal*punt.xs[i])+ x_midden + 2, y_midden - (int)(schaal*punt.ys[i]) - 2, (int)(schaal*punt.xs[i]) + x_midden - 2, y_midden - (int)(schaal*punt.ys[i]) + 2);
            // Teken punten na correctie met het middelpunt en de schaal
        }
        if(plotter == true && p.graad != -1)    //p.graad mag niet gelijk zijn aan -1, want dat betekent dat de polynoom niet opgesteld is (allemaal nullen als coëfficiënten).
        {
            plotpunt = new Punt();

            g.setColor(Color.blue);
            for(int xteller = 0; xteller < 2 * x_midden; xteller++)
            {
                // Bereken alle y-waarden na invullen van geschaalde x en tekenen van lijnen tussen deze punten
                plotpunt.Voegtoe((double)(xteller - x_midden)/schaal, p.Vul_x_in((double)(xteller - x_midden)/schaal));
                if(xteller > 0)
                    g.drawLine((int)(schaal*(plotpunt.xs[xteller-1])) + x_midden, y_midden - (int)(schaal*plotpunt.ys[xteller-1]), (int)(schaal*plotpunt.xs[xteller]) + x_midden, y_midden - (int)(schaal*plotpunt.ys[xteller]));
            }
            plotter = false;
        }

    }

    public boolean mouseDown(Event evt, int x, int y)
    {
        if(punt.Checkpoint((double)(x - x_midden)/schaal)==false)
            plot.formule.setText(langSupport.get("msg_xexists"));
        else
        {
            // Voeg punt toe aan array, TextField en Canvas
            plot.formule.setText(langSupport.get("msg_nextpt"));
            punt.Voegtoe((double)(x - x_midden)/schaal, (double)(y_midden - y)/schaal);
            Font puntfont = new Font(getFont().getName(), Font.PLAIN, 12);
            plot.punten.setFont(puntfont);
            plot.punten.setText(langSupport.get("txt_pts") + ": " + punt);
            repaint();
        }
        return true;
    }

    public boolean mouseMove(Event evt, int x, int y)   // Laat coördinatenteller lopen tijdens bewegen
    {
        int xc = x - x_midden;
        int yc = y_midden - y;
        plot.coordinates.setText((double)xc/schaal + ", " + (double)yc/schaal);
        return true;
    }

    public boolean mouseExit(Event evt, int x, int y)
    {
	plot.coordinates.setText("");
	return true;
    }

    public void wissen()
    {
        plot.formule.setText(langSupport.get("msg_ptscleared"));
        punt.Wissen();
        try         // Als wissen wordt ingedrukt voordat interpolate is ingedrukt, bestaat er *nog* geen plotpunt
        {
            plotpunt.Wissen();
            plot.formule.setText(langSupport.get("msg_ptscleared"));
        }
        catch(Exception e)
        {
            plot.formule.setText(langSupport.get("msg_ptscleared"));
        }
        plot.punten.setText(langSupport.get("txt_pts") + ": " + punt);
        repaint();
    }

}

class Punt
{
    double xs[], ys[];
    int aantal;

    Punt()
    {
        xs = new double[600];   // Als x_midden in PolyCanv wordt aangepast, dan hier ook arraylengte veranderen!
        ys = new double[600];
        aantal = 0;
    }

    void Voegtoe(double x, double y)
    {
        xs[aantal] = x;
        ys[aantal] = y;
        aantal++;
    }

    boolean Checkpoint(double x)
    {
        for(int loper = 0; loper < aantal; loper++)
            if( xs[loper]== x )
                return false;
        return true;
    }

    void Wissen()
    {
        for(int index = 0; index < aantal; index++)
        {
            xs[index]=0;
            ys[index]=0;
        }
        aantal = 0;
    }

    public String toString()           // Geeft inhoud van array weer op TextField punten
    {
        String s = "";
        if(aantal == 0) return "";
        for(int loper = 0; loper < aantal; loper++)
            s += "["+ xs[loper] +", "+ ys[loper] +"], ";
        return s;
    }


}

class Polynoom
{
    double coef[];
    int graad;
    Punt punt;

    Polynoom(Punt huidigpunt)
    {
        coef = new double[100];
        graad = -1;         // -1 staat voor ongedefinieerd, 0 is niet handig,
                            // want dat betekent horizontale lijn over x-as.
        punt = huidigpunt;
    }


    void Lagrange()
    {
        Polynoom q;
        coef[0] = punt.ys[0];
        graad = 0;
        for(int telpunt = 1; telpunt < punt.aantal; telpunt++)      // loop alle ingevoerde punten langs
        {
            q = new Polynoom(punt);
            q.coef[0] = 1;
            q.graad = 0;
            q.Maak(telpunt, this);          // maak correctie polynoom t.o.v. vorig polynoom
            Telop(q);                       // tel deze op bij de definitieve polynoom

        }
    }

    void Maak(int telpunt, Polynoom vorigpoly)
    {
        double b, noemer;

        b = punt.ys[telpunt] - vorigpoly.Vul_x_in(punt.xs[telpunt]); // Verschil tussen y-waarde en x-waarde ingevuld in oude polynoom.
        noemer = Noemer(telpunt);
        for(int teller = 0; teller < telpunt; teller++)
            Maal_x_c(punt.xs[teller]);
        for(int index = 0; index <= graad; index++)     // Eindresultaat: polynoom volgens:
            coef[index] *= (b/noemer);                  // q(x)=(b/{a[m]-a[1])·...·(a[m]-a[m-1])}·(x-a[1])·...·(x-a[m-1])
    }

    void Telop(Polynoom anderpoly)
    {
        for(int index = 0; index <= Math.max(graad,anderpoly.graad); index++)   //Langste array bepaalt lengte som-array
            coef[index] += anderpoly.coef[index];
        graad = Math.max(graad, anderpoly.graad);
    }

    double Vul_x_in(double x)
    {
        double uitkomst = 0;
        double macht = 1;
        for(int teller = 0; teller<=graad; teller++)
        {
            uitkomst += coef[teller]*macht;
            macht *= x;                     // Iedere keer vermenigvuldigen met x,
                                            // zodat niet telkens volledig nieuwe macht nodig is
        }
        return uitkomst;
    }

    void Maal_x_c(double c)                 // Vermenigvuldigt bestaande array met (x-c)
    {
        for(int loper = graad; loper >= 0; loper--)
            coef[loper + 1] = (coef[loper + 1] * - c) + coef[loper];
        graad++;
        coef[0] *= -c;
    }

    double Noemer(int laatste)              // Berekent noemer: (x[m]-x[1])·...·(x[m]-x[m-1])
    {
        double uitkomst = 1;
        for(int puntloper = 0; puntloper <= laatste-1; puntloper++)
            uitkomst *= (punt.xs[laatste]-punt.xs[puntloper]);
        return uitkomst;
    }
    public String toString()
    {
        String s = "f(x) = ";

        for(int i = graad; i >= 0; i--)
        {
            if(s != "" && coef[i] > 0 && i < graad)     // Geen + voor eerste en na laatste term.
                s += "+ ";
            if(coef[i] != 0 && coef[i] != 1)            // Als coëfficiënt geen 0 of 1 is,
                s += coef[i] + "";                      // afbeelden
            if(coef[i] != 0 && coef[i] != 1 && i != 0)  // Als er een coëfficiënt en x staat
                s += "·";                               // vermenigvuldigteken ertussen:
            if(coef[i] != 0 && i > 0)                   // Geen x bij 0·x en x^0
                s += "x";
            if(coef[i] != 0 && i == 1)                  // Geen machtteken, maar wel een spatie
                s += " ";
            if(coef[i] != 0 && i >= 2)                  // Bij zinvolle term, ^ + macht plaatsen
                s += "^"+ i + " ";
        }
        return s;
    }

    void Reset()
    {
        for(int index = 0; index <=graad; index++) // Alles op nul: tegen later foutief doorrekenen
            coef[index] = 0;
        graad = -1;                                // Aangeven dat array 'leeg' is; niet geldig
    }
}

class PolyplotLangSupport
{
  private String lang;
  private String default_lang;
  private Hashtable dictionaries;
  private Hashtable activeDict;

  public PolyplotLangSupport(String lang)
  { default_lang = "eng";
    initDictionaries();
    setLang(lang);
  }

  public void setLang(String lang)
  { try
    { activeDict = (Hashtable)dictionaries.get(lang);
    }
    catch(NullPointerException e)
    { activeDict = (Hashtable)dictionaries.get(default_lang);
    }
  }

  private void initDictionaries()
  {
    Hashtable eng = new Hashtable();
    Hashtable dut = new Hashtable();
    eng.put("msg_welcome", new String("Welcome to Poly Plot v. 1.1 !"));
    eng.put("cmd_clear", new String("clear"));
    eng.put("cmd_interpolate", new String("interpolate"));
    eng.put("txt_pts", new String("Points"));
    eng.put("msg_xexists", new String("This x-value already belongs to an other point! Try again!"));
    eng.put("msg_nextpt", new String("Click another point or push \"Interpolate\"!"));
    eng.put("msg_ptscleared", new String("All points cleared"));

    dut.put("msg_welcome", new String("Welkom bij Poly Plot v. 1.1 !"));
    dut.put("cmd_clear", new String("wissen"));
    dut.put("cmd_interpolate", new String("interpoleer"));
    dut.put("txt_pts", new String("Punten"));
    dut.put("msg_xexists", new String("Deze x-waarde hoort al bij een ander punt; klik ergens anders!"));
    dut.put("msg_nextpt", new String("Klik een nieuw punt aan of druk op \"Interpoleer\"!"));
    dut.put("msg_ptscleared", new String("Alle punten gewist!"));

    dictionaries = new Hashtable();
    dictionaries.put("eng", eng);
    dictionaries.put("dut", dut);
  }

  public String get(String key)
  { String value = (String)activeDict.get(key);
    if(value == null)
    { value = "";
    }
    return value;
  }

  public String[] getList(String key)
  { String value[] = (String[])activeDict.get(key);
    if(value == null)
    { value = new String[0];
    }
    return value;
  }
}
