/*
 * Copyright (C) 2013 JPEXS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.jpexs.decompiler.graph;

import com.jpexs.decompiler.flash.action.Action;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;

/**
 *
 * @author JPEXS
 */
public abstract class GraphSource implements Serializable {

    public abstract int size();

    public abstract GraphSourceItem get(int pos);

    public abstract boolean isEmpty();

    public abstract List<GraphTargetItem> translatePart(GraphPart part, List<Object> localData, Stack<GraphTargetItem> stack, int start, int end, int staticOperation, String path);

    private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs, int endIp) {
        boolean debugMode = false;
        while (((endIp == -1) || (ip < endIp)) && (ip < size())) {
            refs.get(ip).add(lastIp);
            lastIp = ip;
            if (refs.get(ip).size() > 1) {
                break;
            }
            GraphSourceItem ins = get(ip);

            if (ins.isIgnored()) {
                ip++;
                continue;
            }
            if (debugMode) {
                System.err.println("visit ip " + ip + " action:" + ins);
            }
            if (ins.isExit()) {
                break;
            }

            if (ins instanceof GraphSourceItemContainer) {
                GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins;
                if (ins instanceof Action) { //TODO: Remove dependency of AVM1
                    long endAddr = ((Action) ins).getAddress() + cnt.getHeaderSize();
                    for (long size : cnt.getContainerSizes()) {
                        if (size != 0) {
                            visitCode(adr2pos(endAddr), ip, refs, adr2pos(endAddr + size));
                        }
                        endAddr += size;
                    }
                    ip = adr2pos(endAddr);
                    continue;
                }

            }

            if (ins.isBranch() || ins.isJump()) {
                List<Integer> branches = ins.getBranches(this);
                for (int b : branches) {
                    if (b >= 0) {
                        visitCode(b, ip, refs, endIp);
                    }
                }
                break;
            }
            ip++;
        };
    }

    public HashMap<Integer, List<Integer>> visitCode(List<Integer> alternateEntries) {
        HashMap<Integer, List<Integer>> refs = new HashMap<>();
        int siz = size();
        for (int i = 0; i < siz; i++) {
            refs.put(i, new ArrayList<Integer>());
        }
        visitCode(0, 0, refs, -1);
        int pos = 0;
        for (int e : alternateEntries) {
            pos++;
            visitCode(e, -pos, refs, -1);
        }
        return refs;
    }

    public abstract int adr2pos(long adr);

    public abstract long pos2adr(int pos);
}
