/// <summary>
        /// Processes the specified instruction.
        /// </summary>
        /// <param name="mainProcessor">The main instruction processor.</param>
        /// <param name="instruction">The instruction.</param>
        /// <param name="methodRef">The method reference.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public List <PapyrusInstruction> Process(
            IClrInstructionProcessor mainProcessor,
            Instruction instruction, MethodReference methodRef,
            List <object> parameters)
        {
            // BUG: We are always concating the string with itself, ex: temp0 = temp0 + "val"; - This works if the instruction isnt looped.
            var output = new List <PapyrusInstruction>();
            // Equiviliant Papyrus: StrCat <output_destination> <val1> <val2>
            // Make sure we have a temp variable if necessary
            bool isStructAccess;
            var  destinationVariable = mainProcessor.GetTargetVariable(instruction, methodRef,
                                                                       out isStructAccess);

            for (var i = 0; i < parameters.Count; i++)
            {
                var stackItem = parameters[i] as EvaluationStackItem;
                if (stackItem != null)
                {
                    var fieldVar  = stackItem.Value as PapyrusFieldDefinition;
                    var paramVar  = stackItem.Value as PapyrusParameterDefinition;
                    var targetVar = stackItem.Value as PapyrusVariableReference;
                    if (targetVar != null)
                    {
                        if (!stackItem.TypeName.ToLower().Contains("string"))
                        {
                            output.Add(mainProcessor.CreatePapyrusCastInstruction(destinationVariable,
                                                                                  targetVar));
                        }

                        if (i == 0)
                        // Is First? Then we just want to assign the destinationVariable with the target value
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Assign,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable), targetVar));
                        }
                        else
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Strcat,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              targetVar));
                        }
                    }
                    else if (paramVar != null)
                    {
                        if (!stackItem.TypeName.ToLower().Contains("string"))
                        {
                            output.Add(mainProcessor.CreatePapyrusCastInstruction(destinationVariable,
                                                                                  mainProcessor.CreateVariableReferenceFromName(paramVar.Name.Value)));
                        }

                        if (i == 0)
                        // Is First? Then we just want to assign the destinationVariable with the target value
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Assign,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable), paramVar));
                        }
                        else
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Strcat,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              paramVar));
                        }
                    }
                    else if (fieldVar != null)
                    {
                        if (!stackItem.TypeName.ToLower().Contains("string"))
                        {
                            output.Add(mainProcessor.CreatePapyrusCastInstruction(destinationVariable,
                                                                                  mainProcessor.CreateVariableReferenceFromName(fieldVar.Name.Value)));
                        }

                        if (i == 0)
                        // Is First? Then we just want to assign the destinationVariable with the target value
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Assign,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable), fieldVar));
                        }
                        else
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Strcat,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              fieldVar));
                        }
                    }
                    else
                    {
                        var value      = stackItem.Value;
                        var newTempVar = false;
                        if (!stackItem.TypeName.ToLower().Contains("string"))
                        {
                            // First, get a new temp variable of type string.
                            // This new temp variable will be used for casting the source object into a string.
                            value = mainProcessor.GetTargetVariable(instruction, methodRef,
                                                                    out isStructAccess, "String", true);

                            // Create a new temp variable that we use to assign our source object to.
                            // this is so we avoid doing ex: cast ::temp0 55
                            // and instead we do: cast ::temp0 ::temp1
                            var valueToCastTemp = mainProcessor.GetTargetVariable(instruction, methodRef,
                                                                                  out isStructAccess, stackItem.TypeName, true);
                            var valueToCast =
                                mainProcessor.CreateVariableReference(
                                    Utility.GetPrimitiveTypeFromValue(stackItem.Value),
                                    stackItem.Value);

                            // Assign our newly created tempvalue with our object.
                            // ex: assign ::temp1 55
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Assign,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    valueToCastTemp),
                                                                              valueToCast));

                            // Cast the new ::temp1 to ::temp0 (equivilant to .ToString())
                            output.Add(mainProcessor.CreatePapyrusCastInstruction((string)value,
                                                                                  mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                        valueToCastTemp)));
                            newTempVar = true;

                            // Make sure that our newly ::temp1 is used when concating the string.
                            value = valueToCastTemp;
                        }

                        if (i == 0)
                        // Is First? Then we just want to assign the destinationVariable with the target value
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Assign,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              mainProcessor.CreateVariableReference(newTempVar
                                    ? PapyrusPrimitiveType.Reference
                                    : PapyrusPrimitiveType.String, value)));
                        }
                        else
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Strcat,
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              mainProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference,
                                                                                                                    destinationVariable),
                                                                              mainProcessor.CreateVariableReference(newTempVar
                                    ? PapyrusPrimitiveType.Reference
                                    : PapyrusPrimitiveType.String, value)));
                        }
                    }
                }
            }

            // TODO: In case we want to concat more strings together or call a method using this new value
            // we will have to use the targetVariable above and push it back into the stack. (Or do we...?)
            return(output);
        }
        /// <summary>
        /// Processes the conditional instruction.
        /// </summary>
        /// <param name="mainProcessor">The main instruction processor.</param>
        /// <param name="instruction">The instruction.</param>
        /// <param name="overrideOpCode">The override op code.</param>
        /// <param name="tempVariable">The temporary variable.</param>
        /// <returns></returns>
        public IEnumerable <PapyrusInstruction> Process(
            IClrInstructionProcessor mainProcessor,
            Instruction instruction,
            Code overrideOpCode = Code.Nop,
            string tempVariable = null)
        {
            bool isStructAccess;
            var  output = new List <PapyrusInstruction>();

            //cast = null;

            var heapStack = mainProcessor.EvaluationStack;

            // TODO: GetConditional only applies on Integers and must add support for Float further on.

            var papyrusOpCode =
                Utility.GetPapyrusMathOrEqualityOpCode(
                    overrideOpCode != Code.Nop ? overrideOpCode : instruction.OpCode.Code, false);

            if (heapStack.Count >= 2) //Utility.GetStackPopCount(instruction.OpCode.StackBehaviourPop))
            {
                var numeratorObject   = heapStack.Pop();
                var denumeratorObject = heapStack.Pop();
                var vars = mainProcessor.PapyrusMethod.GetVariables();
                int varIndex;

                object numerator;
                object denumerator;


                if (numeratorObject.Value is PapyrusFieldDefinition)
                {
                    numeratorObject.Value = (numeratorObject.Value as PapyrusFieldDefinition).DefaultValue;
                }

                if (denumeratorObject.Value is PapyrusFieldDefinition)
                {
                    denumeratorObject.Value = (denumeratorObject.Value as PapyrusFieldDefinition).DefaultValue;
                }

                if (numeratorObject.Value is PapyrusVariableReference)
                {
                    var varRef      = numeratorObject.Value as PapyrusVariableReference;
                    var refTypeName = varRef.TypeName?.Value;

                    if (refTypeName == null)
                    {
                        refTypeName = "Int";
                    }

                    numerator = varRef;

                    // if not int or string, we need to cast it.
                    if (!refTypeName.ToLower().Equals("int") && !refTypeName.ToLower().Equals("system.int32") &&
                        !refTypeName.ToLower().Equals("system.string") && !refTypeName.ToLower().Equals("string"))
                    {
                        var typeVariable = mainProcessor.GetTargetVariable(instruction, null,
                                                                           out isStructAccess, "Int");
                        output.Add(mainProcessor.CreatePapyrusCastInstruction(typeVariable, varRef));
                        // cast = "Cast " + typeVariable + " " + value1;
                    }
                }
                else if (numeratorObject.Value is FieldReference)
                {
                    numerator =
                        mainProcessor.CreateVariableReferenceFromName(
                            (numeratorObject.Value as FieldReference).Name);
                }
                else
                {
                    numerator = mainProcessor.CreateVariableReference(
                        Utility.GetPrimitiveTypeFromValue(numeratorObject.Value),
                        numeratorObject.Value);
                }

                if (denumeratorObject.Value is PapyrusVariableReference)
                {
                    var varRef      = denumeratorObject.Value as PapyrusVariableReference;
                    var refTypeName = varRef.TypeName.Value;
                    denumerator = varRef;

                    // if not int or string, we need to cast it.
                    if (!refTypeName.ToLower().Equals("int") && !refTypeName.ToLower().Equals("system.int32") &&
                        !refTypeName.ToLower().Equals("system.string") && !refTypeName.ToLower().Equals("string"))
                    {
                        // CAST BOOL TO INT
                        // var typeVariable = GetTargetVariable(instruction, null, "Int");
                        // cast = "Cast " + typeVariable + " " + value2;

                        var typeVariable = mainProcessor.GetTargetVariable(instruction, null,
                                                                           out isStructAccess, "Int");
                        output.Add(mainProcessor.CreatePapyrusCastInstruction(typeVariable, varRef));
                    }
                }
                else if (denumeratorObject.Value is FieldReference)
                {
                    denumerator =
                        mainProcessor.CreateVariableReferenceFromName(
                            (denumeratorObject.Value as FieldReference).Name);
                }
                else
                {
                    denumerator =
                        mainProcessor.CreateVariableReference(
                            Utility.GetPrimitiveTypeFromValue(denumeratorObject.Value), denumeratorObject.Value);
                }

                if (!string.IsNullOrEmpty(tempVariable))
                {
                    output.Add(mainProcessor.CreatePapyrusInstruction(papyrusOpCode,
                                                                      mainProcessor.CreateVariableReferenceFromName(tempVariable), denumerator, numerator));
                    return(output);
                }

                // if (Utility.IsGreaterThan(code) || Utility.IsLessThan(code))
                {
                    var next = instruction.Next;

                    // If the next one is a switch, it most likely
                    // means that we want to apply some mathematical stuff
                    // on our constant value so that we can properly do an equality
                    // comparison.
                    if (InstructionHelper.IsSwitch(next.OpCode.Code))
                    {
                        var newTempVariable = mainProcessor.GetTargetVariable(instruction, null,
                                                                              out isStructAccess, "Int", true);
                        mainProcessor.SwitchConditionalComparer =
                            mainProcessor.CreateVariableReferenceFromName(newTempVariable);
                        mainProcessor.SwitchConditionalComparer.Type     = PapyrusPrimitiveType.Reference;
                        mainProcessor.SwitchConditionalComparer.TypeName =
                            "Int".Ref(mainProcessor.PapyrusAssembly);

                        output.Add(mainProcessor.CreatePapyrusInstruction(papyrusOpCode,
                                                                          mainProcessor.SwitchConditionalComparer, denumerator, numerator));
                        return(output);
                    }

                    while (next != null &&
                           !InstructionHelper.IsStoreLocalVariable(next.OpCode.Code) &&
                           !InstructionHelper.IsStoreFieldObject(next.OpCode.Code) &&
                           !InstructionHelper.IsCallMethod(next.OpCode.Code))
                    {
                        next = next.Next;
                    }

                    if (next != null && next.Operand is MethodReference)
                    {
                        // if none found, create a temp one.
                        var methodRef = next.Operand as MethodReference;

                        var tVar = mainProcessor.CreateTempVariable(
                            methodRef.MethodReturnType.ReturnType.FullName != "System.Void"
                                ? methodRef.MethodReturnType.ReturnType.FullName
                                : "System.int");

                        var targetVar = tVar;

                        mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                        {
                            Value    = tVar.Value,
                            TypeName = tVar.TypeName.Value
                        });

                        output.Add(mainProcessor.CreatePapyrusInstruction(papyrusOpCode, targetVar,
                                                                          denumerator, numerator));
                        return(output);
                    }


                    if (next == null)
                    {
                        // No intentions to store this value into a variable,
                        // Its to be used in a function call.
                        //return "NULLPTR " + denumerator + " " + numerator;
                        return(output);
                    }

                    mainProcessor.SkipToOffset = next.Offset;
                    if (next.Operand is FieldReference)
                    {
                        var field     = mainProcessor.GetFieldFromStfld(next);
                        var structRef = field as PapyrusStructFieldReference;
                        if (structRef != null)
                        {
                            // output.Add(mainInstructionProcessor.CreatePapyrusInstruction(papyrusOpCode, field, denumerator, numerator));
                            // structRef.
                        }
                        else if (field != null)
                        {
                            output.Add(mainProcessor.CreatePapyrusInstruction(papyrusOpCode, field,
                                                                              denumerator, numerator));
                            return(output);
                            // LastSaughtTypeName = fieldData.TypeName;
                        }

                        //if (field != null)
                        //{
                        //    output.Add(mainInstructionProcessor.CreatePapyrusInstruction(papyrusOpCode, field, denumerator, numerator));
                        //    return output;
                        //    //return field.Name + " " + denumerator + " " + numerator;
                        //}
                    }


                    var numericValue = mainProcessor.GetNumericValue(next);
                    varIndex = (int)numericValue;
                }

                output.Add(mainProcessor.CreatePapyrusInstruction(papyrusOpCode, vars[varIndex], denumerator,
                                                                  numerator));
                //return vars[varIndex].Name + " " + denumerator + " " + numerator;
            }
            else if (mainProcessor.PapyrusCompilerOptions == PapyrusCompilerOptions.Strict)
            {
                throw new StackUnderflowException();
            }
            return(output);
        }