/*
 *  Copyright (C) 2010-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.flash.abc.types;

import com.jpexs.decompiler.flash.Configuration;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.ConstantPool;
import com.jpexs.decompiler.flash.helpers.Helper;
import java.util.HashMap;
import java.util.List;

public class MethodInfo {

    public int param_types[];
    public int ret_type;
    public int name_index; //0=no name
    // 1=need_arguments, 2=need_activation, 4=need_rest 8=has_optional 16=ignore_rest, 32=explicit, 64=setsdxns, 128=has_paramnames
    public int flags;
    public ValueKind optional[];
    public int paramNames[];
    private MethodBody body;

    public void setFlagSetsdxns() {
        flags |= 64;
    }

    public void setFlagSetsdxns(boolean val) {
        if (val) {
            setFlagSetsdxns();
        } else {
            unsetFlagSetsdxns();
        }
    }

    public void unsetFlagSetsdxns() {
        if (flagSetsdxns()) {
            flags -= 64;
        }
    }

    public void setFlagNeed_activation() {
        flags |= 2;
    }

    public void setFlagNeed_activation(boolean val) {
        if (val) {
            setFlagNeed_activation();
        } else {
            unsetFlagNeed_activation();
        }
    }

    public void unsetFlagNeed_activation() {
        if (flagNeed_activation()) {
            flags -= 2;
        }
    }

    public void setFlagNeed_rest() {
        flags |= 4;
    }

    public void unsetFlagNeed_rest() {
        if (flagNeed_rest()) {
            flags -= 4;
        }
    }

    public void setFlagNeed_rest(boolean val) {
        if (val) {
            setFlagNeed_rest();
        } else {
            unsetFlagNeed_rest();
        }
    }

    public void setFlagHas_optional() {
        flags |= 8;
    }

    public void unsetFlagHas_optional() {
        if (flagHas_optional()) {
            flags -= 8;
        }
    }

    public void setFlagHas_optional(boolean val) {
        if (val) {
            setFlagHas_optional();
        } else {
            unsetFlagHas_optional();
        }
    }

    public void setFlagHas_paramnames() {
        flags |= 128;
    }

    public void unsetFlagHas_paramnames() {
        if (flagHas_paramnames()) {
            flags -= 128;
        }
    }

    public void setFlagHas_paramnames(boolean val) {
        if (val) {
            setFlagHas_paramnames();
        } else {
            unsetFlagHas_paramnames();
        }
    }

    public boolean flagNeed_arguments() {
        return (flags & 1) == 1;
    }

    public boolean flagNeed_activation() {
        return (flags & 2) == 2;
    }

    public boolean flagNeed_rest() {
        return (flags & 4) == 4;
    }

    public boolean flagHas_optional() {
        return (flags & 8) == 8;
    }

    public boolean flagIgnore_restHas_optional() {
        return (flags & 16) == 16;
    }

    public boolean flagExplicit() {
        return (flags & 32) == 32;
    }

    public boolean flagSetsdxns() {
        return (flags & 64) == 64;
    }

    public boolean flagHas_paramnames() {
        return (flags & 128) == 128;
    }

    public MethodInfo(int param_types[], int ret_type, int name_index, int flags, ValueKind optional[], int paramNames[]) {
        this.param_types = param_types;
        this.ret_type = ret_type;
        this.name_index = name_index;
        this.flags = flags;
        this.optional = optional;
        this.paramNames = paramNames;
    }

    @Override
    public String toString() {
        String optionalStr = "[";
        if (optional != null) {
            for (int i = 0; i < optional.length; i++) {
                if (i > 0) {
                    optionalStr += ",";
                }
                optionalStr += optional[i].toString();
            }
        }
        optionalStr += "]";
        return "MethodInfo: param_types=" + Helper.intArrToString(param_types) + " ret_type=" + ret_type + " name_index=" + name_index + " flags=" + flags + " optional=" + optionalStr + " paramNames=" + Helper.intArrToString(paramNames);
    }

    public String toString(ConstantPool constants, List<String> fullyQualifiedNames) {
        String optionalStr = "[";
        if (optional != null) {
            for (int i = 0; i < optional.length; i++) {
                if (i > 0) {
                    optionalStr += ",";
                }
                optionalStr += optional[i].toString(constants);
            }
        }
        optionalStr += "]";

        String param_typesStr = "";
        for (int i = 0; i < param_types.length; i++) {
            if (i > 0) {
                param_typesStr += ",";
            }
            if (param_types[i] == 0) {
                param_typesStr += "*";
            } else {
                param_typesStr += constants.constant_multiname[param_types[i]].toString(constants, fullyQualifiedNames);
            }
        }

        String paramNamesStr = "";
        for (int i = 0; i < paramNames.length; i++) {
            if (i > 0) {
                paramNamesStr += ",";
            }
            paramNamesStr += constants.constant_string[paramNames[i]];
        }

        String ret_typeStr = "";
        if (ret_type == 0) {
            ret_typeStr += "*";
        } else {
            ret_typeStr += constants.constant_multiname[ret_type].toString(constants, fullyQualifiedNames);
        }

        return "param_types=" + param_typesStr + " ret_type=" + ret_typeStr + " name=\"" + constants.constant_string[name_index] + "\" flags=" + flags + " optional=" + optionalStr + " paramNames=" + paramNamesStr;
    }

    public String getName(ConstantPool constants) {
        if (name_index == 0) {
            return "UNKNOWN";
        }
        return constants.constant_string[name_index];
    }

    public String getParamStr(ConstantPool constants, MethodBody body, ABC abc, List<String> fullyQualifiedNames) {
        HashMap<Integer, String> localRegNames = new HashMap<>();
        if (body != null) {
            localRegNames = body.code.getLocalRegNamesFromDebug(abc);
        }
        String paramStr = "";
        for (int i = 0; i < param_types.length; i++) {
            if (i > 0) {
                paramStr += ", ";
            }
            if (!localRegNames.isEmpty()) {
                paramStr += localRegNames.get(i + 1);
            } else if ((paramNames.length > i) && (paramNames[i] != 0) && Configuration.PARAM_NAMES_ENABLE) {
                paramStr += constants.constant_string[paramNames[i]];
            } else {
                paramStr += "param" + (i + 1);
            }
            paramStr += ":";
            if (param_types[i] == 0) {
                paramStr += "*";
            } else {
                paramStr += constants.constant_multiname[param_types[i]].getName(constants, fullyQualifiedNames);
            }
            if (optional != null) {
                if (i >= param_types.length - optional.length) {
                    paramStr += "=" + optional[i - (param_types.length - optional.length)].toString(constants);
                }
            }
        }
        if (flagNeed_rest()) {
            if ((param_types != null) && (param_types.length > 0)) {
                paramStr += ", ";
            }
            paramStr += "... ";
            if (!localRegNames.isEmpty()) {
                paramStr += localRegNames.get(param_types.length + 1);
            } else {
                paramStr += "rest";
            }

        }
        return paramStr;
    }

    public String getReturnTypeStr(ConstantPool constants, List<String> fullyQualifiedNames) {
        if (ret_type == 0) {
            return "*";
        }
        return constants.constant_multiname[ret_type].getName(constants, fullyQualifiedNames);
    }

    public void setBody(MethodBody body) {
        this.body = body;
    }

    public MethodBody getBody() {
        return body;
    }
}
