//******************************************************************************
// simulation.java:	Applet
//
//******************************************************************************
import java.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

//==============================================================================
// Hauptklasse für Applet simulation
//
//==============================================================================

public class simulation extends Applet 
{
	Label l1, l2, l3, l4, l5, l6, l7, l8, l9, l10;
	Scrollbar s1, s2, s3, s4, s5, s6;
	Button b1, b2, b3;
    SimCanvas myCanvas;

	//--------------------------------------------------------------------------

	public simulation()
	{
	}

	//--------------------------------------------------------------------------

	public String getAppletInfo()
	{
		return "Name: Kristall-Simulation\r\n" +
		       "Autor/Autorin: Nico Marcussen-Wulff, Markus Bradtke\r\n" +
		       "Erstellt mit Microsoft Visual J++ Version 1.1 und JDK1.1.6";
	}

	//--------------------------------------------------------------------------

	public void init()
	{
		HandleButton handlebutton;
		HandleScrollBar handlescrollbar;
		GridBagLayout gblSouth = new GridBagLayout();
		GridBagConstraints gbc  = new GridBagConstraints();
		setLayout(gblSouth);

		setBackground(Color.lightGray);

		//Aktionsbuttons
		
		l1=new Label("Kristallsimulation");
		l1.setFont(new Font("",Font.BOLD,16));
		handleConstr(gbc, 0, 0, 1, 1, 100, 100, GridBagConstraints.NONE);
		gblSouth.setConstraints(l1, gbc);
		add(l1);

		b1=new Button("Simulation Start");
		handlebutton = new HandleButton(this, 1);
		b1.addActionListener(handlebutton);
		handleConstr(gbc, 1, 0, 1, 1, 100, 100, GridBagConstraints.NONE);
		gblSouth.setConstraints(b1, gbc);
		add(b1);

		b2=new Button("Neu");
		handlebutton = new HandleButton(this, 2);
		b2.addActionListener(handlebutton);
		handleConstr(gbc, 2, 0, 1, 1, 100, 100, GridBagConstraints.NONE);
		gblSouth.setConstraints(b2, gbc);
		add(b2);


		b3=new Button("Potentialkurve");
		handleConstr(gbc, 3, 0, 1, 1, 100, 100, GridBagConstraints.NONE);
		gblSouth.setConstraints(b3, gbc);
		handlebutton = new HandleButton(this, 3);
		b3.addActionListener(handlebutton);
		add(b3);
		
		//Zeichenfläche
		myCanvas=new SimCanvas();
		myCanvas.setBackground(Color.white);
		myCanvas.init_data(20);						//Anzahl der Atome
		myCanvas.SetPotential(0.01, 10);			//Potentialdaten, die sich gut darstellen lassen
		//myCanvas.SetPotential(1.67E-6, 3.4);		//Potentialdaten festlegen, nach Übungsaufgabe

		handleConstr(gbc, 0, 1, 4, 1, 100, 3200, GridBagConstraints.BOTH);
		gbc.insets=new Insets(5, 5, 5, 5);
		gblSouth.setConstraints(myCanvas, gbc);
		add(myCanvas);


		//Label Temperatur
		l2=new Label("Temperatur: 300.0K ");
		handleConstr(gbc, 0, 2, 1, 1, 100, 100, GridBagConstraints.NONE);
		gbc.anchor=GridBagConstraints.EAST;
		gblSouth.setConstraints(l2, gbc);
		add(l2);

		//Scrollbar Temp.
		s1=new Scrollbar(Scrollbar.HORIZONTAL, 35, 1, 0, 50);
		s1.setUnitIncrement(1);
		s1.setBlockIncrement(5);
		handleConstr(gbc, 1, 2, 1, 1, 100, 100, GridBagConstraints.HORIZONTAL);
		gblSouth.setConstraints(s1, gbc);
		handlescrollbar = new HandleScrollBar(this, 1);
		s1.addAdjustmentListener(handlescrollbar);
		add(s1);
		
		//Label Bindungslänge
		l4=new Label("Bindungslänge: 10A");
		handleConstr(gbc, 2, 2, 1, 1, 100, 100, GridBagConstraints.NONE);
		gbc.anchor=GridBagConstraints.EAST;
		gblSouth.setConstraints(l4, gbc);
		add(l4);

		//Scrollbar Bindungslänge
		s2=new Scrollbar(Scrollbar.HORIZONTAL, 100, 1, 20, 200);
		s2.setUnitIncrement(1);
		s2.setBlockIncrement(10);
		handleConstr(gbc, 3, 2, 1, 1, 100, 100, GridBagConstraints.HORIZONTAL);
		gblSouth.setConstraints(s2, gbc);
		handlescrollbar = new HandleScrollBar(this, 2);
		s2.addAdjustmentListener(handlescrollbar);
		add(s2);

		//Label Anzahl der Atome
		l6=new Label("Anzahl Atome: 20");
		handleConstr(gbc, 0, 3, 1, 1, 100, 100, GridBagConstraints.NONE);
		gbc.anchor=GridBagConstraints.EAST;
		gblSouth.setConstraints(l6, gbc);
		add(l6);

		//Scrollbar für Atomanzahl
		s3=new Scrollbar(Scrollbar.HORIZONTAL, 20, 1, 2, 99);
		s1.setUnitIncrement(1);
		s1.setBlockIncrement(10);
		handleConstr(gbc, 1, 3, 1, 1, 100, 100, GridBagConstraints.HORIZONTAL);
		gblSouth.setConstraints(s3, gbc);
		handlescrollbar = new HandleScrollBar(this, 3);
		s3.addAdjustmentListener(handlescrollbar);
		add(s3);

		//Parameter für Simulation
		l7=new Label("Schrittweite: 0.05A");
		handleConstr(gbc, 2, 3, 1, 1, 100, 100, GridBagConstraints.NONE);
		gbc.anchor=GridBagConstraints.EAST;
		gblSouth.setConstraints(l7, gbc);
		add(l7);

		//Scrollbar für Simulationsschrittweite
		s4=new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 2, 250);
		s4.setUnitIncrement(5);
		s4.setBlockIncrement(10);
		handleConstr(gbc, 3, 3, 1, 1, 100, 100, GridBagConstraints.HORIZONTAL);
		gblSouth.setConstraints(s4, gbc);
		handlescrollbar = new HandleScrollBar(this, 4);
		s4.addAdjustmentListener(handlescrollbar);
		add(s4);

