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

// This PoC is written using aac_dec_fuzzer.cpp as reference.
#include "aacdecoder_lib.h"
#include <stdlib.h>

constexpr uint8_t kNumberOfLayers = 1;
constexpr uint8_t kMaxChannelCount = 8;
constexpr uint8_t kConfigBytes = 92;
constexpr uint32_t kMaxOutBufferSize = 2048 * kMaxChannelCount;

class Codec {
public:
  Codec() = default;
  ~Codec() { deInitDecoder(); }
  bool initDecoder();
  void decodeFrames(UCHAR *data, UINT size);
  void deInitDecoder();

private:
  HANDLE_AACDECODER mAacDecoderHandle = nullptr;
  AAC_DECODER_ERROR mErrorCode = AAC_DEC_OK;
  bool mConfigFlag = false;
};

bool Codec::initDecoder() {
  mAacDecoderHandle = aacDecoder_Open(TT_MP4_ADIF, kNumberOfLayers);
  if (!mAacDecoderHandle) {
    return false;
  }
  return true;
}

void Codec::deInitDecoder() {
  aacDecoder_Close(mAacDecoderHandle);
  mAacDecoderHandle = nullptr;
}

void Codec::decodeFrames(UCHAR *data, UINT size) {
  while (size > 0) {
    UINT inputSize = size;
    UINT valid = size;

    if (!mConfigFlag) {
      inputSize = kConfigBytes;
      aacDecoder_ConfigRaw(mAacDecoderHandle, &data, &inputSize);
      data += kConfigBytes;
      size -= kConfigBytes;
      mConfigFlag = true;
      continue;
    }

    aacDecoder_Fill(mAacDecoderHandle, &data, &inputSize, &valid);
    INT_PCM outputBuf[kMaxOutBufferSize];
    do {
        mErrorCode = aacDecoder_DecodeFrame(mAacDecoderHandle, outputBuf, kMaxOutBufferSize, 0);
    } while (mErrorCode == AAC_DEC_OK);
    UINT offset = inputSize - valid;
    data += offset;
    size = valid;
  }
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    return EXIT_FAILURE;
  }

  FILE *fp = fopen(argv[1], "rb");
  if (!fp) {
    return EXIT_FAILURE;
  }

  fseek(fp, 0, SEEK_END);
  UINT size = ftell(fp);
  fseek(fp, 0, SEEK_SET);
  UCHAR *data = new UCHAR[size];
  fread(data, sizeof(UCHAR), size, fp);
  fclose(fp);
  fp = nullptr;

  Codec *codec = new Codec();
  if (!codec) {
    delete[] data;
    return EXIT_FAILURE;
  }

  if (codec->initDecoder()) {
    codec->decodeFrames((UCHAR *)(data), static_cast<UINT>(size));
  }

  delete codec;
  delete[] data;
  return EXIT_SUCCESS;
}
