/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * 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 android.ddm;

import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
import android.os.Debug;
import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Handle profiling requests.
 */
public class DdmHandleProfiling extends ChunkHandler {

    public static final int CHUNK_MPRS = type("MPRS");
    public static final int CHUNK_MPRE = type("MPRE");
    public static final int CHUNK_MPSS = type("MPSS");
    public static final int CHUNK_MPSE = type("MPSE");
    public static final int CHUNK_MPRQ = type("MPRQ");
    public static final int CHUNK_SPSS = type("SPSS");
    public static final int CHUNK_SPSE = type("SPSE");

    private static final boolean DEBUG = false;
    private static DdmHandleProfiling mInstance = new DdmHandleProfiling();


    /* singleton, do not instantiate */
    private DdmHandleProfiling() {}

    /**
     * Register for the messages we're interested in.
     */
    public static void register() {
        DdmServer.registerHandler(CHUNK_MPRS, mInstance);
        DdmServer.registerHandler(CHUNK_MPRE, mInstance);
        DdmServer.registerHandler(CHUNK_MPSS, mInstance);
        DdmServer.registerHandler(CHUNK_MPSE, mInstance);
        DdmServer.registerHandler(CHUNK_MPRQ, mInstance);
        DdmServer.registerHandler(CHUNK_SPSS, mInstance);
        DdmServer.registerHandler(CHUNK_SPSE, mInstance);
    }

    /**
     * Called when the DDM server connects.  The handler is allowed to
     * send messages to the server.
     */
    public void connected() {}

    /**
     * Called when the DDM server disconnects.  Can be used to disable
     * periodic transmissions or clean up saved state.
     */
    public void disconnected() {}

    /**
     * Handle a chunk of data.
     */
    public Chunk handleChunk(Chunk request) {
        if (DEBUG)
            Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
        int type = request.type;

        if (type == CHUNK_MPRS) {
            return handleMPRS(request);
        } else if (type == CHUNK_MPRE) {
            return handleMPRE(request);
        } else if (type == CHUNK_MPSS) {
            return handleMPSS(request);
        } else if (type == CHUNK_MPSE) {
            return handleMPSEOrSPSE(request, "Method");
        } else if (type == CHUNK_MPRQ) {
            return handleMPRQ(request);
        } else if (type == CHUNK_SPSS) {
            return handleSPSS(request);
        } else if (type == CHUNK_SPSE) {
            return handleMPSEOrSPSE(request, "Sample");
        } else {
            throw new RuntimeException("Unknown packet "
                + ChunkHandler.name(type));
        }
    }

    /*
     * Handle a "Method PRofiling Start" request.
     */
    private Chunk handleMPRS(Chunk request) {
        ByteBuffer in = wrapChunk(request);

        int bufferSize = in.getInt();
        int flags = in.getInt();
        int len = in.getInt();
        String fileName = getString(in, len);
        if (DEBUG)
            Log.v("ddm-heap", "Method profiling start: filename='" + fileName
                + "', size=" + bufferSize + ", flags=" + flags);

        try {
            Debug.startMethodTracing(fileName, bufferSize, flags);
            return null;        // empty response
        } catch (RuntimeException re) {
            return createFailChunk(1, re.getMessage());
        }
    }

    /*
     * Handle a "Method PRofiling End" request.
     */
    private Chunk handleMPRE(Chunk request) {
        byte result;

        try {
            Debug.stopMethodTracing();
            result = 0;
        } catch (RuntimeException re) {
            Log.w("ddm-heap", "Method profiling end failed: "
                + re.getMessage());
            result = 1;
        }

        /* create a non-empty reply so the handler fires on completion */
        byte[] reply = { result };
        return new Chunk(CHUNK_MPRE, reply, 0, reply.length);
    }

    /*
     * Handle a "Method Profiling w/Streaming Start" request.
     */
    private Chunk handleMPSS(Chunk request) {
        ByteBuffer in = wrapChunk(request);

        int bufferSize = in.getInt();
        int flags = in.getInt();
        if (DEBUG) {
            Log.v("ddm-heap", "Method prof stream start: size=" + bufferSize
                + ", flags=" + flags);
        }

        try {
            Debug.startMethodTracingDdms(bufferSize, flags, false, 0);
            return null;        // empty response
        } catch (RuntimeException re) {
            return createFailChunk(1, re.getMessage());
        }
    }

    /*
     * Handle a "Method Profiling w/Streaming End" request or a
     * "Sample Profiling w/Streaming End" request.
     */
    private Chunk handleMPSEOrSPSE(Chunk request, String type) {
        if (DEBUG) {
            Log.v("ddm-heap", type + " prof stream end");
        }

        try {
            Debug.stopMethodTracing();
        } catch (RuntimeException re) {
            Log.w("ddm-heap", type + " prof stream end failed: "
                + re.getMessage());
            return createFailChunk(1, re.getMessage());
        }

        /* VM sent the (perhaps very large) response directly */
        return null;
    }

    /*
     * Handle a "Method PRofiling Query" request.
     */
    private Chunk handleMPRQ(Chunk request) {
        int result = Debug.getMethodTracingMode();

        /* create a non-empty reply so the handler fires on completion */
        byte[] reply = { (byte) result };
        return new Chunk(CHUNK_MPRQ, reply, 0, reply.length);
    }

    /*
     * Handle a "Sample Profiling w/Streaming Start" request.
     */
    private Chunk handleSPSS(Chunk request) {
        ByteBuffer in = wrapChunk(request);

        int bufferSize = in.getInt();
        int flags = in.getInt();
        int interval = in.getInt();
        if (DEBUG) {
            Log.v("ddm-heap", "Sample prof stream start: size=" + bufferSize
                + ", flags=" + flags + ", interval=" + interval);
        }

        try {
            Debug.startMethodTracingDdms(bufferSize, flags, true, interval);
            return null;        // empty response
        } catch (RuntimeException re) {
            return createFailChunk(1, re.getMessage());
        }
    }
}

