package soot.dava.toolkits.base.AST.transformations;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2005 Nomair A. Naeem
 * %%
 * This program 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 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */

import java.util.Iterator;
import java.util.List;

import soot.G;
import soot.Singletons;
import soot.dava.internal.AST.ASTLabeledNode;
import soot.dava.internal.AST.ASTNode;
import soot.dava.internal.AST.ASTStatementSequenceNode;
import soot.dava.internal.AST.ASTTryNode;
import soot.dava.internal.SET.SETNodeLabel;
import soot.dava.internal.asg.AugmentedStmt;
import soot.dava.internal.javaRep.DAbruptStmt;
import soot.jimple.Stmt;

public class UselessLabelFinder {
  public static boolean DEBUG = false;

  public UselessLabelFinder(Singletons.Global g) {
  }

  public static UselessLabelFinder v() {
    return G.v().soot_dava_toolkits_base_AST_transformations_UselessLabelFinder();
  }

  // check whether label on a node is useless
  public boolean findAndKill(ASTNode node) {
    if (!(node instanceof ASTLabeledNode)) {
      if (DEBUG) {
        System.out.println("Returning from findAndKill for node of type " + node.getClass());
      }
      return false;
    } else {
      if (DEBUG) {
        System.out.println("FindAndKill continuing for node fo type" + node.getClass());
      }
    }

    String label = ((ASTLabeledNode) node).get_Label().toString();
    if (label == null) {
      return false;
    }
    if (DEBUG) {
      System.out.println("dealing with labeled node" + label);
    }

    List<Object> subBodies = node.get_SubBodies();
    Iterator<Object> it = subBodies.iterator();
    while (it.hasNext()) {
      List subBodyTemp = null;

      if (node instanceof ASTTryNode) {
        // an astTryNode
        ASTTryNode.container subBody = (ASTTryNode.container) it.next();
        subBodyTemp = (List) subBody.o;
        // System.out.println("\ntryNode body");
      } else {
        // not an astTryNode
        // System.out.println("not try node in findAndkill");
        subBodyTemp = (List) it.next();
      }

      if (checkForBreak(subBodyTemp, label)) {
        // found a break
        return false;
      }
    }

    // only if all bodies dont contain a break can we remove the label

    // means break was not found so we can remove
    ((ASTLabeledNode) node).set_Label(new SETNodeLabel());
    if (DEBUG) {
      System.out.println("USELESS LABEL DETECTED");
    }
    return true;
  }

  /*
   * Returns True if finds a break for this label
   */
  private boolean checkForBreak(List ASTNodeBody, String outerLabel) {
    // if(DEBUG)
    // System.out.println("method checkForBreak..... label is "+outerLabel);
    Iterator it = ASTNodeBody.iterator();
    while (it.hasNext()) {
      ASTNode temp = (ASTNode) it.next();
      // check if this is ASTStatementSequenceNode
      if (temp instanceof ASTStatementSequenceNode) {
        // if(DEBUG) System.out.println("Stmt seq Node");
        ASTStatementSequenceNode stmtSeq = (ASTStatementSequenceNode) temp;
        for (AugmentedStmt as : stmtSeq.getStatements()) {
          Stmt s = as.get_Stmt();
          String labelBroken = breaksLabel(s);
          if (labelBroken != null && outerLabel != null) {
            // stmt breaks some label
            if (labelBroken.compareTo(outerLabel) == 0) {
              // we have found a break breaking this label
              return true;
            }
          }
        }
      } // if it was a StmtSeq node
      else {
        // otherwise recursion
        // getSubBodies
        // if(DEBUG) System.out.println("Not Stmt seq Node");
        List<Object> subBodies = temp.get_SubBodies();
        Iterator<Object> subIt = subBodies.iterator();
        while (subIt.hasNext()) {
          List subBodyTemp = null;
          if (temp instanceof ASTTryNode) {
            ASTTryNode.container subBody = (ASTTryNode.container) subIt.next();
            subBodyTemp = (List) subBody.o;
            // System.out.println("Try body node");
          } else {
            subBodyTemp = (List) subIt.next();
          }

          if (checkForBreak(subBodyTemp, outerLabel)) {
            // if this is true there was a break found
            return true;
          }
        }
      }
    }

    return false;
  }

  /*
   * If the stmt is a break/continue stmt then this method returns the labels name else returns null
   */
  private String breaksLabel(Stmt stmt) {
    if (!(stmt instanceof DAbruptStmt)) {
      // this is not a break stmt
      return null;
    }
    DAbruptStmt abStmt = (DAbruptStmt) stmt;
    if (abStmt.is_Break() || abStmt.is_Continue()) {
      SETNodeLabel label = abStmt.getLabel();
      return label.toString();
    } else {
      return null;
    }
  }
}