/*
 * 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.
 */
#include <stdint.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <utils/StrongPointer.h>

#include "audio/Buffer.h"
#include "Log.h"
#include "audio/AudioProtocol.h"


bool AudioProtocol::sendCommand(AudioParam& param)
{
    mBuffer[0] = htonl(mCommand);
    mBuffer[1] = 0;
    return sendData((char*)mBuffer, 8);
}

bool AudioProtocol::handleReply(const uint32_t* data, AudioParam* param)
{
    if (!checkHeaderId(data, mCommand)) {
        return false;
    }
    if (data[1] != 0) { // no endian change for 0
        LOGE("error in reply %d", ntohl(data[1]));
        return false;
    }
    if (data[2] != 0) {
        LOGE("payload length %d not zero", ntohl(data[2]));
        return false;
    }
    return true;
}

bool AudioProtocol::handleReplyHeader(ClientSocket& socket, uint32_t* data, CommandId& id)
{
    if (!socket.readData((char*)data, REPLY_HEADER_SIZE)) {
        LOGE("handleReplyHeader cannot read");
        return false;
    }
    uint32_t command = ntohl(data[0]);
    if ((command & 0xffff0000) != 0x43210000) {
        LOGE("Wrong header %x %x", command, data[0]);
        return false;
    }
    command = (command & 0xffff) | 0x12340000; // convert to id
    if (command < ECmdStart) {
        LOGE("Wrong header %x %x", command, data[0]);
        return false;
    }
    if (command > (ECmdLast - 1)) {
        LOGE("Wrong header %x %x", command, data[0]);
        return false;
    }
    id = (CommandId)command;
    LOGD("received reply with command %x", command);
    return true;
}

bool AudioProtocol::checkHeaderId(const uint32_t* data, uint32_t command)
{
    if (ntohl(data[0]) != ((command & 0xffff) | 0x43210000)) {
        LOGE("wrong reply ID 0x%x", ntohl(data[0]));
        return false;
    }
    return true;
}


/**
 * param0 u32 data id
 * param1 sp<Buffer>
 */
bool CmdDownload::sendCommand(AudioParam& param)
{
    mBuffer[0] = htonl(ECmdDownload);
    mBuffer[1] = htonl(4 + param.mBuffer->getSize());
    mBuffer[2] = htonl(param.mId);
    if(!sendData((char*)mBuffer, 12)) {
        return false;
    }
    return sendData(param.mBuffer->getData(), param.mBuffer->getSize());
}

/**
 * param0 u32 data id
 * param1 u32 sampling rate
 * param2 u32 mono / stereo(MSB) | mode
 * param3 u32 volume
 * param4 u32 repeat
 */
bool CmdStartPlayback::sendCommand(AudioParam& param)
{
    mBuffer[0] = htonl(ECmdStartPlayback);
    mBuffer[1] = htonl(20);
    mBuffer[2] = htonl(param.mId);
    mBuffer[3] = htonl(param.mSamplingF);
    uint32_t mode = param.mStereo ? 0x80000000 : 0;
    mode |= param.mMode;
    mBuffer[4] = htonl(mode);
    mBuffer[5] = htonl(param.mVolume);
    mBuffer[6] = htonl(param.mNumberRepetition);

    return sendData((char*)mBuffer, 28);
}


/**
 * param0 u32 sampling rate
 * param1 u32 mono / stereo(MSB) | mode
 * param2 u32 volume
 * param3 u32 samples
 */
bool CmdStartRecording::sendCommand(AudioParam& param)
{
    mBuffer[0] = htonl(ECmdStartRecording);
    mBuffer[1] = htonl(16);
    mBuffer[2] = htonl(param.mSamplingF);
    uint32_t mode = param.mStereo ? 0x80000000 : 0;
    mode |= param.mMode;
    mBuffer[3] = htonl(mode);
    mBuffer[4] = htonl(param.mVolume);
    uint32_t samples = param.mBuffer->getSize() / (param.mStereo ? 4 : 2);
    mBuffer[5] = htonl(samples);

    return sendData((char*)mBuffer, 24);
}

/**
 * param0 sp<Buffer>
 */
bool CmdStartRecording::handleReply(const uint32_t* data, AudioParam* param)
{
    if (!checkHeaderId(data, ECmdStartRecording)) {
        return false;
    }
    if (data[1] != 0) { // no endian change for 0
        LOGE("error in reply %d", ntohl(data[1]));
        return false;
    }
    int len = ntohl(data[2]);
    if (len > (int)param->mBuffer->getCapacity()) {
        LOGE("received data %d exceeding buffer capacity %d", len, param->mBuffer->getCapacity());
        // read and throw away
        //Buffer tempBuffer(len);
        //readData(tempBuffer.getData(), len);
        return false;
    }
    if (!readData(param->mBuffer->getData(), len)) {
        return false;
    }
    LOGI("received data %d from device", len);
    param->mBuffer->setHandled(len);
    param->mBuffer->setSize(len);
    return true;
}