/*
 * Copyright 2013-2016 must-be.org
 *
 * 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 consulo.dotnet.mono.debugger.proxy;

import java.util.List;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import consulo.dotnet.debugger.proxy.DotNetAbsentInformationException;
import consulo.dotnet.debugger.proxy.DotNetInvalidObjectException;
import consulo.dotnet.debugger.proxy.DotNetInvalidStackFrameException;
import consulo.dotnet.debugger.proxy.DotNetLocalVariableProxy;
import consulo.dotnet.debugger.proxy.DotNetMethodParameterProxy;
import consulo.dotnet.debugger.proxy.DotNetSourceLocation;
import consulo.dotnet.debugger.proxy.DotNetStackFrameProxy;
import consulo.dotnet.debugger.proxy.DotNetThreadProxy;
import consulo.dotnet.debugger.proxy.value.DotNetValueProxy;
import mono.debugger.AbsentInformationException;
import mono.debugger.InvalidObjectException;
import mono.debugger.InvalidStackFrameException;
import mono.debugger.LocalVariableOrParameterMirror;
import mono.debugger.StackFrameMirror;
import mono.debugger.Value;
import mono.debugger.util.ImmutablePair;

/**
 * @author VISTALL
 * @since 18.04.2016
 */
public class MonoStackFrameProxy implements DotNetStackFrameProxy
{
	private int myIndex;
	private MonoVirtualMachineProxy myVirtualMachineProxy;
	private StackFrameMirror myFrameMirror;

	public MonoStackFrameProxy(int index, MonoVirtualMachineProxy virtualMachineProxy, StackFrameMirror frameMirror)
	{
		myIndex = index;
		myVirtualMachineProxy = virtualMachineProxy;
		myFrameMirror = frameMirror;
	}

	public StackFrameMirror getFrameMirror()
	{
		return myFrameMirror;
	}

	@NotNull
	@Override
	public DotNetValueProxy getThisObject() throws DotNetInvalidObjectException, DotNetInvalidStackFrameException, DotNetAbsentInformationException
	{
		try
		{
			return MonoValueProxyUtil.wrap(myFrameMirror.thisObject());
		}
		catch(AbsentInformationException e)
		{
			throw new DotNetAbsentInformationException(e);
		}
		catch(InvalidObjectException e)
		{
			throw new DotNetInvalidObjectException(e);
		}
		catch(InvalidStackFrameException e)
		{
			throw new DotNetInvalidStackFrameException(e);
		}
	}

	@Nullable
	@Override
	public DotNetValueProxy getParameterValue(@NotNull DotNetMethodParameterProxy parameterProxy)
	{
		MonoMethodParameterProxy methodParameterProxy = (MonoMethodParameterProxy) parameterProxy;
		return MonoValueProxyUtil.wrap(getRefreshedFrame().localOrParameterValue(methodParameterProxy.getParameter()));
	}

	@Override
	@SuppressWarnings("unchecked")
	public void setParameterValue(@NotNull DotNetMethodParameterProxy parameterProxy, @NotNull DotNetValueProxy valueProxy)
	{
		MonoMethodParameterProxy monoMethodParameterProxy = (MonoMethodParameterProxy) parameterProxy;

		Value value = ((MonoValueProxyBase) valueProxy).getMirror();

		getRefreshedFrame().setLocalOrParameterValues(new ImmutablePair<LocalVariableOrParameterMirror, Value<?>>(monoMethodParameterProxy.getParameter(), value));
	}

	@Nullable
	@Override
	public DotNetValueProxy getLocalValue(@NotNull DotNetLocalVariableProxy localVariableProxy)
	{
		MonoLocalVariableProxy methodParameterProxy = (MonoLocalVariableProxy) localVariableProxy;
		return MonoValueProxyUtil.wrap(getRefreshedFrame().localOrParameterValue(methodParameterProxy.getMirror()));

	}

	@Override
	@SuppressWarnings("unchecked")
	public void setLocalValue(@NotNull DotNetLocalVariableProxy localVariableProxy, @NotNull DotNetValueProxy valueProxy)
	{
		MonoLocalVariableProxy monoLocalVariableProxy = (MonoLocalVariableProxy) localVariableProxy;

		Value value = ((MonoValueProxyBase) valueProxy).getMirror();

		getRefreshedFrame().setLocalOrParameterValues(new ImmutablePair<LocalVariableOrParameterMirror, Value<?>>(monoLocalVariableProxy.getMirror(), value));
	}

	@NotNull
	@Override
	public DotNetThreadProxy getThread()
	{
		return new MonoThreadProxy(myVirtualMachineProxy, myFrameMirror.thread());
	}

	@Override
	public int getIndex()
	{
		return myIndex;
	}

	@NotNull
	@Override
	public Object getEqualityObject()
	{
		return myFrameMirror.location().method().id();
	}

	@Nullable
	@Override
	public DotNetSourceLocation getSourceLocation()
	{
		return new MonoSourceLocation(myFrameMirror.location());
	}

	@NotNull
	private StackFrameMirror getRefreshedFrame()
	{
		try
		{
			List<StackFrameMirror> frames = myFrameMirror.thread().frames();
			return frames.get(myIndex);
		}
		catch(Exception e)
		{
			return myFrameMirror;
		}
	}
}

@@ -1747,16 +1747,22 @@
 private 
+final 
 int myIn
@@ -1775,16 +1775,22 @@
 private 
+final 
 MonoVirt
@@ -1829,32 +1829,38 @@
 Proxy;%0A%09private 
+final 
 StackFrameMirror
@@ -1876,16 +1876,48 @@
 irror;%0A%0A
+%09private final int myMethodId;%0A%0A
 %09public 
@@ -2122,19 +2122,83 @@
 Mirror;%0A
-%09%7D%0A
+%0A%09%09myMethodId = frameMirror.location().method().id();%0A%09%7D%0A%0A%09@NotNull
 %0A%09public
@@ -2244,29 +2244,35 @@
 %09return 
-myFrameMirror
+getRefreshedFrame()
 ;%0A%09%7D%0A%0A%09@
@@ -2482,29 +2482,35 @@
 il.wrap(
-myFrameMirror
+getRefreshedFrame()
 .thisObj
@@ -4712,44 +4712,16 @@
 n my
-FrameMirror.location().method().id()
+MethodId
 ;%0A%09%7D
@@ -4828,29 +4828,35 @@
 ocation(
-myFrameMirror
+getRefreshedFrame()
 .locatio

use always refreshed frame