

import java.util.ArrayList;
import java.util.List;


public class Parser {
	
	private List<String> lexems;
	private ProjectGUI parent;
	
	public Parser(List<String> lexems,ProjectGUI parent) throws Exception{
		this.lexems = lexems;
		this.parent = parent;
		parse();
	}
	
	public void parse() throws Exception {
		
		//sprawdzenie czy poprawny program - program nazwa .. end.
		if (!lexems.get(0).equals("program")) throw new Exception("Brak slowa kluczowego program na poczatku kodu");
		if (!Scanner.isIdentifier(lexems.get(1))) throw new Exception("Brak nazwy programu");
		if (!lexems.get(lexems.size()-1).equals("end.")) throw new Exception("Program niepoprawnie zakonczony");
		boolean isVar = false; //czy sa zadeklarowane zmienne
		isVar = lexems.contains("var");
		if (isVar) { //sa zmienne
			int i= parent.getPosOfLastVariable();
			boolean foundBegin = false; //czy znaleziono slowo kluczowe 'begin'
			//przechodzimy do sekcji programu za deklaracja zmiennych 
			//oraz sprawdzamy czy jest slowo kluczowe 'begin'
			while (!foundBegin && i<lexems.size()) {
				if (lexems.get(i++).equalsIgnoreCase("begin")) 
						foundBegin = true;				
			} 
			
			if (!foundBegin) throw new Exception("Sekcja wykonywalna nie rozpoczyna sie od slowa kluczowego 'begin'");
			//sprawdzenie czy nie ma dwoch instrukcji po sobie - co jest bledem
			for (int j=parent.getPosOfLastVariable();j<lexems.size()-1;j++) {
				String a = lexems.get(j);
				String b = lexems.get(j+1);
				if (Scanner.isChar(a) && Scanner.isChar(b))
					throw new Exception("Znaki "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
				if (Scanner.isIdentifier(a) && Scanner.isIdentifier(b))
					throw new Exception("Identyfikatory "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
				if (Scanner.isInteger(a) && Scanner.isInteger(b))
					throw new Exception("Liczby "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
				if (Scanner.isKeyword(a) && Scanner.isKeyword(b)) {
					if (!a.equalsIgnoreCase("begin") && !a.equalsIgnoreCase("do"))
						if (!a.equalsIgnoreCase("then") )
						throw new Exception("Slowa kluczowe "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
					if (a.equalsIgnoreCase("begin") && !(b.equalsIgnoreCase("while") || b.equalsIgnoreCase("if") 
						 	|| b.equalsIgnoreCase("write") || b.equalsIgnoreCase("read") || b.equalsIgnoreCase("writeln") 
							|| b.equalsIgnoreCase("end.") || b.equalsIgnoreCase("end")))
					throw new Exception("Slowa kluczowe "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
				}
				if (Scanner.isReal(a) && Scanner.isReal(b))
					throw new Exception("Liczby "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
				if (Scanner.isString(a) && Scanner.isString(b))
					throw new Exception("Napisy "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
				if (Scanner.isOperator(a) && Scanner.isOperator(b)) {
					if (a.equals(")") && b.equals("("))
						throw new Exception("Nawiasy "+a+" oraz "+b+" nie moga wystepowac jednoczesnie po sobie");
					 if (a.equals(")") && (!Scanner.isArithemiticalExpression(b) && !b.equals(")") && !b.equals(";") && !b.equals("=") && !Scanner.isLogicalOperator(b)))
						throw new Exception("Operatory "+a+" oraz "+b+" nie moga wystepowac obok siebie");
					 if (a.equals("-")) { //pierwszy jest minus
						if (!b.equals("("))
							throw new Exception("Operatory "+a+" oraz "+b+" nie moga wystepowac obok siebie");
					}
					 if (Scanner.isArithemiticalExpression(a) && !(b.equals("(") || Scanner.isIdentifier(b) || 
						Scanner.isInteger(b) || Scanner.isReal(b)))
						throw new Exception("Operatory "+a+" oraz "+b+" nie moga wystepowac obok siebie");
					 
					
					
					// throw new Exception("Operatory "+a+" oraz "+b+" nie moga wystepowac obok siebie");
						
				}
				
				
			}
			
			//sprawdzanie instrukcji po instrukcji
			//i - ostatnio przetwarzana pozycja na liscie lexemow - czyli pozycja begin
			while (i<lexems.size()-1) { // -1 bo ostatnim lexemem jest end. 
			//	System.out.println("Poczatek petli");
				String lexem = lexems.get(i);
				List<String> temp = new ArrayList<String>();
				//wczytanie skladni calej instrukcji do listy
				//instrukcja = ciag lexemow do srednika
				int open = 0; //ilosc nawiasow otwierajacych
				int close = 0; // -||-         zamykajacych
				while (!lexem.equals(";") && i<lexems.size()) { //badamy instrukcje po instrukcji, az napotkamy ma koncu kazdej srednik				
					if (lexems.get(i).equals("(")) open++;
					if (lexems.get(i).equals(")")) close++;
					lexem = lexems.get(i++);
					temp.add(lexem);
					
				}
			//	System.out.println(temp);
				//sprawdzenie ilosci nawiasow
				if (open!=close) throw new Exception("Ilosc nawiasow otwierajacych oraz zamykajacych nie zgadza sie");
			
				
				
				
				//jesli instrukcja warunkowa badz petla to natepny musi byc warunek
				int j=0;
				//System.out.println("temp[0] = "+temp.get(0));
				if (temp.get(0).equalsIgnoreCase("while")) {
					boolean foundDo = false;
					
					for (j=1;j<temp.size();j++) {
						if (temp.get(j).equalsIgnoreCase("do")) { foundDo = true;
							break; }
					}
					if (!foundDo)
						throw new Exception("Blad skladni - brak slowa kluczowego 'do' w petli 'while'");
					
					//logicalInstruction(j,temp);
					
				}
		        //czy instrukcja if
				if (temp.get(0).equalsIgnoreCase("if")) {
					boolean foundDo = false;
					
					for (j=1;j<temp.size();j++) {
						if (temp.get(j).equalsIgnoreCase("then")) { foundDo = true;
							break; }
					}
					if (!foundDo)
						throw new Exception("Blad skladni - brak slowa kluczowego 'then' w klauzuli 'if'");
					
				//	logicalInstruction(j,temp);
					
				}
				
				int x = 0;
				if (temp.get(0).equalsIgnoreCase("while") || temp.get(0).equalsIgnoreCase("if")) {
					//znalezienei pozycji do lub then
					
					String xx = temp.get(x);
					while (x<temp.size() && !(xx.equalsIgnoreCase("do") || xx.equalsIgnoreCase("then"))) {
						x++;
						xx = temp.get(x);
					}
					//kolejne wyrazenie musi byc formula logiczna
					logicalInstruction(1,x,temp);
				}
				//System.out.println("x = "+x);
				if (x!=0)
					temp = temp.subList(x+1,temp.size());
				
				//System.out.println("temp2: "+temp);
				
				//instrukcja przypisania
				if (temp.contains(":=") && !temp.contains("if")) {
					String lvalue = temp.get(0); //do czego przypisujemy
					Variable v = parent.getVariables().get(lvalue);
					if (v==null && !Scanner.isKeyword(temp.get(0))) throw new Exception("Brak zmiennej "+lvalue+" - przypisanie nie jest mozliwe");
					if (!Scanner.isKeyword(lvalue)) {
					String type = v.getType();
					if (type.equalsIgnoreCase("integer")) { //liczba calkowita - nie moze byc dalej real-ow, boolean, char, string 
						for (int h=x;h<temp.size();h++) {
							String lex = temp.get(h);
							//System.out.println("lex = "+lex);
							if (Scanner.isLogicalOperator(lex)) throw new Exception("Nie mozna uzyc operatora logicznego w przypisaniu do zmiennej typu integer");
							if (Scanner.isReal(lex)) throw new Exception("Nie mozna przypisac real do integer");
							if (Scanner.isChar(lex)) throw new Exception("Nie mozna przypisac char do integer");
							if (Scanner.isString(lex)) throw new Exception("Nie mozna przypisac string do integer");
							if (Scanner.isBoolean(lex)) throw new Exception("Nie mozna przypisac boolean do integer");
							if (Scanner.isIdentifier(lex) && parent.getVariables().containsKey(lex)) { //jest taka zmienna
								String typ = parent.getVariables().get(lex).getType();
								if (typ.equalsIgnoreCase("boolean")) throw new Exception("Nie mozna przypisac boolean do integer");
								if (typ.equalsIgnoreCase("real")) throw new Exception("Nie mozna przypisac real do integer");
								if (typ.equalsIgnoreCase("string")) throw new Exception("Nie mozna przypisac string do integer");
								if (typ.equalsIgnoreCase("char")) throw new Exception("Nie mozna przypisac char do integer");
								
							}
						}
					}
					if (type.equalsIgnoreCase("real")) { //liczba calkowita - nie moze byc dalej real-ow, boolean, char, string 
						for (int h=x;h<temp.size();h++) {
							String lex = temp.get(h);
							if (Scanner.isLogicalOperator(lex)) throw new Exception("Nie mozna uzyc operatora logicznego w przypisaniu do zmiennej typu real");
							if (lex.equalsIgnoreCase("mod")) throw new Exception("Operacja mod niedozwolona dla typu real");
							if (lex.equalsIgnoreCase("div")) throw new Exception("Operacja div niedozwolona dla typu real");
							if (Scanner.isChar(lex)) throw new Exception("Nie mozna przypisac char do real");
							if (Scanner.isString(lex)) throw new Exception("Nie mozna przypisac string do real");
							if (Scanner.isBoolean(lex)) throw new Exception("Nie mozna przypisac boolean do real");
							if (Scanner.isIdentifier(lex) && parent.getVariables().containsKey(lex)) { //jest taka zmienna
								String typ = parent.getVariables().get(lex).getType();
								if (typ.equalsIgnoreCase("boolean")) throw new Exception("Nie mozna przypisac boolean do real");
								if (typ.equalsIgnoreCase("string")) throw new Exception("Nie mozna przypisac string do real");
								if (typ.equalsIgnoreCase("char")) throw new Exception("Nie mozna przypisac char do real");
								
							}
						}
					}
					
					if (type.equalsIgnoreCase("string")) { //liczba calkowita - nie moze byc dalej real-ow, boolean, char, string 
						for (int h=x;h<temp.size();h++) {
							String lex = temp.get(h);
							if (Scanner.isLogicalOperator(lex)) throw new Exception("Nie mozna uzyc operatora logicznego w przypisaniu do zmiennej typu string");
							if (lex.equalsIgnoreCase("mod")) throw new Exception("Operacja mod niedozwolona dla typu string");
							if (lex.equalsIgnoreCase("div")) throw new Exception("Operacja div niedozwolona dla typu string");
							if (Scanner.isBoolean(lex)) throw new Exception("Nie mozna przypisac boolean do string");
							if (Scanner.isIdentifier(lex) && parent.getVariables().containsKey(lex)) { //jest taka zmienna
								String typ = parent.getVariables().get(lex).getType();
								if (typ.equalsIgnoreCase("boolean")) throw new Exception("Nie mozna przypisac boolean do string");
								if (typ.equalsIgnoreCase("integer")) throw new Exception("Nie mozna przypisac integer do string");
								if (typ.equalsIgnoreCase("real")) throw new Exception("Nie mozna przypisac real do real");
								
							}
						}
					}
					
					if (type.equalsIgnoreCase("char")) { //liczba calkowita - nie moze byc dalej real-ow, boolean, char, string 
						for (int h=x;h<temp.size();h++) {
							String lex = temp.get(h);
							if (lex.equals("*") || lex.equals("/")) throw new Exception("Nie mozna uzyc operatora arytmetycznego w przypisaniu do zmiennej typu char");
							if (lex.equalsIgnoreCase("mod")) throw new Exception("Operacja mod niedozwolona dla typu char");
							if (lex.equalsIgnoreCase("div")) throw new Exception("Operacja div niedozwolona dla typu char");
							if (Scanner.isBoolean(lex)) throw new Exception("Nie mozna przypisac boolean do char");
							if (Scanner.isIdentifier(lex) && parent.getVariables().containsKey(lex)) { //jest taka zmienna
								String typ = parent.getVariables().get(lex).getType();
								if (typ.equalsIgnoreCase("boolean")) throw new Exception("Nie mozna przypisac boolean do char");
								if (typ.equalsIgnoreCase("integer")) throw new Exception("Nie mozna przypisac integer do char");
								if (typ.equalsIgnoreCase("real")) throw new Exception("Nie mozna przypisac real do char");
								if (typ.equalsIgnoreCase("string")) throw new Exception("Nie mozna przypisac string do char");
								
							}
						}
					}
					if (type.equalsIgnoreCase("boolean")) { //liczba calkowita - nie moze byc dalej real-ow, boolean, char, string 
				             //do boolean mozna przypisac tylko true lub false albo inna zmienna boolowska
						String lex = temp.get(x+2); //pobieramy to co przypisujemy
						boolean bool = false;
						try {
							String typ = parent.getVariables().get(lex).getType();
							if (!typ.equalsIgnoreCase("boolean")) throw new Exception("Nie mozna przypisac typu "+typ+" do boolean");
								
						}catch (Exception e){
							if (!Scanner.isBoolean(lex)) throw new Exception("Nie mozna przypisac "+lex+" do boolean");
						}
					}
					
					
			
				}
				}
				//System.out.println("dalej");
				continue;
			}
			
		} else { //brak zmiennych
			//System.out.println(parent.getPosOfLastVariable()); = 0 - brak zmiennych
		}
	}
	
	/**
	 * Sprawdza czy poprawna instrukcja if 
	 * @param pos - pozycja na liscie lexemow
	 */
	public void ifInstruction(int pos) throws Exception {
		
	}
	/**
	 * Sprawdza czy poprawna instrukcja przypisania
	 * @param pos - pozycja na liscie lexemow
	 * @throws Exception
	 */
	public void assignInstruction(int pos) throws Exception {
	
	}
	/*
	 * Sprawdza czy poprawna instrukcja petli
	 */
	public void loopInstruction(int pos) throws Exception {
		
	}
	/**
	 * czy poprawna instrukcja arytmetyczna
	 * @param pos
	 * @throws Exception
	 */
	public void arithmeticalInstruction(int pos) throws Exception {
		
	}
	/**
	 * Czy poprawna instrukcja logiczna
	 * @param pos
	 * @throws Exception
	 */
	public void logicalInstruction(int from,int to,List<String> list) throws Exception {
	//	System.out.println("islogical: "+list.subList(from,to));
	 boolean found = false;
	 if (from-to==-1) {
		 
		 try{
		 if (!parent.getVariables().get(list.get(from)).getType().equals("boolean")) {
			 throw new Exception("Wyrazenie musi byc typu logicznego");
		 }
		 }catch(Exception e){
			 throw new Exception("Brak zmiennej logicznej "+list.get(from));
		 }
	 }
	 if (to-from>=2){
		for (int i=from;i<to;i++) {
			String s = list.get(i);
			if (s.equalsIgnoreCase("and") || s.equalsIgnoreCase("or") || s.equalsIgnoreCase("=")
					|| s.equalsIgnoreCase(">") || s.equalsIgnoreCase("<") || s.equalsIgnoreCase(">=") 
					|| s.equalsIgnoreCase("<=") || s.equalsIgnoreCase("<>"))
				found = true;
			
			
		}
		
		
		if (!found) 
			throw new Exception("Brak operatora logicznego");
		
//		sprawdzenie czy miedzy dwa operandy obok siebie sa tego samego typu
		for (int i=from+1;i<to-1;i++) {
			String s = list.get(i);
			if (Scanner.isArithemiticalExpression(s) || Scanner.isLogicalOperator(s)) {
				String a = list.get(i-1);
				String b = list.get(i+1);
				if (Scanner.isIdentifier(a) && Scanner.isIdentifier(b)) {
					try {
						String t1 = parent.getVariables().get(a).getType();
						String t2 = parent.getVariables().get(b).getType();
						if (!t1.equals(t2))
							throw new Exception("Operandy nie posiadaja tych samych typow");
					}catch(Exception e){}
				}
			}
		}
	 }
	}
	
	

}