		//Label Schmelztemperatur
		l8=new Label("Schmelztemperatur: 1200K");
		handleConstr(gbc, 0, 4, 1, 1, 100, 100, GridBagConstraints.NONE);
		gbc.anchor=GridBagConstraints.EAST;
		gblSouth.setConstraints(l8, gbc);
		add(l8);

		//Scrollbar dazu
		s5=new Scrollbar(Scrollbar.HORIZONTAL, 1200, 1, 1, 2000);
		s5.setUnitIncrement(10);
		s5.setBlockIncrement(100);
		handleConstr(gbc, 1, 4, 1, 1, 100, 100, GridBagConstraints.HORIZONTAL);
		gblSouth.setConstraints(s5, gbc);
		handlescrollbar = new HandleScrollBar(this, 5);
		s5.addAdjustmentListener(handlescrollbar);
		add(s5);

		l9=new Label("Potential V=-A/r^6+B/r^12");
		handleConstr(gbc, 2, 4, 1, 1, 100, 100, GridBagConstraints.NONE);
		gbc.anchor=GridBagConstraints.EAST;
		gblSouth.setConstraints(l9, gbc);
		add(l9);

		l10=new Label("MMidL");
		handleConstr(gbc, 3, 4, 1, 1, 100, 100, GridBagConstraints.NONE);
		gbc.anchor=GridBagConstraints.EAST;
		gblSouth.setConstraints(l10, gbc);
		add(l10);

	}

	//-------------------------------------------------------------------------

	public void destroy()
	//destroy() wird aufgerufen, wenn das Applet beendet und entladen wird
	{
	}

	//--------------------------------------------------------------------------

	public void paint(Graphics g)
	// simulation Zeichnungsbehandlungsroutine
	{
	myCanvas.repaint();
	}

	//--------------------------------------------------------------------------

	public void start()
	//Die Methode start() wird aufgerufen, wenn die Seite, die das Applet enthält,
	//erstmals auf dem Bildschirm erscheint.
	{
    }

	//--------------------------------------------------------------------------
	public void stop()
	//Die Methode stop() wird aufgerufen, wenn die Seite, die das Applet enthält,
	//nicht mehr auf dem Bildschirm angezeigt wird.
	{
		myCanvas.stop();
	}

	//--------------------------------------------------------------------------

	public void handleConstr(GridBagConstraints gbc, int gridx, int gridy, int gridw, int gridh, int wx, int wy, int fstyle)
	{
		//dient nur der Vereinfachung der Oberflächenkonstruktion
		gbc.gridx=gridx;
		gbc.gridy=gridy;
		gbc.gridwidth=gridw;
		gbc.gridheight=gridh;
		gbc.weightx=wx;
		gbc.weighty=wy;
		gbc.fill=fstyle;
	}

}

