/*
 *    Fernflower - The Analytical Java Decompiler
 *    http://www.reversed-java.com
 *
 *    (C) 2008 - 2010, Stiver
 *
 *    This software is NEITHER public domain NOR free software 
 *    as per GNU License. See license.txt for more details.
 *
 *    This software is distributed WITHOUT ANY WARRANTY; without 
 *    even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 *    A PARTICULAR PURPOSE. 
 */

package de.fernflower.modules.decompiler.stats;

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

import de.fernflower.main.DecompilerContext;
import de.fernflower.modules.decompiler.DecHelper;
import de.fernflower.modules.decompiler.ExprProcessor;
import de.fernflower.modules.decompiler.StatEdge;
import de.fernflower.modules.decompiler.exps.Exprent;
import de.fernflower.modules.decompiler.exps.IfExprent;
import de.fernflower.util.InterpreterUtil;


public class IfStatement extends Statement {

	public static int IFTYPE_IF = 0;
	public static int IFTYPE_IFELSE = 1;
	
	public int iftype; 
	
	// *****************************************************************************
	// private fields
	// *****************************************************************************
	
	private Statement ifstat;
	private Statement elsestat;

	private StatEdge ifedge;
	private StatEdge elseedge;
	
	private boolean negated = false;
	
	private boolean iffflag;
	
	private List<Exprent> headexprent = new ArrayList<Exprent>(); // contains IfExprent
	
	// *****************************************************************************
	// constructors
	// *****************************************************************************
	
	private IfStatement() {
		type = TYPE_IF;
		
		headexprent.add(null);
	}
	
	private IfStatement(Statement head, int regedges, Statement postst) {

		this();
		
		first = head;
		stats.addWithKey(head, head.id);

		List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL);
		
		switch(regedges) {
		case 0:
			ifstat = null;
			elsestat = null;
			
			break;
		case 1:
			ifstat = null;
			elsestat = null;

			StatEdge edgeif = lstHeadSuccs.get(1);
			if(edgeif.getType() != StatEdge.TYPE_REGULAR) {
				post = lstHeadSuccs.get(0).getDestination(); 
			} else {
				post = edgeif.getDestination(); 
				negated = true;
			}
			break;
		case 2:
			elsestat = lstHeadSuccs.get(0).getDestination(); 
			ifstat = lstHeadSuccs.get(1).getDestination();
			
			List<StatEdge> lstSucc = ifstat.getSuccessorEdges(StatEdge.TYPE_REGULAR); 
			List<StatEdge> lstSucc1 = elsestat.getSuccessorEdges(StatEdge.TYPE_REGULAR);

			if(ifstat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size()>1 || lstSucc.size()>1) {
				post = ifstat;
			} else if(elsestat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size()>1 || lstSucc1.size()>1) {
				post = elsestat;
			} else {
				if(lstSucc.size() == 0){
					post = elsestat;
				} else if(lstSucc1.size() == 0){
					post = ifstat;
				}
			}

			if(ifstat == post) {
				if(elsestat != post) {
					ifstat = elsestat;
					negated = true;
				} else {
					ifstat = null;
				}
				elsestat = null;
			} else if(elsestat == post) {
				elsestat = null;
			}  else {
				post = postst;
			}
			
			if(elsestat == null) {
				regedges = 1;  // if without else
			}
		}

		ifedge = lstHeadSuccs.get(negated?0:1);
		elseedge = (regedges == 2)?lstHeadSuccs.get(negated?1:0):null;
		
		iftype = (regedges == 2)?IFTYPE_IFELSE:IFTYPE_IF;
		
		if(iftype == IFTYPE_IF) {
			if(regedges == 0) {
				StatEdge edge = lstHeadSuccs.get(0);
				head.removeSuccessor(edge);
				edge.setSource(this);
				this.addSuccessor(edge);
			} else if(regedges == 1) {
				StatEdge edge = lstHeadSuccs.get(negated?1:0);
				head.removeSuccessor(edge);
			}
		}
		
		if(ifstat != null) {
			stats.addWithKey(ifstat, ifstat.id);
		}
		
		if(elsestat != null) {
			stats.addWithKey(elsestat, elsestat.id);
		}

