/*
 * Copyright 2016, 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.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "ArcVideoService"

#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <media/arcvideobridge/IArcVideoBridge.h>
#include <utils/Log.h>

#include <base/bind.h>
#include <base/bind_helpers.h>
#include <mojo/edk/embedder/embedder.h>
#include <mojo/public/cpp/bindings/binding.h>

#include <arc/ArcBridgeSupport.h>
#include <arc/ArcService.h>
#include <arc/Future.h>
#include <arc/IArcBridgeService.h>
#include <arc/MojoProcessSupport.h>
#include <video.mojom.h>

namespace {

// [MinVersion] of OnVideoInstanceReady method in arc_bridge.mojom.
constexpr int kMinimumArcBridgeHostVersion = 6;

void onCaptureResult(arc::Future<arc::MojoBootstrapResult>* future, uint32_t version,
                     mojo::ScopedHandle handle, const std::string& token) {
    mojo::edk::ScopedPlatformHandle scoped_platform_handle;
    MojoResult result =
            mojo::edk::PassWrappedPlatformHandle(handle.release().value(), &scoped_platform_handle);
    if (result != MOJO_RESULT_OK) {
        ALOGE("Received invalid file descriptor.");
        future->set(arc::MojoBootstrapResult());
        return;
    }

    base::ScopedFD fd(scoped_platform_handle.release().handle);
    future->set(arc::MojoBootstrapResult(std::move(fd), token, version));
}

}  // namespace

namespace arc {

class VideoService : public mojom::VideoInstance,
                     public ArcService,
                     public android::BnArcVideoBridge {
public:
    explicit VideoService(MojoProcessSupport* mojoProcessSupport)
          : mMojoProcessSupport(mojoProcessSupport), mBinding(this) {
        mMojoProcessSupport->arc_bridge_support().requestArcBridgeProxyAsync(
                this, kMinimumArcBridgeHostVersion);
    }

    ~VideoService() override { mMojoProcessSupport->disconnect(&mBinding, &mHostPtr); }

    // VideoInstance overrides:
    void InitDeprecated(mojom::VideoHostPtr hostPtr) override {
        Init(std::move(hostPtr), base::Bind(&base::DoNothing));
    }

    void Init(mojom::VideoHostPtr hostPtr, const InitCallback& callback) override {
        ALOGV("Init");
        mHostPtr = std::move(hostPtr);
        // A method must be called while we are still in a Mojo thread so the
        // proxy can perform lazy initialization and be able to be called from
        // non-Mojo threads later.
        // This also caches the version number so it can be obtained by calling
        // .version().
        mHostPtr.QueryVersion(base::Bind(
            [](const InitCallback& callback, uint32_t version) {
                ALOGI("VideoService ready (version=%d)", version);
                callback.Run();
            },
            callback));
        ALOGV("Init done");
    }

    // ArcService overrides:
    void ready(mojom::ArcBridgeHostPtr* bridgeHost) override {
        (*bridgeHost)->OnVideoInstanceReady(mBinding.CreateInterfacePtrAndBind());
    }

    void versionMismatch(uint32_t version) override {
        ALOGE("ArcBridgeHost version %d, does not support video (version %d)\n", version,
              kMinimumArcBridgeHostVersion);
    }

    // BnArcVideoBridge overrides:
    MojoBootstrapResult bootstrapVideoAcceleratorFactory() override {
        ALOGV("VideoService::bootstrapVideoAcceleratorFactory");

        Future<MojoBootstrapResult> future;
        mMojoProcessSupport->mojo_thread().getTaskRunner()->PostTask(
                FROM_HERE, base::Bind(&VideoService::bootstrapVideoAcceleratorFactoryOnMojoThread,
                                      base::Unretained(this), &future));
        return future.get();
    }

    int32_t hostVersion() override {
        ALOGV("VideoService::hostVersion");
        return mHostPtr.version();
    }

private:
    void bootstrapVideoAcceleratorFactoryOnMojoThread(Future<MojoBootstrapResult>* future) {
        if (!mHostPtr) {
            ALOGE("mHostPtr is not ready yet");
            future->set(MojoBootstrapResult());
            return;
        }
        mHostPtr->OnBootstrapVideoAcceleratorFactory(
                base::Bind(&onCaptureResult, base::Unretained(future), mHostPtr.version()));
    }

    // Outlives VideoService.
    MojoProcessSupport* const mMojoProcessSupport;
    mojo::Binding<mojom::VideoInstance> mBinding;
    mojom::VideoHostPtr mHostPtr;
};

}  // namespace arc

namespace android {

int register_android_server_ArcVideoService() {
    defaultServiceManager()->addService(
            String16("android.os.IArcVideoBridge"),
            new arc::VideoService(arc::MojoProcessSupport::getLeakyInstance()));
    return 0;
}

}  // namespace android