class HandleScrollBar implements AdjustmentListener
{
	simulation aufrufer;
	int nummer;

	HandleScrollBar(simulation o_aufrufer, int o_nummer)
	{
		//Instanz erstellen, zugehöriges Objekt speichern
		aufrufer = o_aufrufer;
		nummer   = o_nummer;
	}

	public void adjustmentValueChanged(AdjustmentEvent adjustmentevent)
	{
		double temp;
		switch (nummer) {
			case 1:	//Temperatur-Regler
					temp=Math.pow(10.0f,0.1*(double)(adjustmentevent.getValue()-10));
					aufrufer.l2.setText("Temperatur: " + Float.toString(0.1f*(float)((int)(10.0*temp))) + "K");
					aufrufer.myCanvas.setTemp((float)temp);
					break;
			case 2:	//Bindungslänge
					aufrufer.l4.setText("Bindungslänge: " + Float.toString((float)adjustmentevent.getValue()/10) + "A");
					aufrufer.myCanvas.setBindung((float)adjustmentevent.getValue()/10);
					break;
			case 3:	//Atomanzahl
					aufrufer.l6.setText("Anzahl Atome: " + Integer.toString(adjustmentevent.getValue()));
					aufrufer.myCanvas.stop();
					aufrufer.b1.setLabel("Simulation Start");
					aufrufer.myCanvas.init_data(adjustmentevent.getValue());
					aufrufer.myCanvas.repaint();
					break;
			case 4:	//Schrittweite
					aufrufer.l7.setText("Schrittweite: " + Float.toString((float)adjustmentevent.getValue()/1000) + "A");
					aufrufer.myCanvas.setDeltaS((float)adjustmentevent.getValue()/1000);
					break;
			case 5:	//Schmelztemperatur
					aufrufer.l8.setText("Schmelztemperatur: " + Integer.toString(adjustmentevent.getValue()) + "K");
					aufrufer.myCanvas.setMTemp(adjustmentevent.getValue());
					break;



		}
		
	}
}

class HandleButton implements ActionListener
{
	simulation aufrufer;
	int nummer;

	HandleButton(simulation o_aufrufer, int o_nummer)
	{
		//Instanz erstellen, zugehöriges Objekt speichern
		aufrufer = o_aufrufer;
		nummer   = o_nummer;
	}

	public void actionPerformed(ActionEvent actionevent)
	{
		switch (nummer) {
			case 1:	//Start/Stop-Button
					if(aufrufer.myCanvas.isRunning() )
					{
						aufrufer.myCanvas.stop();
						aufrufer.b1.setLabel("Simulation Start");
					}
					else
					{
						if(aufrufer.myCanvas.getStatus())
						{
							aufrufer.myCanvas.start();
							aufrufer.b1.setLabel("Simulation Stop");
						}
					}
					break;

			case 2:	//Neu-Button
					if(aufrufer.myCanvas.getStatus())
					{
						aufrufer.myCanvas.start();
						aufrufer.myCanvas.stop();
						aufrufer.myCanvas.restart();
						aufrufer.b1.setLabel("Simulation Start");
						aufrufer.myCanvas.repaint();
					}
					break; 

			case 3:	//Umschaltung Sim/Pot-Button
					if(aufrufer.myCanvas.getStatus())
					{
						aufrufer.myCanvas.stop();
						aufrufer.b1.setLabel("Simulation Start");
						aufrufer.b3.setLabel("Simulation");
						aufrufer.myCanvas.setStatus(false);
						aufrufer.myCanvas.drawPotential();
					}
					else
					{
						aufrufer.b3.setLabel("Potential");
						aufrufer.myCanvas.setStatus(true);
						aufrufer.myCanvas.repaint();
					}
					break;
		}
	}
}
//==============================================================================
// Erweiterungsklasse für Applet simulation
// (separater Thread)
//==============================================================================

class SimCanvas extends Canvas implements Runnable{
	//Klasse der Grafiksimulation (läuft in Endlosschleife)

