/*
 * Syntax.java
 *
 * Created on November 12, 2005, 1:41 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */

package cz.pcland.functions.syntax;

/**
 *
 * @author petr
 */
import cz.pcland.functions.nodes.*;
import cz.pcland.functions.lexan.*;
import cz.pcland.functions.syntax.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;

public class Syntax extends Thread {
    Lexan lex;
    DefaultListModel dlmInfo;
    //DefaultListModel dlmErrors;
    DefaultListModel dlmExpr;
    /** Creates a new instance of Syntax */
    Token currentToken;
    boolean error = false;
    int tokenNumber;
    
    
    
    public Syntax() {
        tokenNumber = 0;
        /*
        String str = "SIMPLIFY((a^((222+254!)^2))^a*a+SIN(x)/(a+b)^2545+c!)";
        String str2 = "SIMPLIFY(a^(222)++-254*a!*(a)-++(SIN(x)/(a+-+b))^2545+c!)";
        String str3 = "sin(cos(a+b!)+a+b!+3!*(a+b)!+2*a-2*a+++cos(a+b)+++a/b+b)";
        String str4 = "evaluate(((a+b+a*b+sin(a+b)*3!)^(a+b))^2/2+1/2, a=1, b=2)";
        String str5 = "simplify(a+evaluate(a+b+expand(simplify(a+b)),a=5,b=6)!!! + a! + expand((a+a)/b)^expand(2)+a^(a!+b)!)";
        String str6 = "a+b!+c!";
        String str7 ="a-(b-(c-((d-e))))/(a+b)*2/(3/4)/5/6/7/8+2+3+4+(6+7)+9";    
         */
    }
    
    public Node startAnalysis(String exprString, DefaultListModel dlmInfo, DefaultListModel dlmExpr) throws ParseErrorException{
        lex = new Lexan(exprString);
        this.dlmInfo = dlmInfo;
        this.dlmExpr = dlmExpr;
        
        currentToken = lex.readToken(); //read firt token
        tokenNumber = 1;
        
        Node s = E(); //start syntax analysis
        
                
        if(currentToken.getType() == TokenType.TOKEN_END && !error) { //check the result :)
            String resStr = s.print(Priorities.LOWESTPRIORITY);
            dlmInfo.add(0, "Input is OK");
            dlmInfo.add(0, resStr + " ACCEPTED AS AN INPUT");
        }else {
            dlmInfo.add(0, "Input is NOT OK :(");
            throw new ParseErrorException("Unexpected end of input :(");
        }
        
        return s;
    }
    
    void match(Token awaitedToken) throws ParseErrorException {
        boolean ok = true;
        if(awaitedToken.getType() != currentToken.getType()) {
            ok = false;
        }
        
        if(ok) {
            dlmInfo.add(0, "matching token: " + awaitedToken.print());
            currentToken = lex.readToken();
            tokenNumber++;
        }else {            
            dlmInfo.add(0, "Unexpected input character: " + "expected token: "  + awaitedToken.print() + ", real token : "  + currentToken.print());
            error = true;
            throw new ParseErrorException("Unexpected input character !" + " Expected token: "  + awaitedToken.print() + ", real token : "  + currentToken.print());
            
        }
    }
    
    
    
    String matchVariable() throws ParseErrorException { //todo: ehmmmm
        if(currentToken.getType() == TokenType.TOKEN_VARIABLE) {
            String varName = ((VariableToken)currentToken).getValue();
            currentToken = lex.readToken();    
            tokenNumber++;
            return varName;  
        }else{
            error = true;
            dlmInfo.add(0, "Awaited variable ... :( ");
            throw new ParseErrorException("Awaited variable ... :( ");
        }
    }
    
    int matchNumber() throws ParseErrorException { //todo: ehmmmm
        if(currentToken.getType() == TokenType.TOKEN_NUMBER) {
            int num = ((NumberToken)currentToken).getValue();
            currentToken = lex.readToken();    
            dlmInfo.add(0, "matching number ... " + num);
            tokenNumber++;
            return num;  
        }else{
            error = true;
            dlmInfo.add(0, "Awaited number ... :( ");
            throw new ParseErrorException("Awaited number ... :( ");
        }
    }  
    
    String matchString() throws ParseErrorException {
       
        if(currentToken.getType() == TokenType.TOKEN_FUNCTION) {
            String name = ((FunctionToken)currentToken).getValue();
            //System.out.println("MATCHING FUNCTION: " + name);
            dlmInfo.add(0, "MATCHING FUNCTION: " + name);
            currentToken = lex.readToken(); 
            tokenNumber++;
            return name;  
        }
        
        if(currentToken.getType() == TokenType.TOKEN_STRING) {
            String name = ((StringToken)currentToken).getValue();
            dlmInfo.add(0, "MATCHING UNKNOWN STRING: " + name);
            currentToken = lex.readToken();  
            tokenNumber++;
            return name;  
        }
        
            error = true;
            dlmInfo.add(0, "Awaited some keyword, may be function ... :( ");
            throw new ParseErrorException("Awaited some keyword, may be function ... :( ");
    }
    
