/**
 * Copyright (C) 2019 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 <SharedMemoryBuffer.h>
#include <binder/MemoryDealer.h>
#include <media/OMXBuffer.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/foundation/AMessage.h>

#include "OMX_Component.h"

using namespace android;

struct DummyOMXObserver : public BnOMXObserver {
 public:
  DummyOMXObserver() {}
  virtual void onMessages(const std::list<omx_message> &) {}

 protected:
  virtual ~DummyOMXObserver() {}
};

bool fuzzIOMXQcomVpx() {
  const char *name = "OMX.qcom.video.decoder.vp8";
  int fenceFd = -1;

  int inSize = 6230016 * 4;
  int outSize = 159744 * 4;

  sp<IMemory> memory;
  sp<IOMXNode> mOMXNode;
  sp<IOMX> mOmx;

  OMXClient client;
  if (client.connect() != OK) {
    ALOGE("OMXClient connect == NULL");
    return false;
  }

  mOmx = client.interface();
  if (mOmx == NULL) {
    ALOGE("OMXClient interface mOmx == NULL");
    client.disconnect();
    return false;
  }

  sp<DummyOMXObserver> observerDec = new DummyOMXObserver();

  ALOGI("-----------decode------------");
  status_t err = mOmx->allocateNode(name, observerDec, &mOMXNode);
  if (err != OK) {
    ALOGE("%s node allocation failed", name);
    client.disconnect();
    return false;
  }

  // change state from loaded to idle
  err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
  if (err != OK) {
    ALOGE("sendCommand is failed in OMX_StateIdle, err: %d", err);
    mOMXNode->freeNode();
    client.disconnect();
    return false;
  }

  // Input
  sp<MemoryDealer> dealerIn = new MemoryDealer(inSize);
  IOMX::buffer_id inBufferId = 0;
  memory = dealerIn->allocate(inSize);
  if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) {
    ALOGE("memory allocate failed for port index 0, err: %d", err);
    mOMXNode->freeNode();
    client.disconnect();
    return false;
  }

  /*
   * keep running to check whether mediaserver crashes
   * Error conditions are not checked for usebuffer/emptybuffer/fillbuffer
   */

  OMXBuffer omxInBuf(memory);
  memset(memory->unsecurePointer(), 0xCF, inSize);
  err = mOMXNode->useBuffer(0, omxInBuf, &inBufferId);
  ALOGI("useBuffer, port index 0, err: %d", err);

  sp<AMessage> inputFormat = new AMessage;
  sp<MediaCodecBuffer> codecDataIn;
  codecDataIn = new SharedMemoryBuffer(inputFormat, memory);
  OMXBuffer omxInBufShared(codecDataIn);

  // Output
  sp<MemoryDealer> dealerOut = new MemoryDealer(outSize);
  IOMX::buffer_id outBufferId = 0;
  memory = dealerOut->allocate(outSize);
  if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) {
    ALOGE("memory allocate failed for port index 1, err: %d", err);
    mOMXNode->freeNode();
    client.disconnect();
    return false;
  }
  OMXBuffer omxOutBuf(memory);
  err = mOMXNode->useBuffer(1, omxOutBuf, &outBufferId);
  ALOGI("useBuffer, port index 1, err: %d", err);

  sp<AMessage> outputFormat = new AMessage;
  sp<MediaCodecBuffer> codecDataOut;
  codecDataOut = new SharedMemoryBuffer(outputFormat, memory);
  OMXBuffer omxOutBufShared(codecDataOut);

  // change state from idle to executing
  err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateExecuting);
  if (err != OK) {
    ALOGE("sendCommand is failed in OMX_StateExecuting, err: %d", err);
    mOMXNode->freeNode();
    client.disconnect();
    return false;
  }

  // keep running to check whether mediaserver crashes
  err = mOMXNode->emptyBuffer(inBufferId, omxInBufShared, 0, 0, fenceFd);
  ALOGI("emptyBuffer, err: %d", err);

  err = mOMXNode->fillBuffer(outBufferId, omxOutBufShared, fenceFd);
  ALOGI("fillBuffer, err: %d", err);

  // free node
  err = mOMXNode->freeNode();
  ALOGI("freeNode, err: %d", err);

  client.disconnect();
  return true;
}

int main() { return (int)(!fuzzIOMXQcomVpx()); }
