/*
 * Copyright 2014 NAVER Corp.
 *
 * 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 com.navercorp.pinpoint.plugin.user.interceptor;

import com.navercorp.pinpoint.bootstrap.context.*;
import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor;
import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScope;
import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScopeInvocation;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.plugin.user.UserConstants;
import com.navercorp.pinpoint.plugin.user.UserIncludeMethodDescriptor;

/**
 * @author jaehong.kim
 */
public class UserIncludeMethodInterceptor implements AroundInterceptor {
    private static final UserIncludeMethodDescriptor USER_INCLUDE_METHOD_DESCRIPTOR = new UserIncludeMethodDescriptor();
    private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
    private final boolean isDebug = logger.isDebugEnabled();

    private TraceContext traceContext;
    private MethodDescriptor descriptor;

    public UserIncludeMethodInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) {
        this.traceContext = traceContext;
        this.descriptor = methodDescriptor;

        traceContext.cacheApi(USER_INCLUDE_METHOD_DESCRIPTOR);
    }

    @Override
    public void before(Object target, Object[] args) {
        if (isDebug) {
            logger.beforeInterceptor(target, args);
        }

        Trace trace = traceContext.currentRawTraceObject();
        if (trace == null) {
            trace = traceContext.newTraceObject(TraceType.USER);
            if (isDebug) {
                logger.debug("New user trace and sampled {}", trace.canSampled());
            }

            // record root span.
            final SpanRecorder recorder = trace.getSpanRecorder();
            recorder.recordServiceType(ServiceType.STAND_ALONE);
            recorder.recordApi(USER_INCLUDE_METHOD_DESCRIPTOR);
        }

        if(trace.getTraceType() == TraceType.USER) {
            // entry point in.
            final EntryPointChecker entryPointChecker = trace.getEntryPointChecker();
            entryPointChecker.entry();
        }

        if (!trace.canSampled()) {
            return;
        }

        trace.traceBlockBegin();
    }


    @Override
    public void after(Object target, Object[] args, Object result, Throwable throwable) {
        if (isDebug) {
            logger.afterInterceptor(target, args);
        }

        final Trace trace = traceContext.currentRawTraceObject();
        if (trace == null) {
            return;
        }

        if(trace.getTraceType() == TraceType.USER) {
            final EntryPointChecker entryPointChecker = trace.getEntryPointChecker();
            entryPointChecker.leave();
        }

        if (!trace.canSampled()) {
            if (trace.getTraceType() == TraceType.USER) {
                final EntryPointChecker entryPointChecker = trace.getEntryPointChecker();
                if(!entryPointChecker.isNested()) {
                    if (isDebug) {
                        logger.debug("Remove trace. {}", trace);
                    }
                    traceContext.removeTraceObject();
                }
            }

            return;
        }

        try {
            final SpanEventRecorder recorder = trace.currentSpanEventRecorder();
            recorder.recordApi(descriptor);
            recorder.recordServiceType(UserConstants.USER_INCLUDE);
            recorder.recordException(throwable);
        } finally {
            trace.traceBlockEnd();
            if (isDebug) {
                logger.debug("Closed user trace. {}", trace.getCallStackFrameId());
            }

            if (trace.getTraceType() == TraceType.USER) {
                final EntryPointChecker entryPointChecker = trace.getEntryPointChecker();
                if(!entryPointChecker.isNested()) {
                    if (isDebug) {
                        logger.debug("Closed user trace. {}", trace);
                    }
                    trace.close();
                    traceContext.removeTraceObject();
                }
            }
        }
    }
}
@@ -2701,16 +2701,118 @@
 int in.%0A
+            if (isDebug) %7B%0A                logger.debug(%22Entry user trace. %7B%7D%22, trace);%0A            %7D%0A
         
@@ -2881,32 +2881,32 @@
 PointChecker();%0A
-
             entr
@@ -3406,32 +3406,134 @@
 aceType.USER) %7B%0A
+            if (isDebug) %7B%0A                logger.debug(%22Leave user trace. %7B%7D%22, trace);%0A            %7D%0A
             fina
@@ -3967,16 +3967,21 @@
 %22Remove 
+user 
 trace. %7B
@@ -4447,134 +4447,8 @@
 ();%0A
-            if (isDebug) %7B%0A                logger.debug(%22Closed user trace. %7B%7D%22, trace.getCallStackFrameId());%0A            %7D%0A%0A
     
@@ -4721,17 +4721,27 @@
 g(%22Close
-d
+ and remove
  user tr

fix log message.