    Node E() throws ParseErrorException {
        if(currentToken.getType() == TokenType.TOKEN_PLUS ||
                currentToken.getType() == TokenType.TOKEN_MINUS ||
                currentToken.getType() == TokenType.TOKEN_NUMBER ||
                currentToken.getType() == TokenType.TOKEN_VARIABLE ||
                currentToken.getType() == TokenType.TOKEN_FUNCTION ||
                currentToken.getType() == TokenType.TOKEN_OPEN_PARENTHESIS
                ) {
            dlmInfo.add(0, "E->T E1");
            
            return E1(T());
        }else {
            dlmInfo.add(0, "error during E expansion :(" + " I cant expand E in order to get " + currentToken.print() );
            throw new ParseErrorException("Error during E expansion :(" + " I cant expand E in order to get " + currentToken.print());            
        }
    }
    
    Node E1(Node inheritedAtr) throws ParseErrorException {
        if(currentToken.getType() == TokenType.TOKEN_PLUS ) {
         //   System.out.println("E1->+T E1");
            dlmInfo.add(0, "E1->+T E1");
            
            match(new Token(TokenType.TOKEN_PLUS));
            return E1(new AdditionNode(inheritedAtr, T()));
        } else
            
            if(currentToken.getType() == TokenType.TOKEN_MINUS ) {
            dlmInfo.add(0, "E1->-T E1");
            
            match(new Token(TokenType.TOKEN_MINUS));
            return E1(new SubtractionNode(inheritedAtr, T()));
            } else
                
                
                if( currentToken.getType() == TokenType.TOKEN_CLOSE_PARENTHESIS ||
                currentToken.getType() == TokenType.TOKEN_END ||
                currentToken.getType() == TokenType.TOKEN_COMMA
                
                ) {
            
            dlmInfo.add(0, "E1 -> $");
            return inheritedAtr;
                }else {
            dlmInfo.add(0, "error during E1 expansion :(" + " I cant expand E1 in order to get " + currentToken.print() );   
            error = true;
            throw new ParseErrorException("Error during E1 expansion :(" + " I cant expand E1 in order to get " + currentToken.print()); 
                }
    }
    
    Node T() throws ParseErrorException {
        if(currentToken.getType() == TokenType.TOKEN_PLUS ||
                currentToken.getType() == TokenType.TOKEN_MINUS ||
                currentToken.getType() == TokenType.TOKEN_NUMBER ||
                currentToken.getType() == TokenType.TOKEN_VARIABLE ||
               
                currentToken.getType() == TokenType.TOKEN_FUNCTION ||
                currentToken.getType() == TokenType.TOKEN_VARIABLE ||
                currentToken.getType() == TokenType.TOKEN_OPEN_PARENTHESIS
                ) {
            dlmInfo.add(0, "T->M T1");
            return T1(M());
        }else {  
            dlmInfo.add(0, "error during T expansion :(" + " I cant expand T in order to get " + currentToken.print()  );
            error = true;
            throw new ParseErrorException("Error during T expansion :(" + " I cant expand T in order to get " + currentToken.print());             
        }
    }
    
    Node T1(Node inheritedAtr) throws ParseErrorException{
        if(currentToken.getType() == TokenType.TOKEN_PLUS  ||
                currentToken.getType() == TokenType.TOKEN_MINUS ||
                currentToken.getType() == TokenType.TOKEN_CLOSE_PARENTHESIS ||
                currentToken.getType() == TokenType.TOKEN_END ||
                
                currentToken.getType() == TokenType.TOKEN_COMMA
                ) {
            dlmInfo.add(0, "T1 -> $");
            return inheritedAtr;
        } else
            
            if(currentToken.getType() == TokenType.TOKEN_ASTERISK ) {
            match(new Token(TokenType.TOKEN_ASTERISK));
            return T1(new MultiplicationNode(inheritedAtr, M()));
            } else
                
                
         if( currentToken.getType() == TokenType.TOKEN_SLASH )  {
            dlmInfo.add(0, "T1 ->/M T1");
            match(new Token(TokenType.TOKEN_SLASH));
            return T1(new DivisionNode(inheritedAtr, M()));
         }  else {
            error = true;
            throw new ParseErrorException("error during T1 expansion :(" + " I cant expand T1 in order to get " + currentToken.print()); 
         }
    }
    
