/*
 * generated by Xtext 2.9.0
 */
package mgpl.generator

import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractGenerator
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
import mgpl.mGPL.Prog
import static extension mgpl.Common.* // erlaubt "name.capitalize" (d. h. Verwendung als Erweiterungsmethode); ohne "extension" wäre nur "capitalize(name)" möglich
// import static -.* importiert statische Datenelemente und Methoden aus der angegebenen Klasse
import static mgpl.mGPL.MGPLPackage.Literals.*
import mgpl.mGPL.ObjDecl
import mgpl.mGPL.AnimBlock
import mgpl.mGPL.EventBlock
import mgpl.mGPL.ArrayDecl
import mgpl.mGPL.VarDecl
import mgpl.mGPL.AttrAss
import mgpl.mGPL.StmtBlock
import mgpl.mGPL.IfStmt
import mgpl.mGPL.ForStmt
import mgpl.mGPL.AssStmt
import mgpl.mGPL.ElementSelect
import mgpl.mGPL.MemberSelect
import mgpl.mGPL.Var
import mgpl.mGPL.Decl
import mgpl.mGPL.ParamDecl
import mgpl.mGPL.Or
import mgpl.mGPL.And
import mgpl.mGPL.Equals
import mgpl.mGPL.Less
import mgpl.mGPL.LessOrEquals
import mgpl.mGPL.Plus
import mgpl.mGPL.Minus
import mgpl.mGPL.Divide
import mgpl.mGPL.Times
import mgpl.mGPL.Negation
import mgpl.mGPL.Complement
import mgpl.mGPL.Touches
import mgpl.mGPL.NumberLiteral

/**
 * Generates code from your model files on save.
 * 
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
 */
class MGPLGenerator extends AbstractGenerator {
	
/* 
 * ##### Alle EObjekte des AST werden durch-iteriert und für jedes gefilterte eine Compilefunktion aufgerufen,
 * ##### die von MGPL in den entsprechenden Javacodeabschnitt übersetzt.
 * ##### Das Java-Programm ist zum Großenteil als Template vorgegeben.
 * ##### TODOs für offene compile-Funktionen befinden sich hier weiter unten!
 * ##### Suchen Sie am Besten nach TODO mit Strg+F um keines zu vergessen.
 * ##### Orientieren Sie sich beim Implementieren an dem Vorgehen der vorgegebenen compile-Funktionen.
 * ##### Verwenden Sie ggf. auch die statischen Funktionen der Datei Common.xtend
 */

