/*
 * Copyright (c) 2009-2012 Panxiaobo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.dex2jar.ir.stmt;

import java.util.Set;
import java.util.TreeSet;

import com.googlecode.dex2jar.ir.ET;
import com.googlecode.dex2jar.ir.LabelAndLocalMapper;
import com.googlecode.dex2jar.ir.expr.Value;

/**
 * Represent a statement
 * 
 * @see ST
 * @see ET
 * 
 * @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
 * @version $Rev$
 */
public abstract class Stmt {

    /**
     * Represent a statement with no argument
     * 
     * @see ET#E0
     */
    public static abstract class E0Stmt extends Stmt {

        public E0Stmt(ST type) {
            super(type, ET.E0);
        }
    }

    /**
     * Represent a statement with 1 argument
     * 
     * @see ET#E1
     */
    public static abstract class E1Stmt extends Stmt {

        public Value op;

        public E1Stmt(ST type, Value op) {
            super(type, ET.E1);
            this.op = op;
        }

        @Override
        public Value getOp() {
            return op;
        }

        public void setOp(Value op) {
            this.op = op;
        }

    }

    /**
     * Represent a statement with 2 arguments
     * 
     * @see ET#E2
     */
    public static abstract class E2Stmt extends Stmt {

        public Value op1;
        public Value op2;

        public E2Stmt(ST type, Value op1, Value op2) {
            super(type, ET.E2);
            this.op1 = op1;
            this.op2 = op2;
        }

        @Override
        public Value getOp1() {
            return op1;
        }

        @Override
        public Value getOp2() {
            return op2;
        }

        public void setOp1(Value op1) {
            this.op1 = op1;
        }

        public void setOp2(Value op2) {
            this.op2 = op2;
        }

    }

    public static final int CAN_CONTINUE = 1 << 0;
    public static final int CAN_BRNANCH = 1 << 1;
    public static final int CAN_SWITCH = 1 << 2;
    public static final int CAN_THROW = 1 << 3;
    public static final int MAY_THROW=1<<4;

    /**
     * Statement Type
     * 
     */
    public static enum ST {

        LOCAL_START(CAN_CONTINUE), // same as ASSIGN but left must keep and must be local
        LOCAL_END(CAN_CONTINUE), // must keep and op must be local
        ASSIGN(CAN_CONTINUE | MAY_THROW), IDENTITY(CAN_CONTINUE), LABEL(CAN_CONTINUE), LOCK(CAN_CONTINUE | CAN_THROW), NOP(
                CAN_CONTINUE), UNLOCK(CAN_CONTINUE | CAN_THROW), VOID_INVOKE(CAN_CONTINUE | CAN_THROW), FILL_ARRAY_DATA(
                CAN_CONTINUE | CAN_THROW), //
        RETURN(MAY_THROW), RETURN_VOID(0), THROW(CAN_THROW), //
        GOTO(CAN_BRNANCH), IF(CAN_CONTINUE | CAN_BRNANCH | MAY_THROW), //
        LOOKUP_SWITCH(CAN_SWITCH | MAY_THROW), TABLE_SWITCH(CAN_SWITCH | MAY_THROW), ;
        private int config;

        ST(int config) {
            this.config = config;
        }

        public boolean canBranch() {
            return 0 != (CAN_BRNANCH & config);
        }

        public boolean canContinue() {
            return 0 != (CAN_CONTINUE & config);
        }

        public boolean canSwitch() {
            return 0 != (CAN_SWITCH & config);
        }

        public boolean mayThrow() {
            return 0 != (MAY_THROW & config);
        }

        public boolean canThrow() {
            return 0 != (CAN_THROW & config);
        }
    }

    /**
     * Used in construct of a method CFG, Previous {@link Stmt} nodes
     */
    public Set<Stmt> _cfg_froms;

    /**
     * Used in construct of a method CFG, After {@link Stmt} nodes
     */
    public Set<LabelStmt> exceptionHandlers;

    /**
     * Used in visit the method CFG
     */
    public boolean visited;

    /**
     * Used in Local Split, forward frame of the {@link Stmt}
     */
    public Object frame;

    public Stmt _ts_default_next;

    /**
     * The number of argument
     */
    public final ET et;
    /**
     * Used in ordering statements in a {@link TreeSet}, id of the {@link Stmt} in its {@link StmtList}
     */
    public int id;

    /**
     * Owner of the statement
     */
    /* default */
    StmtList list;

    /**
     * Next statement in {@link StmtList}
     */
    /* default */
    Stmt next;

    /**
     * Previous statement in {@link StmtList}
     */
    /* default */
    Stmt pre;
    /**
     * Statement Type
     */
    public final ST st;

    /**
     * 
     * @param st
     *            Statement Type
     * @param et
     *            The number of argument
     */
    protected Stmt(ST st, ET et) {
        this.st = st;
        this.et = et;
    }

    public abstract Stmt clone(LabelAndLocalMapper mapper);

    /**
     * 
     * @return Next statement in {@link StmtList}, null if it is the last statement in {@link StmtList}
     */
    public final Stmt getNext() {
        return next;
    }

    public Value getOp() {
        return null;
    }

    public Value getOp1() {
        return null;
    }

    public Value getOp2() {
        return null;
    }

    public Value[] getOps() {
        return null;
    }

    /**
     * 
     * @return Previous statement in {@link StmtList}, null if it is the first statement in {@link StmtList}
     */
    public final Stmt getPre() {
        return pre;
    }

    public void setOp(Value op) {
    }

    public void setOp1(Value op) {
    }

    public void setOp2(Value op) {
    }

    public void setOps(Value[] op) {
    }
}