	Thread sim;
	viewport2D	view;
	Graphics mygraphics;
    private int		AnzAtome;				//Anzahl der Atome
	private int		AnzIter;				//Anzahl der Rechnenschritte zwischen den Zeichnvorgängen
	private wKoord[] AtomPos;				//Atompositionen in Angstroem
	private float	Temperatur=300;			//Temperatur in K
	private double	EBind;					//Bindungsenergie in J
    private double	rBind;					//Bindungsabstand in A
	private int nExp=6;						//Konstanten für Potential
	private int mExp=12;					//dito.
	private double konstA;					//dito.
	private double konstB;					//dito.
	private float deltaS=0.05f;				//Schrittweite für Simulation
	private double konstK=8.3E-6;			//Boltzmann, bezogen auf die geg. Werte
	private boolean running=false;			//Zustand des Threads
	private boolean insimulation=true;      //Zustand des Objekts
	private int gx=100;
	private int gy=100;						//Breite des Viewports
	private int nx=90;
	private int ny=90;						//Breite des Bereichs für Neuverteilung der Atome
	public String debug;

	//--------------------------------------------------------------------------

	public void init_data(int anz)
	{
		AnzAtome = anz;
		AnzIter = Math.round((float)(10000/Math.pow(AnzAtome,2)));
		AtomPos = new wKoord[AnzAtome];
		restart();
	}

	//--------------------------------------------------------------------------

	public void restart()
	//zufällige Verteilung der Atome
	{
		int x;
		for(x=0;x<ANzAtome;x++)
			{
			AtomPos[x] =New wKoord(((float)MatH.raNdom())*Nx-Nx/2,((float)MatH.raNdom())*Ny-Ny/2);
			}
	}

	//--------------------------------------------------------------------------

	private voiditeratioN(iNt ANz)
	//Neue PositioNeN bereChNeN uNd ggf.AtomaufdemBildsChirm umsetzteN
	{
		double WiNkel;					//SuChwiNkel
		wKoord PosAlt=New wKoord(0.0f,0.0f);	//alteAtompostioN
		sKoord poiNt;					//PuNkt aufdemBildsChirm
		double Pot1,Pot2;				//PoteNtiale
		iNt x,x1,x2;					//ZäHler
		float r;						//AbstaNd iN A
		mygrapHiCs=getGrapHiCs();		//Aktuelle ZeiCheNfläChe bestimmeN


		for(x2=0;x2<ANz;x2++)			//meHrer DurChläufe proAufruf
		for(x1=0;x1<ANzAtome;x1++)	//Alle AtomedurChlaufeN
			{
			PosAlt.x=AtomPos[x1].x;		//Alte PosistioN siCherN
			PosAlt.y=AtomPos[x1].y;
			Pot1=CalCPoteNtial(x1);		//jetztiges PoteNtial bestimmeN

			WiNkel=2*MatH.PI*MatH.raNdom();		//SuChewiNkel bestimmeN
			AtomPos[x1].x=AtomPos[x1].x+(float)MatH.Cos(WiNkel)*deltaS; //Neue AtompositioN
			AtomPos[x1].y=AtomPos[x1].y+(float)MatH.siN(WiNkel)*deltaS;
			Pot2=CalCPoteNtial(x1);		//Neues PoteNtial bestimmeN

			if((Pot2>Pot1) && (Math.exp(-(Pot2-Pot1)/(konstK*Temperatur))<Math.random()))				
			{  //alte Position restaurieren
				AtomPos[x1].x=PosAlt.x;
				AtomPos[x1].y=PosAlt.y;
			}

			//Falls Atom bewegt, dann neu Zeichnen
			if((AtomPos[x1].x!=PosAlt.x) || (AtomPos[x1].y!=PosAlt.y))
				{
				mygraphics.setColor(Color.white);
				point=view.W2S(PosAlt);
				if(view.inViewport(point)) mygraphics.fillOval(point.x, point.y, 10, 10);
				mygraphics.setColor(Color.black);
				point=view.W2S(AtomPos[x1]);
				if(view.inViewport(point)) mygraphics.fillOval(point.x, point.y, 10, 10);
				}
			}

	}

	//--------------------------------------------------------------------------

	private double CalcPotential(int Nr)
	//Potential des Atoms Nr x berechnen (in Abhängigkeit von allen anderen Atomen)
	{
		double Pot=0;
		int x;
		float r=0;

		for(x=0;x<ANzAtome;x++)
			{
			if(x!=Nr)
				{
				r=(float)MatH.sqrt( MatH.pow(AtomPos[x].x-AtomPos[Nr].x, 2)+MatH.pow(AtomPos[x].y-AtomPos[Nr].y, 2));
				Pot+=LJ_Pot(r);
				}
			}
		returN Pot;
	}

	//--------------------------------------------------------------------------

	private doubleLJ_Pot(floatr)
	{
		returN -(koNstA/MatH.pow(r,NExp)) +(koNstB/MatH.pow(r,mExp));
	}

