package soot.toolkits.graph.pdg;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 1999 - 2010 Hossein Sadat-Mohtasham
 * %%
 * 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.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import soot.SootClass;
import soot.SootMethod;
import soot.Unit;
import soot.toolkits.graph.Block;
import soot.toolkits.graph.UnitGraph;

/**
 * This class was originally designed to represent a weak region. Later, PDGRegion was designed to represent a richer region
 * representation but since there were cases that we wanted to enforce the use of Region instead of PDGRegion, and for some
 * other compatibility issues, we chose not to eliminate this class and even not to factor it into a common abstract class.
 *
 * One major contributing factor to the above is that, a Region can exist independent from a PDG since they are generated by
 * RegionAnalysis, whereas a PDGRegion is generated by a PDG.
 *
 * @author Hossein Sadat-Mohtasham Jan 2009
 */

public class Region implements IRegion {

  private SootClass m_class = null;
  private SootMethod m_method = null;
  private List<Block> m_blocks = null;
  private List<Unit> m_units = null;
  private int m_id = -1;
  private UnitGraph m_unitGraph = null;

  // The following are needed to create a tree of regions based on the containment (dependency)
  // relation between regions.
  private IRegion m_parent = null;
  private List<IRegion> m_children = new ArrayList<IRegion>();

  public Region(int id, SootMethod m, SootClass c, UnitGraph ug) {
    this(id, new ArrayList<Block>(), m, c, ug);

  }

  public Region(int id, List<Block> blocks, SootMethod m, SootClass c, UnitGraph ug) {

    this.m_blocks = blocks;
    this.m_id = id;
    this.m_method = m;
    this.m_class = c;
    this.m_unitGraph = ug;
    this.m_units = null;

  }

  @SuppressWarnings("unchecked")
  public Object clone() {
    Region r = new Region(this.m_id, this.m_method, this.m_class, this.m_unitGraph);
    r.m_blocks = (List<Block>) ((ArrayList<Block>) this.m_blocks).clone();

    return r;

  }

  public SootMethod getSootMethod() {
    return this.m_method;
  }

  public SootClass getSootClass() {
    return this.m_class;
  }

  public List<Block> getBlocks() {
    return this.m_blocks;
  }

  public UnitGraph getUnitGraph() {
    return this.m_unitGraph;
  }

  public List<Unit> getUnits() {
    if (this.m_units == null) {
      this.m_units = new LinkedList<Unit>();
      for (Iterator<Block> itr = this.m_blocks.iterator(); itr.hasNext();) {
        Block b = itr.next();
        for (Iterator<Unit> itr1 = b.iterator(); itr1.hasNext();) {
          Unit u = itr1.next();
          ((LinkedList<Unit>) this.m_units).addLast(u);

        }
      }

    }

    return this.m_units;
  }

  public List<Unit> getUnits(Unit from, Unit to) {

    return m_units.subList(m_units.indexOf(from), m_units.indexOf(to));

  }

  public Unit getLast() {
    if (this.m_units != null) {
      if (this.m_units.size() > 0) {
        return ((LinkedList<Unit>) this.m_units).getLast();
      }
    }

    return null;
  }

  public Unit getFirst() {
    if (this.m_units != null) {
      if (this.m_units.size() > 0) {
        return ((LinkedList<Unit>) this.m_units).getFirst();
      }
    }

    return null;
  }

  public void add(Block b) {
    // Add to the front
    this.m_blocks.add(0, b);

  }

  public void add2Back(Block b) {
    // Add to the last
    this.m_blocks.add(this.m_blocks.size(), b);

  }

  public void remove(Block b) {
    this.m_blocks.remove(b);
    // make the units be recalculated.
    this.m_units = null;
  }

  public int getID() {
    return this.m_id;
  }

  public boolean occursBefore(Unit u1, Unit u2) {
    int i = this.m_units.lastIndexOf(u1);
    int j = this.m_units.lastIndexOf(u2);

    if (i == -1 || j == -1) {
      throw new RuntimeException("These units don't exist in the region!");
    }

    return i < j;
  }

  public void setParent(IRegion pr) {
    this.m_parent = pr;
  }

  public IRegion getParent() {
    return this.m_parent;
  }

  public void addChildRegion(IRegion chr) {
    if (!this.m_children.contains(chr)) {
      this.m_children.add(chr);
    }
  }

  public List<IRegion> getChildRegions() {
    return this.m_children;
  }

  public String toString() {
    String str = new String();
    str += "Begin-----------Region:  " + this.m_id + "-------------\n";

    List<Unit> regionUnits = this.getUnits();
    for (Iterator<Unit> itr = regionUnits.iterator(); itr.hasNext();) {
      Unit u = itr.next();
      str += u + "\n";

    }
    str += "End Region " + this.m_id + " -----------------------------\n";

    return str;

  }

}
