/*
 * Copyright (C) 2012 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.
 */


#ifndef CTSAUDIO_REMOTEAUDIO_H
#define CTSAUDIO_REMOTEAUDIO_H

#include <map>

#include <utils/Looper.h>
#include <utils/String8.h>
#include <utils/StrongPointer.h>
#include <utils/threads.h>

#include "audio/Buffer.h"
#include "AudioProtocol.h"
#include "ClientSocket.h"
#include "Semaphore.h"

class CommandHandler;
/**
 * Tcp communication runs in a separate thread,
 * and client can communicate using public APIs
 * Assumption: only one command at a time. No other command can come
 * while a command is pending.
 */
class RemoteAudio: public android::Thread {
public:

    RemoteAudio(ClientSocket& socket);
    virtual ~RemoteAudio();

    /** launch a thread, and connect to host */
    bool init(int port);
    bool downloadData(const android::String8 name, android::sp<Buffer>& buffer, int& id);
    // <0 : not found
    int getDataId(const android::String8& name);
    bool startPlayback(bool stereo, int samplingF, int mode, int volume,
            int id, int numberRepetition);
    void stopPlayback();
    bool waitForPlaybackCompletion();
    // buffer.getSize() determines number of samples
    bool startRecording(bool stereo, int samplingF, int mode, int volume,
            android::sp<Buffer>& buffer);
    bool waitForRecordingCompletion();
    void stopRecording();

    bool getDeviceInfo(android::String8& data);
    /** should be called before RemoteAudio is destroyed */
    void release();

private:
    RemoteAudio(const RemoteAudio&);

    bool threadLoop();
    void wakeClient(bool result);
    void cleanup(bool notifyClient);

    bool handlePacket();
    static int socketRxCallback(int fd, int events, void* data);

    class CommandHandler;
    void sendCommand(android::sp<android::MessageHandler>& command);

    // this is just semaphore wait without any addition
    bool waitForCompletion(android::sp<android::MessageHandler>& command, int timeInMSec);
    // common code for waitForXXXCompletion
    bool waitForPlaybackOrRecordingCompletion(
            android::sp<android::MessageHandler>& commandHandler);
    // common code for stopXXX
    void doStop(android::sp<android::MessageHandler>& commandHandler, AudioProtocol::CommandId id);

    CommandHandler* toCommandHandler(android::sp<android::MessageHandler>& command) {
        return reinterpret_cast<CommandHandler*>(command.get());
    };

private:
    bool mExitRequested;
    bool mInitResult;
    // used only for notifying successful init
    Semaphore mInitWait;


    enum EventId {
        EIdSocket = 1,
    };
    static const int CLIENT_WAIT_TIMEOUT_MSEC = 2000;
    int mPort;
    ClientSocket& mSocket;


    android::sp<android::Looper> mLooper;

    friend class CommandHandler;

    class CommandHandler: public android::MessageHandler {
    public:
        enum ClientCommands {
            EExit = 1,
        };
        CommandHandler(RemoteAudio& thread, int command)
            : mThread(thread),
              mMessage(command),
              mNotifyOnReply(false),
              mActive(false) {};
        virtual ~CommandHandler() {};
        void handleMessage(const android::Message& message);
        bool timedWait(int timeInMSec) {
            return mClientWait.timedWait(timeInMSec);
        };
        AudioParam& getParam() {
            return mParam;
        };
        android::Message& getMessage() {
            return mMessage;
        };

    private:
        RemoteAudio& mThread;
        AudioParam mParam;
        Semaphore mClientWait;
        android::Mutex mStateLock;
        android::Message mMessage;
        bool mResult;
        bool mNotifyOnReply;
        bool mActive;
        friend class RemoteAudio;
    };
    android::sp<android::MessageHandler> mDownloadHandler;
    android::sp<android::MessageHandler> mPlaybackHandler;
    android::sp<android::MessageHandler> mRecordingHandler;
    android::sp<android::MessageHandler> mDeviceInfoHandler;

    AudioProtocol* mCmds[AudioProtocol::ECmdLast - AudioProtocol::ECmdStart];
    int mDownloadId;
    std::map<int, android::sp<Buffer> > mBufferList;
    std::map<android::String8, int> mIdMap;
};



#endif // CTSAUDIO_REMOTEAUDIO_H