/*
 *  Copyright (C) 2010-2021 JPEXS, All rights reserved.
 * 
 * This library 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 3.0 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */
package com.jpexs.decompiler.flash.abc.avm2.parser.script;

import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns;
import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.abc.types.ValueKind;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author JPEXS
 */
public class PropertyAVM2Item extends AssignableAVM2Item {

    public String propertyName;

    public GraphTargetItem object;

    public AbcIndexing abcIndex;

    private final List<NamespaceItem> openedNamespaces;

    private final List<MethodBody> callStack;

    public List<GraphTargetItem> scopeStack = new ArrayList<>();

    @Override
    public AssignableAVM2Item copy() {
        PropertyAVM2Item p = new PropertyAVM2Item(object, propertyName, abcIndex, openedNamespaces, callStack);
        return p;
    }

    public PropertyAVM2Item(GraphTargetItem object, String propertyName, AbcIndexing abcIndex, List<NamespaceItem> openedNamespaces, List<MethodBody> callStack) {
        this.propertyName = propertyName;
        this.object = object;
        this.abcIndex = abcIndex;
        this.openedNamespaces = openedNamespaces;
        this.callStack = callStack;
    }

    @Override
    public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
        return writer;
    }

    private int allNsSet(AbcIndexing abc) throws CompilationException {
        int[] nssa = new int[openedNamespaces.size()];
        for (int i = 0; i < nssa.length; i++) {
            nssa[i] = openedNamespaces.get(i).getCpoolIndex(abc);
        }

        return abc.getSelectedAbc().constants.getNamespaceSetId(nssa, true);
    }

    private static GraphTargetItem multinameToType(Set<Integer> visited, int m_index, AVM2ConstantPool constants) {
        if (visited.contains(m_index)) {
            Logger.getLogger(PropertyAVM2Item.class.getName()).log(Level.WARNING, "Recursive typename detected");
            return null;
        }
        if (m_index == 0) {
            return TypeItem.UNBOUNDED;
        }
        Multiname m = constants.getMultiname(m_index);
        if (m.kind == Multiname.TYPENAME) {
            visited.add(m_index);
            GraphTargetItem obj = multinameToType(visited, m.qname_index, constants);
            if (obj == null) {
                return null;
            }
            List<GraphTargetItem> params = new ArrayList<>();
            for (int pm : m.params) {
                GraphTargetItem r = multinameToType(visited, pm, constants);
                if (r == null) {
                    return null;
                }
                if (pm == 0) {
                    r = new NullAVM2Item(null, null);
                }
                params.add(r);
            }
            return new ApplyTypeAVM2Item(null, null, obj, params);
        } else {
            return new TypeItem(m.getNameWithNamespace(constants, true));
        }
    }

    public static GraphTargetItem multinameToType(int m_index, AVM2ConstantPool constants) {
        return multinameToType(new HashSet<>(), m_index, constants);
    }

    public void resolve(boolean mustExist, SourceGeneratorLocalData localData, Reference<GraphTargetItem> objectType, Reference<GraphTargetItem> propertyType, Reference<Integer> propertyIndex, Reference<ValueKind> propertyValue, Reference<ABC> propertyValueABC) throws CompilationException {
        GraphTargetItem thisType = new TypeItem(localData.getFullClass());
        GraphTargetItem objType = null;
        GraphTargetItem objSubType = null;
        ValueKind propValue = null;
        ABC propValueAbc = null;
        if (object != null) {
            GraphTargetItem oretType = object.returnType();
            if (oretType instanceof UnresolvedAVM2Item) {
                UnresolvedAVM2Item ur = (UnresolvedAVM2Item) oretType;
                oretType = ur.resolved;
            }
            if (oretType instanceof ApplyTypeAVM2Item) {
                ApplyTypeAVM2Item t = (ApplyTypeAVM2Item) oretType;
                objType = t;
            } else if (oretType instanceof TypeItem) {
                TypeItem t = (TypeItem) oretType;
                objType = t;
            } else {
                objType = new TypeItem(oretType.toString());
            }
        }
        GraphTargetItem propType = null;
        int propIndex = 0;
        ABC abc = abcIndex.getSelectedAbc();
        AVM2ConstantPool constants = abc.constants;
        if (!propertyName.startsWith("@")) {

            if (scopeStack.isEmpty()) { //Everything is multiname when with command
                if (objType == null) {

                    /*for (GraphTargetItem s : scopeStack) {
                     String oType = s.returnType().toString();
                     String name = oType;
                     String nsname = "";
                     if(name.contains(".")){
                     nsname = name.substring(0,name.lastIndexOf("."));
                     name = name.substring(name.lastIndexOf(".")+1);
                     }

                     List<ABC> abcs = new ArrayList<>();
                     abcs.add(abc);
                     abcs.addAll(otherABCs);
                     loopabc:
                     for (ABC a : abcs) {
                     for (int h = 0; h < a.instance_info.size(); h++) {
                     InstanceInfo ii = a.instance_info.get(h);
                     Multiname n = a.constants.constant_multiname.get(ii.name_index);
                     if (name.equals(n.getName(a.constants, new ArrayList<>())) && n.getNamespace(a.constants).hasName(nsname,a.constants)) {
                     Reference<String> outName = new Reference<>("");
                     Reference<String> outNs = new Reference<>("");
                     Reference<String> outPropNs = new Reference<>("");
                     Reference<Integer> outPropNsKind = new Reference<>(1);
                     Reference<String> outPropType = new Reference<>("");
                     if (AVM2SourceGenerator.searchPrototypeChain(false, abcs, nsname, name, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropType)) {
                     objType = "".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal();
                     propType = outPropType.getVal();
                     propIndex = abc.constants.getMultinameId(new Multiname(Multiname.QNAME,
                     abc.constants.getStringId(propertyName, true),
                     abc.constants.getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), 0, true), 0, 0, new ArrayList<Integer>()), true
                     );

                     break loopabc;
                     }
                     }
                     }
                     }
                     }*/
                }

                GraphTargetItem ttype = objType;
                if (ttype == null) {
                    ttype = thisType;
                }

                {
                    if (ttype.equals(new TypeItem(InitVectorAVM2Item.VECTOR_FQN))) {
                        switch ("" + objSubType) {
                            case "int":
                                ttype = new TypeItem(InitVectorAVM2Item.VECTOR_INT);
                                break;
                            case "Number":
                                ttype = new TypeItem(InitVectorAVM2Item.VECTOR_DOUBLE);
                                break;
                            case "uint":
                                ttype = new TypeItem(InitVectorAVM2Item.VECTOR_UINT);
                                break;
                            default:
                                ttype = new TypeItem(InitVectorAVM2Item.VECTOR_OBJECT);
                        }
                    }
                    /*                    AbcIndexing.TraitIndex p = abc.findProperty(new AbcIndexing.PropertyDef(propertyName, ttype), false, true);
                     if(p!=null){
                     objType = new TypeItem(outNs.getVal().isEmpty() ? outName.getVal() : outNs.getVal().toRawString() + "." + outName.getVal());
                     propType = p.type;
                     propIndex = abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.QNAME,
                     abc.getLastAbc().getStringId(propertyName, true),
                     abc.getLastAbc().getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), outPropNsIndex.getVal(), true), 0, 0, new ArrayList<>()), true
                     );
                     propValue = outPropValue.getVal();
                     }*/
                    if (ttype instanceof TypeItem) {
                        DottedChain ftn = ((TypeItem) ttype).fullTypeName;
                        Reference<String> outName = new Reference<>("");
                        Reference<DottedChain> outNs = new Reference<>(DottedChain.EMPTY);
                        Reference<DottedChain> outPropNs = new Reference<>(DottedChain.EMPTY);
                        Reference<Integer> outPropNsKind = new Reference<>(1);
                        Reference<Integer> outPropNsIndex = new Reference<>(0);
                        Reference<GraphTargetItem> outPropType = new Reference<>(null);
                        Reference<ValueKind> outPropValue = new Reference<>(null);
                        Reference<ABC> outPropValueAbc = new Reference<>(null);
                        List<Integer> otherNs = new ArrayList<>();
                        for (NamespaceItem n : openedNamespaces) {
                            if (n.isResolved()) {
                                otherNs.add(n.getCpoolIndex(abcIndex));
                            }
                        }
                        if ((object instanceof NameAVM2Item) && "super".equals(((NameAVM2Item) object).getVariableName()))
                        {
                            // super is special cause its static type is the super class, but it still allows access to protected members
                            // so for super to work we need to also allow the protected namespace of the super class
                            // however this namespace is in the ABC of the super class and not in abcIndex.getSelectedAbc()
                            AbcIndexing.ClassIndex ci = abcIndex.findClass(objType);
                            int superProtectedNs = ci.abc.instance_info.get(ci.index).protectedNS;
                            AbcIndexing.TraitIndex sp = abcIndex.findProperty(new AbcIndexing.PropertyDef(propertyName, objType, ci.abc, superProtectedNs), false, true);
                            if (sp != null) {
                                objType = sp.objType;
                                Namespace ns = sp.trait.getName(sp.abc).getNamespace(sp.abc.constants);
                                propIndex = constants.getMultinameId(Multiname.createQName(false,
                                        constants.getStringId(propertyName, true),
                                        constants.getNamespaceId(ns.kind, ns.getName(sp.abc.constants), sp.abc == abc ? abc.constants.getNamespaceSubIndex(sp.trait.getName(sp.abc).namespace_index) : 0, true)), true
                                );
                                propType = sp.returnType;
                                propValue = sp.value;
                                propValueAbc = sp.abc;
                            }
                        }
                        if (propType == null && AVM2SourceGenerator.searchPrototypeChain(otherNs, localData.privateNs, localData.protectedNs, false, abcIndex, ftn.getWithoutLast(), ftn.getLast(), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) {
                            objType = new TypeItem(outNs.getVal().addWithSuffix(outName.getVal()));
                            propType = outPropType.getVal();
                            propIndex = constants.getMultinameId(Multiname.createQName(false,
                                    constants.getStringId(propertyName, true),
                                    constants.getNamespaceId(outPropNsKind.getVal(), outPropNs.getVal(), outPropNsIndex.getVal(), true)), true
                            );
                            propValue = outPropValue.getVal();
                            propValueAbc = outPropValueAbc.getVal();
                        }
                    }

                    if (objType == null) {
                        for (MethodBody b : callStack) {
                            for (int i = 0; i < b.traits.traits.size(); i++) {
                                Trait t = b.traits.traits.get(i);
                                if (t.getName(abc).getName(constants, null, true, true).equals(propertyName)) {
                                    if (t instanceof TraitSlotConst) {
                                        TraitSlotConst tsc = (TraitSlotConst) t;
                                        objType = new TypeItem(DottedChain.FUNCTION);
                                        propType = multinameToType(tsc.type_index, constants);
                                        propIndex = tsc.name_index;
                                        if (!localData.traitUsages.containsKey(b)) {
                                            localData.traitUsages.put(b, new ArrayList<>());
                                        }
                                        localData.traitUsages.get(b).add(i);
                                    }
                                }
                            }
                        }
                        if (objType == null) {
                            loopobjType:
                            for (int i = 0; i < openedNamespaces.size(); i++) {
                                if (!openedNamespaces.get(i).isResolved()) {
                                    continue;
                                }
                                int nsindex = openedNamespaces.get(i).getCpoolIndex(abcIndex);

                                int nsKind = openedNamespaces.get(i).kind;
                                DottedChain nsname = openedNamespaces.get(i).name;
                                int name_index = 0;
                                for (int m = 1; m < constants.getMultinameCount(); m++) {
                                    Multiname mname = constants.getMultiname(m);
                                    if (mname.kind == Multiname.QNAME && mname.getName(constants, null, true, true).equals(propertyName) && mname.namespace_index == nsindex) {
                                        name_index = m;
                                        break;
                                    }
                                }
                                if (name_index > 0) {
                                    for (int c = 0; c < abc.instance_info.size(); c++) {
                                        if (abc.instance_info.get(c).deleted) {
                                            continue;
                                        }
                                        for (Trait t : abc.instance_info.get(c).instance_traits.traits) {
                                            if (t.name_index == name_index) {
                                                objType = multinameToType(abc.instance_info.get(c).name_index, constants);
                                                propType = AVM2SourceGenerator.getTraitReturnType(abcIndex, t);
                                                propIndex = t.name_index;
                                                if (t instanceof TraitSlotConst) {
                                                    TraitSlotConst tsc = (TraitSlotConst) t;
                                                    propValue = new ValueKind(tsc.value_index, tsc.value_kind);
                                                    propValueAbc = abc;
                                                }
                                                break loopobjType;
                                            }
                                        }
                                        for (Trait t : abc.class_info.get(c).static_traits.traits) {
                                            if (t.name_index == name_index) {
                                                objType = multinameToType(abc.instance_info.get(c).name_index, constants);
                                                propType = AVM2SourceGenerator.getTraitReturnType(abcIndex, t);
                                                propIndex = t.name_index;
                                                if (t instanceof TraitSlotConst) {
                                                    TraitSlotConst tsc = (TraitSlotConst) t;
                                                    propValue = new ValueKind(tsc.value_index, tsc.value_kind);
                                                    propValueAbc = abc;
                                                }
                                                break loopobjType;
                                            }
                                        }
                                    }

                                    for (ScriptInfo si : abc.script_info) {
                                        if (si.deleted) {
                                            continue;
                                        }
                                        for (Trait t : si.traits.traits) {
                                            if (t.name_index == name_index) {
                                                objType = new TypeItem(DottedChain.OBJECT);
                                                propType = AVM2SourceGenerator.getTraitReturnType(abcIndex, t);
                                                propIndex = t.name_index;
                                                if (t instanceof TraitSlotConst) {
                                                    TraitSlotConst tsc = (TraitSlotConst) t;
                                                    propValue = new ValueKind(tsc.value_index, tsc.value_kind);
                                                    propValueAbc = abc;
                                                }
                                                break loopobjType;
                                            }
                                        }
                                    }
                                }
                                if (nsKind == Namespace.KIND_PACKAGE && propertyName != null) {
                                    AbcIndexing.TraitIndex p = abcIndex.findNsProperty(new AbcIndexing.PropertyNsDef(propertyName, nsname, abc, openedNamespaces.get(i).getCpoolIndex(abcIndex)), true, true);

                                    Reference<String> outName = new Reference<>("");
                                    Reference<DottedChain> outNs = new Reference<>(DottedChain.EMPTY);
                                    Reference<DottedChain> outPropNs = new Reference<>(DottedChain.EMPTY);
                                    Reference<Integer> outPropNsKind = new Reference<>(1);
                                    Reference<Integer> outPropNsIndex = new Reference<>(0);
                                    Reference<GraphTargetItem> outPropType = new Reference<>(null);
                                    Reference<ValueKind> outPropValue = new Reference<>(null);
                                    Reference<ABC> outPropValueAbc = new Reference<>(null);
                                    if (p != null && (p.objType instanceof TypeItem)) {
                                        List<Integer> otherns = new ArrayList<>();
                                        for (NamespaceItem n : openedNamespaces) {
                                            if (n.isResolved()) {
                                                otherns.add(n.getCpoolIndex(abcIndex));
                                            }
                                        }
                                        if (AVM2SourceGenerator.searchPrototypeChain(otherns, localData.privateNs, localData.protectedNs, false, abcIndex, nsname, (((TypeItem) p.objType).fullTypeName.getLast()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) {
                                            objType = new TypeItem(outNs.getVal().addWithSuffix(outName.getVal()));
                                            propType = p.returnType;
                                            propIndex = constants.getMultinameId(Multiname.createQName(false,
                                                    constants.getStringId(propertyName, true),
                                                    constants.getNamespaceId(outPropNsKind.getVal(), outPropNs.getVal(), outPropNsIndex.getVal(), true)), true
                                            );
                                            propValue = p.value;
                                            propValueAbc = outPropValueAbc.getVal();
                                            break loopobjType;
                                        }
                                    }

                                    //if (propertyName != null && AVM2SourceGenerator.searchPrototypeChain(false, abcs, nsname, n.getName(a.constants, null, true), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue)) {
                                    //
                                    //}
                                }
                            }
                        }
                    }
                }

            }
        }

        if (propIndex == 0 && !mustExist) {
            String pname = propertyName;
            boolean attr = pname.startsWith("@");
            if (attr) {
                pname = pname.substring(1);
            }
            Multiname multiname;
            if (attr && pname.isEmpty()) {
                multiname = Multiname.createMultinameL(true,
                        constants.getNamespaceSetId(new int[]{constants.getNamespaceId(Namespace.KIND_PACKAGE_INTERNAL, localData.pkg, 0, true)}, true));
            } else {
                int name_index = constants.getStringId("*".equals(pname) ? null : pname, true); //Note: name = * is for .@* attribute
                multiname = Multiname.createMultiname(attr, name_index, allNsSet(abcIndex));
            }
            propIndex = constants.getMultinameId(multiname, true);
            propType = TypeItem.UNBOUNDED;
            objType = TypeItem.UNBOUNDED;
            propValue = null;
            propValueAbc = null;

        }
        propertyValue.setVal(propValue);
        propertyValueABC.setVal(propValueAbc);
        propertyIndex.setVal(propIndex);
        propertyType.setVal(propType);
        objectType.setVal(objType);
    }

    public int resolveProperty(SourceGeneratorLocalData localData) throws CompilationException {
        Reference<GraphTargetItem> objType = new Reference<>(null);
        Reference<GraphTargetItem> propType = new Reference<>(null);
        Reference<Integer> propIndex = new Reference<>(0);
        Reference<ValueKind> outPropValue = new Reference<>(null);
        Reference<ABC> outPropValueAbc = new Reference<>(null);
        resolve(false, localData, objType, propType, propIndex, outPropValue, outPropValueAbc);
        return propIndex.getVal();
    }

    @Override
    public GraphTargetItem returnType() {

        Reference<GraphTargetItem> objType = new Reference<>(null);
        Reference<GraphTargetItem> propType = new Reference<>(null);
        Reference<Integer> propIndex = new Reference<>(0);
        Reference<ValueKind> outPropValue = new Reference<>(null);
        Reference<ABC> outPropValueAbc = new Reference<>(null);
        try {
            resolve(false, new SourceGeneratorLocalData(new HashMap<>(), 0, false, 0)/*???*/, objType, propType, propIndex, outPropValue, outPropValueAbc);

            return propType.getVal();
        } catch (CompilationException ex) {
            Logger.getLogger(PropertyAVM2Item.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException {

        Reference<GraphTargetItem> objType = new Reference<>(null);
        Reference<GraphTargetItem> propType = new Reference<>(null);
        Reference<Integer> propIndex = new Reference<>(0);
        Reference<ValueKind> outPropValue = new Reference<>(null);
        Reference<ABC> outPropValueAbc = new Reference<>(null);

        resolve(false, localData, objType, propType, propIndex, outPropValue, outPropValueAbc);

        int propertyId = propIndex.getVal();
        Object obj = resolveObject(localData, generator);
        Reference<Integer> ret_temp = new Reference<>(-1);
        if (assignedValue != null) {
            GraphTargetItem targetType = propType.getVal();
            String srcType = assignedValue.returnType().toString();
            GraphTargetItem coerced = assignedValue;
            if (!targetType.toString().equals(srcType) && !propertyName.startsWith("@")) {
                //coerced = makeCoerced(assignedValue, targetType);
            }
            return toSourceMerge(localData, generator, obj, coerced,
                    needsReturn ? dupSetTemp(localData, generator, ret_temp) : null,
                    ins(AVM2Instructions.SetProperty, propertyId),
                    needsReturn ? getTemp(localData, generator, ret_temp) : null,
                    killTemp(localData, generator, Arrays.asList(ret_temp)));
        } else {
            if (obj instanceof AVM2Instruction && (((AVM2Instruction) obj).definition instanceof FindPropertyStrictIns)) {
                return toSourceMerge(localData, generator, ins(AVM2Instructions.GetLex, propertyId),
                        needsReturn ? null : ins(AVM2Instructions.Pop)
                );
            }
            return toSourceMerge(localData, generator, obj, ins(AVM2Instructions.GetProperty, propertyId),
                    needsReturn ? null : ins(AVM2Instructions.Pop)
            );
        }
    }

    @Override
    public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
        return toSource(localData, generator, true);
    }

    @Override
    public List<GraphSourceItem> toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
        return toSource(localData, generator, false);
    }

    @Override
    public String toString() {
        return "" + object + "." + propertyName;
    }

    @Override
    public boolean hasReturnValue() {
        return true;
    }

    public Object resolveObject(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
        Object obj = object;

        if (obj == null) {
            String cname = localData.currentClass;
            DottedChain pkgName = localData.pkg;
            Reference<String> outName = new Reference<>("");
            Reference<DottedChain> outNs = new Reference<>(DottedChain.EMPTY);
            Reference<DottedChain> outPropNs = new Reference<>(DottedChain.EMPTY);
            Reference<Integer> outPropNsKind = new Reference<>(1);
            Reference<Integer> outPropNsIndex = new Reference<>(0);
            Reference<GraphTargetItem> outPropType = new Reference<>(null);
            Reference<ValueKind> outPropValue = new Reference<>(null);
            Reference<ABC> outPropValueAbc = new Reference<>(null);

            /*List<ABC> abcs = new ArrayList<>();
             abcs.add(abc);
             abcs.addAll(otherABCs);*/
            List<Integer> otherNs = new ArrayList<>();
            for (NamespaceItem n : openedNamespaces) {
                if (n.isResolved()) {
                    otherNs.add(n.getCpoolIndex(abcIndex));
                }
            }
            if (!localData.subMethod && cname != null && AVM2SourceGenerator.searchPrototypeChain(otherNs, localData.privateNs, localData.protectedNs, true, abcIndex, pkgName, cname, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc) && (localData.getFullClass().equals(outNs.getVal().addWithSuffix(outName.getVal()).toRawString()))) {
                NameAVM2Item nobj = new NameAVM2Item(new TypeItem(localData.getFullClass()), 0, "this", null, false, openedNamespaces, abcIndex);
                nobj.setRegNumber(0);
                obj = nobj;
            } else {
                Reference<GraphTargetItem> objType = new Reference<>(null);
                Reference<GraphTargetItem> propType = new Reference<>(null);
                Reference<Integer> propIndex = new Reference<>(0);
                Reference<ValueKind> propValue = new Reference<>(null);
                Reference<ABC> propValueAbc = new Reference<>(null);

                resolve(false, localData, objType, propType, propIndex, outPropValue, propValueAbc);
                obj = ins(AVM2Instructions.FindPropertyStrict, propIndex.getVal());
            }
        }
        return obj;
    }

    @Override
    public List<GraphSourceItem> toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException {

        Reference<GraphTargetItem> objType = new Reference<>(null);
        Reference<GraphTargetItem> propType = new Reference<>(null);
        Reference<Integer> propIndex = new Reference<>(0);
        Reference<ValueKind> outPropValue = new Reference<>(null);
        Reference<ABC> outPropValueAbc = new Reference<>(null);

        resolve(false, localData, objType, propType, propIndex, outPropValue, outPropValueAbc);

        int propertyId = propIndex.getVal();
        Object obj = resolveObject(localData, generator);

        Reference<Integer> ret_temp = new Reference<>(-1);
        Reference<Integer> obj_temp = new Reference<>(-1);

        boolean isInteger = propType.getVal().toString().equals("int");

        List<GraphSourceItem> ret = toSourceMerge(localData, generator, obj, dupSetTemp(localData, generator, obj_temp),
                //Start get original
                //getTemp(localData, generator, obj_temp),
                //index!=null?getTemp(localData, generator, index_temp):null,
                ins(AVM2Instructions.GetProperty, propertyId),
                (!isInteger && post) ? ins(AVM2Instructions.ConvertD) : null,
                //End get original
                (!post) ? (decrement ? ins(isInteger ? AVM2Instructions.DecrementI : AVM2Instructions.Decrement) : ins(isInteger ? AVM2Instructions.IncrementI : AVM2Instructions.Increment)) : null,
                needsReturn ? ins(AVM2Instructions.Dup) : null,
                (post) ? (decrement ? ins(isInteger ? AVM2Instructions.DecrementI : AVM2Instructions.Decrement) : ins(isInteger ? AVM2Instructions.IncrementI : AVM2Instructions.Increment)) : null,
                setTemp(localData, generator, ret_temp),
                getTemp(localData, generator, obj_temp),
                getTemp(localData, generator, ret_temp),
                ins(AVM2Instructions.SetProperty, propertyId),
                //needsReturn?getTemp(localData, generator, ret_temp):null,
                killTemp(localData, generator, Arrays.asList(ret_temp, obj_temp)));
        return ret;
    }
}