    Node M() throws ParseErrorException{
        if(currentToken.getType() == TokenType.TOKEN_PLUS ||
                currentToken.getType() == TokenType.TOKEN_MINUS ||
                currentToken.getType() == TokenType.TOKEN_NUMBER ||
                currentToken.getType() == TokenType.TOKEN_VARIABLE ||
                currentToken.getType() == TokenType.TOKEN_OPEN_PARENTHESIS ||
      
                currentToken.getType() == TokenType.TOKEN_FUNCTION ||
                currentToken.getType() == TokenType.TOKEN_VARIABLE
                ) {
            
            dlmInfo.add(0, "M->PF M1");
            return M1(PF());
        }else {
            error = true;
            throw new ParseErrorException("Error during M expansion :(" + " I cant expand M in order to get " + currentToken.print());
        }
    }
    
    Node M1(Node inheritedAtr) throws ParseErrorException {
        if(currentToken.getType() == TokenType.TOKEN_PLUS ||
                currentToken.getType() == TokenType.TOKEN_MINUS ||
                currentToken.getType() == TokenType.TOKEN_ASTERISK ||
                currentToken.getType() == TokenType.TOKEN_SLASH ||
                currentToken.getType() == TokenType.TOKEN_CLOSE_PARENTHESIS ||
                currentToken.getType() == TokenType.TOKEN_END ||
                currentToken.getType() == TokenType.TOKEN_COMMA
                ) {
            dlmInfo.add(0, "M1 -> $");
            return inheritedAtr;
        } else
            if(currentToken.getType() == TokenType.TOKEN_POWER) {
            dlmInfo.add(0, "M1-> ^PF M1");
            match(new Token(TokenType.TOKEN_POWER));
            return M1(new PowerNode(inheritedAtr, PF()));
            } else {
            error = true;
            throw new ParseErrorException("Error during M1 expansion :(" + " I cant expand M1 in order to get " + currentToken.print());
            }
    }
    
    Node PF() throws ParseErrorException{
        if(currentToken.getType() == TokenType.TOKEN_PLUS ) {
            dlmInfo.add(0, "PF->+PF");
            match(new Token(TokenType.TOKEN_PLUS));
            return new UnaryPlus(PF());
        } else
            
            if(currentToken.getType() == TokenType.TOKEN_MINUS ) {
            dlmInfo.add(0, "PF->-PF");
            match(new Token(TokenType.TOKEN_MINUS));
            return new UnaryMinus(PF());
            } else
                
            if(currentToken.getType() == TokenType.TOKEN_NUMBER ) {
            dlmInfo.add(0, "PF->number");

            return new NumberNode( matchNumber() );
            } else
                    
            if(currentToken.getType() == TokenType.TOKEN_VARIABLE ) {
            dlmInfo.add(0, "PF->variable");
            
            return new VariableNode( matchVariable() );
            } else
                        
                        
            if( currentToken.getType() == TokenType.TOKEN_FUNCTION ) {

            dlmInfo.add(0, "PF -> function(E)");
            String functionName = matchString();
            match(new Token(TokenType.TOKEN_OPEN_PARENTHESIS));
            Node ret = null;
                    if(functionName.equalsIgnoreCase("SIN")) ret = new SinNode(E());
                    if(functionName.equalsIgnoreCase("COS")) ret = new CosNode(E());
                    if(functionName.equalsIgnoreCase("SQRT")) ret = new SqrtNode(E());
                    if(functionName.equalsIgnoreCase("LN")) ret = new LnNode(E());
                    if(functionName.equalsIgnoreCase("LOG")) ret = new LogNode(E());
                    if(functionName.equalsIgnoreCase("TG")) ret = new TgNode(E());
                    if(functionName.equalsIgnoreCase("ABS")) ret = new AbsNode(E());
                    if(functionName.equalsIgnoreCase("MODULO")) ret = new ModuloNode(E());
            if(ret == null) {
                error = true;
                throw new ParseErrorException("Unknown string ... :( " + functionName);
            }
                
            match(new Token(TokenType.TOKEN_CLOSE_PARENTHESIS));
            return ret;
            
            } else
                          
            
        
                
            if(currentToken.getType() == TokenType.TOKEN_OPEN_PARENTHESIS ) {
            dlmInfo.add(0,"PF->(E)");
            match(new Token(TokenType.TOKEN_OPEN_PARENTHESIS));
            Node ret = E();
            match(new Token(TokenType.TOKEN_CLOSE_PARENTHESIS));
            return ret;
            } else {
              error = true;
             throw new ParseErrorException("Error during PF expansion :(" + " I cant expand PF in order to get " + currentToken.print());      
            }
    }
    
   
   
   
    
  
}