	// wird von dem Runtime-Eclipse aufgerufen, wenn eine MGPL-Datei gespeichert wird
	override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
for (g:resource.contents.filter(Prog))
			fsa.generateFile('mgpl/'+g.name.uncapitalize+'/Game.java', g.compile)
	}

	def dispatch CharSequence compile(Prog it) 
	'''
		package mgpl.«name.uncapitalize»;

		import java.awt.Graphics;
		import java.awt.event.KeyEvent;
		
		«/*JPanel Bereich des Spielfensters; Runnable kann als ein Thread gestartet werden*/»
		public class Game extends javax.swing.JPanel implements Runnable {
			private static final long serialVersionUID = 1L;
			«/*Compiler-Warnungen unterdrücken*/»

			«/*konstruiert Spielfenster und reiht die Ausführung dessen Threads in eine Warteschlange der Java-VM ein*/»
			public static void main(String[] args) {
				java.awt.EventQueue.invokeLater(new GameWindow());
			}

			private static class GameWindow extends javax.swing.JFrame implements Runnable {
				private static final long serialVersionUID = 1L;
		
				«/*Konstruktor: bereitet das Spielfenster vor: Panel, Titel, Größe, Position, Schließen-Schaltfläche*/»
				public GameWindow() {
					«/*erstellt Spiel und platziert es als Panel im Fenster*/»
					add(new Game(this));
					setTitle("«name.capitalize»");
					setResizable(false);
					pack(); «/*passt Größe des Fensters an bevorzugte Größe des Panels an*/»
					setLocationRelativeTo(null); «/*Bildschirmitte*/»
					«/*Wenn x- und y-Attribute des Spiels gesetzt sind, wird das Fenster an diese Position verschoben:*/»
					if (x != null)
						setLocation(x, getY());
					if (y != null)
						setLocation(getX(), y);
					«/*Schließen-Schaltfläche beendet das Programm*/»
					setDefaultCloseOperation(EXIT_ON_CLOSE);
				}

				@Override
				public void run() {
					setVisible(true); «/*macht das Fenster sichtbar*/»
				}
			}
		
			«/*MGPL-Attribute des Spiels mit ihren Standardwerten*/»
			public static int height = 500;
			public static int width = 500;
			private static int speed = 50;
			public static Integer x = null;
			public static Integer y = null;
			private int delay; «/* Zeit in Millisekunden zwischen zwei aufeinanderfolgen Animationsbildern (kein MGPL-Attribut)*/»

			«/*Konstruktor: wird ausgeführt, bevor der Konstruktor des Spielfensters das Fenster einrichtet*/»
			public Game(GameWindow window) {
				«/*setzt bevorzugte Größe des Panels auf Größe der MGPL-Spielfläche*/»
				setPreferredSize(new java.awt.Dimension(width, height));
				setDoubleBuffered(true); «/*verbessert Animationen */»
				delay = getDelay(speed);
				«/* registriert Tastaturfunktionalität beim Spielfenster*/»
				window.addKeyListener(onEvent);
			}

			«/*berechnet Verzögerung aus speed-Attribut*/»
			private int getDelay(int speed) {
				speed = 100 - speed;
				return (int) (0.0899 * (speed * speed) + (speed + 1));
			}

			«/*wird von Java-VM aufgerufen*/»
			@Override
			public void addNotify() {
				super.addNotify();
				«/*startet den Thread dieses Games, der die Spielschleife beinhaltet*/»
				new Thread(this).start();
			}
			
			«/*start von Game-Thread: führt abwechselnd aus und zeichnet graphische Objekte*/»
			@Override
			public void run() {
				long beforeTime = System.currentTimeMillis();
				while (true) {
					«/*Animationen durchführen. Wenn das Objekt eine Animation hat, dann Animation-Handler aufrufen und das Objekt übergeben*/»
					for (Object o : objects) {
						if (o.animation_block != null)
							o.animation_block.animate(o);
					}
					«/*Panel neu Zeichnen*/»
					repaint();
					long sleep = beforeTime + delay - System.currentTimeMillis();
					if (sleep <= 0)
						sleep = 1;
					try {
						Thread.sleep(sleep);
					} catch (InterruptedException e) {
						System.err.println("Interrupted: " + e.getMessage());
					}
					beforeTime = System.currentTimeMillis();
				}
			}
			«/*zeichnet alle graphischen Objekte*/»
			@Override
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				for (Object o : objects)
					if (b(o.visible))
						o.draw(g);
				java.awt.Toolkit.getDefaultToolkit().sync();
			}

		«/*abstrakte Oberklasse aller graphischen Objekte; innere Klasse, statisch:
			enthält gemeinsame Attribute und Infrastruktur für Zeichnen und Berührungstests  */»
			private static abstract	 class Object {
				
				«/*gemensame Attribute */»
				Animation animation_block;
				public int visible = 1;
				public int x;
				public int y;

				public abstract void draw(Graphics g); «/*zeichnet das Objekt */»

				«/*liefert ein das Objekt eng umschließendes Java-Rechteck */»
				public abstract java.awt.Rectangle getBounds();
			}

			«/*Klasse für MGPL-Kreise; innere Klasse, statisch*/»
			private static class Circle extends Object {
				public int radius; «/*Radius des Kreises */»

				«/*zeichnet den Kreis */»
				@Override
				public void draw(Graphics g) {
					g.fillOval(x, y, 2 * radius, 2 * radius);
				}

				@Override
				public java.awt.Rectangle getBounds() {
					return new java.awt.Rectangle(x, y, 2 * radius, 2 * radius);
				}
			}

			private static class Rectangle extends Object {
				public int height;
				public int width;

				@Override
				public void draw(Graphics g) {
					g.fillRect(x, y, width, height);
				}

				@Override
				public java.awt.Rectangle getBounds() {
					return new java.awt.Rectangle(x, y, width, height);
				}
			}

			private static class Triangle extends Object {
				public int height;
				public int width;

				@Override
				public void draw(Graphics g) {
					g.fillPolygon(new int[] { x, x + width, x + width / 2 }, new int[] { y, y, y + height }, 3);
				}

				@Override
				public java.awt.Rectangle getBounds() {
					return new java.awt.Rectangle(x, y, width, height);
				}
			}

			private static abstract class Animation {
				protected abstract void animate(Object object);
			}

			private boolean b(int i) {
				return i != 0;
			}

			private int i(boolean b) {
				return b ? 1 : 0;
			}

			private int or(int left, int right) {
				return i(b(left) || b(right));
			}

			private int and(int left, int right) {
				return i(b(left) && b(right));
			}

			private int eq(int left, int right) {
				return i(left == right);
			}

			private int lt(int left, int right) {
				return i(left < right);
			}

			private int le(int left, int right) {
				return i(left <= right);
			}

			private int not(int value) {
				return i(!b(value));
			}

			private int touches(Object left, Object right) {
				return i(left.getBounds().intersects(right.getBounds()));
			}

			«IF eIsSet(PROG__ATTR)/*wird geschaut, ob das Feature atrr mit einem Wert belegt ist */»
				{
					«FOR a:attr»
						«a.compile»
					«ENDFOR»
				}
			«ENDIF»

			«FOR d:declarations»
				«d.compile»
			«ENDFOR»

			private Object[] objects = new Object[] { «FOR o:declarations.filter(ObjDecl) SEPARATOR ', '»«o.objectNames»«ENDFOR» };

			«init.compile»
			«FOR a:blocks.filter(AnimBlock)»
				«a.compile»
			«ENDFOR»

			java.awt.event.KeyListener onEvent = new java.awt.event.KeyAdapter() {
				@Override
				public void keyPressed(KeyEvent e) {
					switch (e.getKeyCode()) {
					«FOR e:blocks.filter(EventBlock)»
						«e.compile»
					«ENDFOR»
					}
				}
			};
		}
	'''
	
	def dispatch objectNames(ArrayDecl it) 
	'''«FOR i:0..<length SEPARATOR ', '»«variable.javaIdentifier»[«i»]«ENDFOR»'''
	
	def dispatch objectNames(ObjDecl it)
	'''«javaIdentifier»'''

	def dispatch CharSequence compile(ArrayDecl it) 
	'''
		«IF variable instanceof VarDecl»
			private «variable.type»[] _«variable.name» = new «variable.type»[«length»];
		«ENDIF»
		«IF variable instanceof ObjDecl»
			private «variable.type.capitalize»[] _«variable.name» = new «variable.type.capitalize»[] { «FOR i:0..<length SEPARATOR ', '»new «variable.type.capitalize»()«ENDFOR» };
		«ENDIF»
	'''
	
	def dispatch CharSequence compile(VarDecl it) 
	'''private «type» _«name»«IF eIsSet(VAR_DECL__INIT)» = «init.compile»«ENDIF»;'''
	
	def dispatch CharSequence compile(ObjDecl it)
	'''
		private «type.capitalize» _«name» = new «type.capitalize»() {
			«IF eIsSet(OBJ_DECL__ATTR) /*wenn attribute gesetzt sind */»
				{
					«FOR a:attr»
						«a.compile»
					«ENDFOR»
				}
			«ENDIF»
		};
	'''

	def dispatch CharSequence compile(AttrAss it) 
	''' ''' /*TODO*/

	def dispatch CharSequence compile(AnimBlock it) 
	'''
		private void _«name»(Object _«param.name») {
			_«name»((«param.type.capitalize») _«param.name»);
		}
		private void _«name»(«param.type.capitalize» _«param.name») «stmtBlock.compile»
	'''
	
	def dispatch CharSequence compile(EventBlock it) 
	'''
		case KeyEvent.«keyStroke.VKCode»: «stmtBlock.compile»
			break;
	'''
	
	def getVKCode(String keyStroke) {
		switch (keyStroke) {
		case 'space': 'VK_SPACE'
		case 'leftarrow': 'VK_LEFT'
		case 'rightarrow': 'VK_RIGHT'
		case 'uparrow': 'VK_UP'
		case 'downarrow': 'VK_DOWN'
		}
	}

	def dispatch CharSequence compile(StmtBlock it) 
	''' ''' /*TODO*/
		
	def dispatch CharSequence compile(IfStmt it) 
	''' ''' /*TODO*/
	
	def dispatch CharSequence compile(ForStmt it) 
	''' ''' /*TODO*/
	
	def compileBareAssignment(AssStmt it) 
	'''«variable.compile» = «expression.compile»'''
	
	def dispatch CharSequence compile(AssStmt it)
	'''«compileBareAssignment»;'''

	def dispatch CharSequence compile(Var it)
	'''«id.javaIdentifier»'''
	
	def dispatch CharSequence compile(ElementSelect it)
	'''«variable.compile»[«index.compile»]'''
	
	def dispatch CharSequence compile(MemberSelect it) 
	''' ''' /*TODO*/

	def dispatch javaIdentifier(Prog it) 
	'''Game'''
	
	def dispatch javaIdentifier(Decl it) 
	'''_«name»'''
	
	def dispatch javaIdentifier(AnimBlock it) 
	'''
		new Animation() {
			@Override
			protected void animate(Object object) {
				_«name»(object);
			}
		}
	'''
	
	def dispatch javaIdentifier(ParamDecl it) 
	'''_«name»'''

	// Expressions: boolean operators
	def dispatch CharSequence compile(Or it) '''or(«left.compile», «right.compile»)'''
	def dispatch CharSequence compile(Equals it) '''eq(«left.compile», «right.compile»)'''
	def dispatch CharSequence compile(And it) ''' ''' /*TODO*/
	def dispatch CharSequence compile(Less it) ''' ''' /*TODO*/
	def dispatch CharSequence compile(LessOrEquals it) ''' ''' /*TODO*/
	def dispatch CharSequence compile(Complement it) ''' ''' /*TODO*/
	def dispatch CharSequence compile(Touches it) ''' ''' /*TODO*/

	// Expressions: arithmetic Operators
	def dispatch CharSequence compile(NumberLiteral it) '''«value»'''
	def dispatch CharSequence compile(Negation it) '''-«expr.compile»'''
	def dispatch CharSequence compile(Plus it) '''(«left.compile» + «right.compile»)'''
	def dispatch CharSequence compile(Minus it) ''' ''' /*TODO*/
	def dispatch CharSequence compile(Divide it) ''' ''' /*TODO*/
	def dispatch CharSequence compile(Times it) ''' ''' /*TODO*/

	}