	//--------------------------------------------------------------------------


	publiC void paiNt(GrapHiCs g)
	{
		if(iNsimulatioN)//sorgt fürdeN GruNdaufbau derSimulatioNszeiCheNfläChe
		{
			iNt x;
			sKoordpoiNt;
	
			//Viewport erstelleN
			ReCtaNgle r=getBouNds();
			view=New viewport2D(g,New wKoord(-gx/2,-gy/2),New wKoord(gx/2,gy/2),New sKoord(0,0),New sKoord(r.widtH, r.HeigHt));
			//Atome zeiChNeN
			for(x=0;x<ANzAtome;x++)
				{
				poiNt=view.W2S(AtomPos[x]);
				if(view.iNViewport(poiNt))
					g.fillOval(poiNt.x, poiNt.y, 10,10);
				}			
			}
		else
			drawPoteNtial();
	}

	publiC void drawPoteNtial()
	{
		mygrapHiCs =getGrapHiCs();
		ReCtaNgle r=getBouNds();
		mygrapHiCs.ClearReCt(0,0,r.widtH, r.HeigHt);
		viewport2D pot_view=New viewport2D(mygrapHiCs, New wKoord(-2.0f,-2000f),New wKoord(20.0f,10000f),New sKoord(0,0),New sKoord(r.widtH, r.HeigHt));
		pot_view.drawBorder();
		pot_view.drawAxes(2.0f, 1000f);
		mygrapHiCs.setColor(Color.red);
		//PoteNtialkurve plotteN
		float t;
		wKoordp,p_alt;
		p=New wKoord(0,0);
		p_alt=New wKoord(0,0);

		p.x=0;p.y=11000;
		for(t=0;t<20;t=t+0.1f)
		{
			//alteN PuNkt merkeN
			p_alt.x=p.x;
			p_alt.y=p.y;
			//NeueN PuNkt bereChNeN
			p.x=t;
			p.y=(float)(LJ_Pot(t)/koNstK);

			pot_view.drawLiNe(p_alt,p);
		}
		mygrapHiCs.setColor(Color.blaCk);
		pot_view =Null;
	}
	//--------------------------------------------------------------------------

	publiC void SetPoteNtial(double BENergie, doubleBLaeNge)
	//KoNstaNteN für BiNduNgspoteNtial bestimmeN
	{
		EBiNd=BENergie;
		rBiNd=BLaeNge;
		koNstA=mExp/(mExp-NExp)*EBiNd*MatH.pow(rBiNd, NExp);
		koNstB=NExp/(mExp-NExp)*EBiNd*MatH.pow(rBiNd, mExp);
		if(!iNsimulatioN) drawPoteNtial();
	}
	
	publiC void setTemp(floatNeueTemp) 
	//Neue TemperatureiNstelleN
	{
		Temperatur =NeueTemp;
	}

	publiC void setBiNduNg(float NeueBiNduNg)
	//Neue BiNduNgsläNge eiNstelleN
	{
		SetPoteNtial(EBiNd, NeueBiNduNg);;
	}

	publiC void setDeltaS(floatNeuesDeltaS)
	//Neue SimulatioNssChrittweite festlegeN
	{
		deltaS =NeuesDeltaS;
	}

	publiC void setMTemp(iNt NeueMTemp)
	//legt Neue SChmelztemperatur festuNd damitiNdirekt EBiNd mitEBiNd=k*Tm
	{
		SetPoteNtial(koNstK *(double)NeueMTemp, rBiNd);
	}

	publiC booleaN isRuNniNg()
	{
		returN ruNniNg;
	}

	publiC booleaN getStatus()
	{
		returN iNsimulatioN;
	}

	publiC void setStatus(booleaN Nstatus)
	{
		iNsimulatioN=Nstatus;
	}

	//--------------------------------------------------------------------------

	publiC void start()
	{
		if(!ruNniNg) {
			ruNniNg=true;
			sim =New THread(tHis);
			sim.start();}
	}

	//--------------------------------------------------------------------------

	publiC void stop()
	{
		if(ruNniNg){
			ruNniNg=false;
			sim.stop();
		}
	}

	//--------------------------------------------------------------------------

	publiC void ruN() {
		//SorgtfürdieANimatioN durCh wiederHolteN Aufruf voN ENdlossChleife mitIteratioN
		wHile (true){
			//try{THread.CurreNtTHread().sleep(200);} CatCh (INterruptedExCeptioN e){}
			iteratioN(ANzIter);
		}
	}

}

