/*
	Copyright (c) 2009-2011 Olivier Chafik, All Rights Reserved
	
	This file is part of JNAerator (http://jnaerator.googlecode.com/).
	
	JNAerator is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	JNAerator is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with JNAerator.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.ochafik.lang.jnaerator.parser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public abstract class Statement extends ModifiableElement { // HACK : statement is not modifiable ! it's just so that Declaration is a Statement and a ModifiableElement (damned single inheritance !)
    @Override
	public Statement clone() {
        return (Statement)super.clone();
    }
	public static class Throw extends Statement {
		Expression expression;
		public Expression getExpression() {
			return expression;
		}
		public void setExpression(Expression expression) {
			this.expression = changeValue(this, this.expression, expression);
		}
		public Throw() {}
		public Throw(Expression expression) {
			setExpression(expression);
		}
		@Override
		public void accept(Visitor visitor) {
			visitor.visitThrow(this);
		}

		@Override
		public boolean replaceChild(Element child, Element by) {
			if (child == getExpression()) {
				setExpression((Expression)by);
				return true;
			}
			return super.replaceChild(child, by);
		}

	}
    /*
	public static class DeclarationStatement extends Statement {
		Declaration declaration;
		public void setDeclaration(Declaration declaration) {
			this.declaration = changeValue(this, this.declaration, declaration);
		}
		public Declaration getDeclaration() {
			return declaration;
		}
		public DeclarationStatement() {}
		public DeclarationStatement(Declaration declaration) {
			setDeclaration(declaration);
		}
		@Override
		public void accept(Visitor visitor) {
			visitor.visitDeclarationStatement(this);
		}
		@Override
		public Element getNextChild(Element child) {
			return null;
		}
		@Override
		public Element getPreviousChild(Element child) {
			return null;
		}
		@Override
		public boolean replaceChild(Element child, Element by) {
			if (child == getDeclaration()) {
				setDeclaration((Declaration)by);
				return true;
			}
			return false;
		}
	}*/
	public static abstract class SingleValueStatement extends Statement {
		Expression value;
		
		public SingleValueStatement() {}
		public SingleValueStatement(Expression value) {
			setValue(value);
		}

		public void setValue(Expression value) {
			this.value = changeValue(this, this.value, value);
		}
		public Expression getValue() {
			return value;
		}

		@Override
		public Element getNextChild(Element child) {
			Element e = super.getNextChild(child);
			if (e != null)
				return e;
			return null;
		}

		@Override
		public Element getPreviousChild(Element child) {
			Element e = super.getPreviousChild(child);
			if (e != null)
				return e;
			return null;
		}

		@Override
		public boolean replaceChild(Element child, Element by) {
			if (child == getValue()) {
				setValue((Expression)by);
				return true;
			}
			return super.replaceChild(child, by);
		}

	}
    public static class Return extends SingleValueStatement {
		public Return() {}
		public Return(Expression value) {
			super(value);
		}
		@Override
		public void accept(Visitor visitor) {
			visitor.visitReturn(this);
		}
    }
	public static class Delete extends SingleValueStatement {
        boolean array;
		public Delete() {}
		public Delete(Expression value, boolean isArray) {
			super(value);
            setArray(isArray);
		}

        public boolean isArray() {
            return array;
        }

        public void setArray(boolean isArray) {
            this.array = isArray;
        }
        
		@Override
		public void accept(Visitor visitor) {
			visitor.visitDelete(this);
		}
    }
	public static class If extends Statement {
		public If() {}
		public If(Expression condition, Statement thenBranch) {
                    this(condition, thenBranch, null);
                }
		public If(Expression condition, Statement thenBranch, Statement elseBranch) {
			setCondition(condition);
			setThenBranch(thenBranch);
			setElseBranch(elseBranch);
		}
		Expression condition;
		Statement thenBranch, elseBranch;
		
		public void setElseBranch(Statement elseBranch) {
			this.elseBranch = changeValue(this, this.elseBranch, elseBranch);
		}
		public void setCondition(Expression condition) {
			this.condition = changeValue(this, this.condition, condition);
		}
		public void setThenBranch(Statement thenBranch) {
			this.thenBranch = changeValue(this, this.thenBranch, thenBranch);
		}
		public Statement getElseBranch() {
			return elseBranch;
		}
		public Statement getThenBranch() {
			return thenBranch;
		}
		public Expression getCondition() {
			return condition;
		}
		@Override
		public void accept(Visitor visitor) {
			visitor.visitIf(this);
		}
		@Override
		public boolean replaceChild(Element child, Element by) {
			if (child == getCondition()) {
				setCondition((Expression)by);
				return true;
			}
			if (child == getThenBranch()) {
				setThenBranch((Statement)by);
				return true;
			}
			if (child == getElseBranch()) {
				setElseBranch((Statement)by);
				return true;
			}
			return super.replaceChild(child, by);
		}
	}
	
	public static class For extends Statement {
		public For() {}
		public For(List<Statement> initStatements, Expression condition, List<Statement> postStatements, Statement body) {
			setInitStatements(initStatements);
			setCondition(condition);
			setPostStatements(postStatements);
			setBody(body);
		}
		Expression condition;
		Statement body;
		final List<Statement> initStatements = new ArrayList<Statement>(), postStatements = new ArrayList<Statement>();
		
		public void setPostStatements(List<Statement> postStatements) {
			changeValue(this, this.postStatements, postStatements);
		}
		public List<Statement> getPostStatements() {
			return unmodifiableList(postStatements);
		}
		public void setInitStatements(List<Statement> initStatements) {
			changeValue(this, this.initStatements, initStatements);
		}
		public List<Statement> getInitStatements() {
			return unmodifiableList(initStatements);
		}
		
		public void setCondition(Expression condition) {
			this.condition = changeValue(this, this.condition, condition);
		}
		public void setBody(Statement body) {
			this.body = changeValue(this, this.body, body);
		}
		public Statement getBody() {
			return body;
		}
		public Expression getCondition() {
			return condition;
		}
		@Override
		public void accept(Visitor visitor) {
			visitor.visitFor(this);
		}
		@Override
		public Element getNextChild(Element child) {
			Element e = super.getNextChild(child);
			if (e != null)
				return e;
			e = getNextSibling(initStatements, child);
			if (e != null)
				return e;
			return getNextSibling(postStatements, child);
		}
		@Override
		public Element getPreviousChild(Element child) {
			Element e = super.getPreviousChild(child);
			if (e != null)
				return e;
			e = getPreviousSibling(initStatements, child);
			if (e != null)
				return e;
			return getPreviousSibling(postStatements, child);
		}
		@Override
		public boolean replaceChild(Element child, Element by) {
			if (child == getCondition()) {
				setCondition((Expression)by);
				return true;
			}
			if (child == getBody()) {
				setBody((Statement)by);
				return true;
			}
			return 
				replaceChild(initStatements, Statement.class, this, child, by) ||
				replaceChild(postStatements, Statement.class, this, child, by) ||
                super.replaceChild(child, by);
		}
	}
    
	public static class While extends Statement {
		public While() {}
		public While(Expression condition, Statement body) {
			setCondition(condition);
			setBody(body);
		}
		Expression condition;
		Statement body;
		
		public void setCondition(Expression condition) {
			this.condition = changeValue(this, this.condition, condition);
		}
		public void setBody(Statement body) {
			this.body = changeValue(this, this.body, body);
		}
		public Statement getBody() {
			return body;
		}
		public Expression getCondition() {
			return condition;
		}
		@Override
		public void accept(Visitor visitor) {
			visitor.visitWhile(this);
		}
		@Override
		public boolean replaceChild(Element child, Element by) {
			if (child == getCondition()) {
				setCondition((Expression)by);
				return true;
			}
			if (child == getBody()) {
				setBody((Statement)by);
				return true;
			}
			return super.replaceChild(child, by);
		}
	}
    public static class DoWhile extends While {
        public DoWhile() {}
		public DoWhile(Expression condition, Statement body) {
            super(condition, body);
		}
		
		@Override
		public void accept(Visitor visitor) {
			visitor.visitDoWhile(this);
		}
    }
	public static class ExpressionStatement extends Statement {

		public ExpressionStatement() {}
		public ExpressionStatement(Expression expression) {
			setExpression(expression);
		}

		Expression expression;
		
		public Expression getExpression() {
			return expression;
		}
		public void setExpression(Expression expression) {
			this.expression = changeValue(this, this.expression, expression);
		}
		
		@Override
		public void accept(Visitor visitor) {
			visitor.visitExpressionStatement(this);
		}

		@Override
		public boolean replaceChild(Element child, Element by) {
			if (getExpression() == child) {
				setExpression((Expression)by);
				return true;
			}
			return super.replaceChild(child, by);
		}
	}
	
	public static class Block extends Statement {
		final List<Statement> statements = new ArrayList<Statement>();
		@Override
		public void accept(Visitor visitor) {
			visitor.visitBlock(this);
		}

		boolean compact;
		
		@Override
		public Block clone() {
			return (Block)super.clone();
		}
		
		public Block setCompact(boolean compact) {
			this.compact = compact;
			return this;
		}
		public boolean isCompact() {
			return compact;
		}
		public Block() {}
		public Block(Statement... statements) {
			addStatements(statements);
		}
		public Block(List<Statement> statements) {
			setStatements(statements);
		}
		public void addStatement(Statement statement) {
			if (statement != null) {
				statement.setParentElement(this);
				statements.add(statement);
			}
		}
		public void setStatements(List<Statement> statements) {
			changeValue(this, this.statements, statements);
		}
		public void addStatements(Statement... statements) {
			setStatements(Arrays.asList(statements));
		}
		public List<Statement> getStatements() {
			return unmodifiableList(statements);
		}
		@Override
		public Element getNextChild(Element child) {
			Element e = super.getNextChild(child);
			if (e != null)
				return e;
			return getNextSibling(statements, child);
		}

		@Override
		public Element getPreviousChild(Element child) {
			Element e = super.getPreviousChild(child);
			if (e != null)
				return e;
			return getPreviousSibling(statements, child);
		}

		@Override
		public boolean replaceChild(Element child, Element by) {
			return replaceChild(statements, Statement.class, this, child, by) ||
                super.replaceChild(child, by);
		}
	}

    public static class Catch extends Statement {
        VariablesDeclaration declaration;
        Statement body;

        public Catch() {}
        public Catch(VariablesDeclaration declaration, Statement body) {
            setDeclaration(declaration);
            setBody(body);
        }

        public VariablesDeclaration getDeclaration() {
            return declaration;
        }

        public void setDeclaration(VariablesDeclaration declaration) {
            this.declaration = changeValue(this, this.declaration, declaration);
        }

        public Statement getBody() {
            return body;
        }

        public void setBody(Statement body) {
            this.body = changeValue(this, this.body, body);
        }

        @Override
        public boolean replaceChild(Element child, Element by) {
            if (child == getDeclaration()) {
                setDeclaration((VariablesDeclaration)by);
                return true;
            }
            if (child == getBody()) {
                setBody((Statement)by);
                return true;
            }
            return super.replaceChild(child, by);
        }

        @Override
        public void accept(Visitor visitor) {
            visitor.visitCatch(this);
        }


    }
    public static class Try extends Statement {
        Statement tryStatement, finallyStatement;
        final List<Catch> catches = new ArrayList<Catch>();

        public Try() {}
        public Try(Statement tryStatement, Statement finallyStatement, Catch... catches) {
            setTryStatement(tryStatement);
            setFinallyStatement(finallyStatement);
            setCatches(Arrays.asList(catches));
        }

        public void setTryStatement(Statement tryStatement) {
            this.tryStatement = changeValue(this, this.tryStatement, tryStatement);
        }

        public Statement getTryStatement() {
            return tryStatement;
        }

        public void setFinallyStatement(Statement finallyStatement) {
            this.finallyStatement = changeValue(this, this.finallyStatement, finallyStatement);
        }

        public Statement getFinallyStatement() {
            return finallyStatement;
        }

        public List<Catch> getCatches() {
            return Collections.unmodifiableList(catches);
        }

        public void setCatches(List<Catch> catches) {
            changeValue(this, this.catches, catches);
        }

        @Override
        public boolean replaceChild(Element child, Element by) {
            if (child == getTryStatement()) {
				setTryStatement((Statement)by);
				return true;
			}
			if (child == getFinallyStatement()) {
				setFinallyStatement((Statement)by);
				return true;
			}
            return replaceChild(catches, Catch.class, this, child, by) ||
                super.replaceChild(child, by);
        }

        @Override
        public void accept(Visitor visitor) {
            visitor.visitTry(this);
        }

        

    }
}