		if(post == head) {
			post = this;
		}

	}
	

	// *****************************************************************************
	// public methods
	// *****************************************************************************
	
	public static Statement isHead(Statement head) {
		
		if(head.type == TYPE_BASICBLOCK && head.getLastBasicType() == LASTBASICTYPE_IF) {
			int regsize = head.getSuccessorEdges(StatEdge.TYPE_REGULAR).size();
			
			Statement p = null;
			
			boolean ok = (regsize < 2);
			if(!ok) {
				List<Statement> lst = new ArrayList<Statement>();
				if(DecHelper.isChoiceStatement(head, lst)) {
					p = lst.remove(0);

					for(Statement st : lst) {
						if(st.isMonitorEnter()) {
							return null;
						}
					}
					
					ok = DecHelper.checkStatementExceptions(lst);
				}
			}
  
			if(ok) {
				return new IfStatement(head, regsize, p);
			}
		}

		return null;
	}

	public String toJava(int indent) {
		String indstr = InterpreterUtil.getIndentString(indent);
		StringBuffer buf = new StringBuffer();
		
		String new_line_separator = DecompilerContext.getNewLineSeparator();
		
		buf.append(ExprProcessor.listToJava(varDefinitions, indent));
		buf.append(first.toJava(indent));
		
		if(isLabeled()) {
			buf.append(indstr+"label"+this.id+":" + new_line_separator);
		}
		
		buf.append(indstr+headexprent.get(0).toJava(indent)+" {" + new_line_separator);
		
		if(ifstat==null) {
			buf.append(InterpreterUtil.getIndentString(indent+1));
			
			if(ifedge.explicit) {
				if(ifedge.getType() == StatEdge.TYPE_BREAK) {
					// break
					buf.append("break");
				} else {
					// continue
					buf.append("continue");
				}
				
				if(ifedge.labeled) {
					buf.append(" label"+ifedge.closure.id);
				} 
			}
			buf.append(";" + new_line_separator);
		} else {
			buf.append(ExprProcessor.jmpWrapper(ifstat, indent+1, true));
		}
		
		boolean elseif = false;
		
		if(elsestat != null) {
			if(elsestat.type == Statement.TYPE_IF
					&& elsestat.varDefinitions.isEmpty() && elsestat.getFirst().getExprents().isEmpty() && 
					!elsestat.isLabeled() && 
					(elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).isEmpty() 
							|| !elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).get(0).explicit)) { // else if
				String content = ExprProcessor.jmpWrapper(elsestat, indent, false);
				content = content.substring(indstr.length());
				
				buf.append(indstr+"} else ");
				buf.append(content);
				
				elseif = true;
			} else {
				String content = ExprProcessor.jmpWrapper(elsestat, indent+1, false);
				
				if(content.length() > 0) {
					buf.append(indstr+"} else {" + new_line_separator);
					buf.append(content);
				}
			}
		}
		
		if(!elseif) {
			buf.append(indstr+"}" + new_line_separator);
		}
		
		return buf.toString();
	}
	
	public void initExprents() {
		
		IfExprent ifexpr = (IfExprent)first.getExprents().remove(first.getExprents().size()-1);
		
		if(negated) {
			ifexpr = (IfExprent)ifexpr.copy();
			ifexpr.negateIf();
		}
		
		headexprent.set(0, ifexpr);
	}
	
	public List<Object> getSequentialObjects() {

		List<Object> lst = new ArrayList<Object>(stats);
		lst.add(1, headexprent.get(0));
		
		return lst;
	}
	
	public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
		if(headexprent.get(0) == oldexpr) {
			headexprent.set(0, newexpr);
		}
	}
	
	public void replaceStatement(Statement oldstat, Statement newstat) {

		super.replaceStatement(oldstat, newstat);
		
		if(ifstat == oldstat) {
			ifstat = newstat;
		}

		if(elsestat == oldstat) {
			elsestat = newstat;
		}

		List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL);
		
		if(iftype == IFTYPE_IF) {
			ifedge = lstSuccs.get(0);
			elseedge = null;
		} else {
			StatEdge edge0 = lstSuccs.get(0);
			StatEdge edge1 = lstSuccs.get(1);
			if(edge0.getDestination() == ifstat) {
				ifedge = edge0;
				elseedge = edge1;
			} else {
				ifedge = edge1;
				elseedge = edge0;
			}
		}
	}
	
	public Statement getSimpleCopy() {
		
		IfStatement is = new IfStatement();
		is.iftype = this.iftype;
		is.negated = this.negated;
		is.iffflag = this.iffflag;
		
		return is;
	}
	
	public void initSimpleCopy() {
		
		first = stats.get(0);
		
		List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL);
		ifedge = lstSuccs.get((iftype == IFTYPE_IF || negated)?0:1);
		if(stats.size() > 1) {
			ifstat = stats.get(1);
		}
		
		if(iftype == IFTYPE_IFELSE) {
			elseedge = lstSuccs.get(negated?1:0);
			elsestat = stats.get(2);
		}
		
	}
	
	// *****************************************************************************
	// getter and setter methods
	// *****************************************************************************
	
	public Statement getElsestat() {
		return elsestat;
	}

	public void setElsestat(Statement elsestat) {
		this.elsestat = elsestat;
	}

	public Statement getIfstat() {
		return ifstat;
	}

	public void setIfstat(Statement ifstat) {
		this.ifstat = ifstat;
	}

	public boolean isNegated() {
		return negated;
	}

	public void setNegated(boolean negated) {
		this.negated = negated;
	}

	public List<Exprent> getHeadexprentList() {
		return headexprent;
	}
	
	public IfExprent getHeadexprent() {
		return (IfExprent)headexprent.get(0);
	}

	public boolean isIffflag() {
		return iffflag;
	}

	public void setIffflag(boolean iffflag) {
		this.iffflag = iffflag;
	}

	public void setElseEdge(StatEdge elseedge) {
		this.elseedge = elseedge;
	}

	public void setIfEdge(StatEdge ifedge) {
		this.ifedge = ifedge;
	}

	public StatEdge getIfEdge() {
		return ifedge;
	}

	public StatEdge getElseEdge() {
		return elseedge;
	}
}
