/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * 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 "devmgr_service_proxy.h"
#include "devhost_service_stub.h"
#include "device_token_stub.h"
#include "devmgr_service.h"
#include "devmgr_service_stub.h"
#include "devsvc_manager_clnt.h"
#include "hdf_base.h"
#include "hdf_log.h"
#include "hdf_sbuf.h"
#include "osal_mem.h"

#define HDF_LOG_TAG devmgr_service_proxy

int DevmgrServiceProxyAttachDeviceHost(struct IDevmgrService *inst, uint16_t hostId, struct IDevHostService *service)
{
    int status = HDF_FAILURE;
    struct HdfSBuf *data = HdfSbufTypedObtain(SBUF_IPC);
    struct HdfSBuf *reply = HdfSbufTypedObtain(SBUF_IPC);
    struct HdfRemoteDispatcher *dipatcher = NULL;
    struct HdfRemoteService *remoteService = NULL;
    struct DevmgrServiceProxy *serviceProxy = (struct DevmgrServiceProxy *)inst;
    struct DevHostServiceStub *hostStub = (struct DevHostServiceStub *)service;
    if ((serviceProxy->remote == NULL) || (data == NULL) || (reply == NULL)) {
        HDF_LOGE("DevmgrServiceProxyAttachDeviceHost failed, host id is %{public}u", hostId);
        goto FINISHED;
    }
    remoteService = serviceProxy->remote;
    dipatcher = remoteService->dispatcher;
    if (!HdfRemoteServiceWriteInterfaceToken(remoteService, data) ||
        !HdfSbufWriteInt32(data, hostId) ||
        HdfSbufWriteRemoteService(data, hostStub->remote) != HDF_SUCCESS) {
        HDF_LOGE("failed to attach host, write sbuf error");
        goto FINISHED;
    }
    status = dipatcher->Dispatch(remoteService, DEVMGR_SERVICE_ATTACH_DEVICE_HOST, data, reply);
    HDF_LOGI("Attach device host dispatch finish, status is %{public}d", status);
FINISHED:
    if (reply != NULL) {
        HdfSbufRecycle(reply);
    }
    if (data != NULL) {
        HdfSbufRecycle(data);
    }
    return status;
}

int DevmgrServiceProxyAttachDevice(struct IDevmgrService *inst, struct IHdfDeviceToken *token)
{
    int status = HDF_FAILURE;
    struct HdfSBuf *data = HdfSbufTypedObtain(SBUF_IPC);
    struct HdfSBuf *reply = HdfSbufTypedObtain(SBUF_IPC);
    struct DevmgrServiceProxy *serviceProxy = (struct DevmgrServiceProxy *)inst;
    if (serviceProxy == NULL || serviceProxy->remote == NULL || data == NULL || reply == NULL || token == NULL) {
        HDF_LOGE("DevmgrServiceProxyAttachDevice failed");
        goto FINISHED;
    }
    struct HdfRemoteService *remoteService = serviceProxy->remote;
    if (!HdfRemoteServiceWriteInterfaceToken(remoteService, data) || !HdfSbufWriteInt32(data, token->devid)) {
        goto FINISHED;
    }

    status = remoteService->dispatcher->Dispatch(remoteService, DEVMGR_SERVICE_ATTACH_DEVICE, data, reply);
FINISHED:
    if (reply != NULL) {
        HdfSbufRecycle(reply);
    }
    if (data != NULL) {
        HdfSbufRecycle(data);
    }
    return status;
}

int DevmgrServiceProxyDetachDevice(struct IDevmgrService *inst, devid_t devid)
{
    int status = HDF_FAILURE;
    struct HdfSBuf *data = HdfSbufTypedObtain(SBUF_IPC);
    struct HdfSBuf *reply = HdfSbufTypedObtain(SBUF_IPC);
    struct DevmgrServiceProxy *serviceProxy = (struct DevmgrServiceProxy *)inst;
    if (serviceProxy == NULL || serviceProxy->remote == NULL || data == NULL || reply == NULL) {
        HDF_LOGE("DevmgrServiceProxyDetachDevice failed");
        goto FINISHED;
    }
    struct HdfRemoteService *remoteService = serviceProxy->remote;
    if (!HdfRemoteServiceWriteInterfaceToken(remoteService, data) || !HdfSbufWriteInt32(data, devid)) {
        goto FINISHED;
    }

    status = remoteService->dispatcher->Dispatch(remoteService, DEVMGR_SERVICE_DETACH_DEVICE, data, reply);
FINISHED:
    if (reply != NULL) {
        HdfSbufRecycle(reply);
    }
    if (data != NULL) {
        HdfSbufRecycle(data);
    }
    return status;
}
int DevmgrServiceProxyLoadDevice(struct IDevmgrService *inst, const char *svcName)
{
    struct DevmgrServiceProxy *serviceProxy = (struct DevmgrServiceProxy *)inst;
    if (serviceProxy == NULL || serviceProxy->remote == NULL || svcName == NULL) {
        HDF_LOGE("DevmgrServiceProxyLoadDevice failed");
        return HDF_ERR_INVALID_PARAM;
    }

    struct HdfSBuf *data = HdfSbufTypedObtain(SBUF_IPC);
    struct HdfRemoteService *remoteService = serviceProxy->remote;
    if (!HdfRemoteServiceWriteInterfaceToken(remoteService, data) || !HdfSbufWriteString(data, svcName)) {
        HdfSbufRecycle(data);
        return HDF_FAILURE;
    }

    int status = remoteService->dispatcher->Dispatch(remoteService, DEVMGR_SERVICE_LOAD_DEVICE, data, NULL);
    HdfSbufRecycle(data);
    return status;
}

int DevmgrServiceProxyUnLoadDevice(struct IDevmgrService *inst, const char *svcName)
{
    struct DevmgrServiceProxy *serviceProxy = (struct DevmgrServiceProxy *)inst;
    if (serviceProxy == NULL || serviceProxy->remote == NULL || svcName == NULL) {
        HDF_LOGE("DevmgrServiceProxyLoadDevice failed");
        return HDF_ERR_INVALID_PARAM;
    }

    struct HdfSBuf *data = HdfSbufTypedObtain(SBUF_IPC);
    struct HdfRemoteService *remoteService = serviceProxy->remote;
    if (!HdfRemoteServiceWriteInterfaceToken(remoteService, data) || !HdfSbufWriteString(data, svcName)) {
        HdfSbufRecycle(data);
        return HDF_FAILURE;
    }

    int status = remoteService->dispatcher->Dispatch(remoteService, DEVMGR_SERVICE_UNLOAD_DEVICE, data, NULL);

    HdfSbufRecycle(data);
    return status;
}

static void DevmgrServiceProxyConstruct(struct DevmgrServiceProxy *inst)
{
    struct IDevmgrService *pvtbl = (struct IDevmgrService *)inst;
    pvtbl->AttachDeviceHost = DevmgrServiceProxyAttachDeviceHost;
    pvtbl->AttachDevice = DevmgrServiceProxyAttachDevice;
    pvtbl->DetachDevice = DevmgrServiceProxyDetachDevice;
    pvtbl->LoadDevice = DevmgrServiceProxyLoadDevice;
    pvtbl->UnloadDevice = DevmgrServiceProxyUnLoadDevice;
    pvtbl->StartService = NULL;
}

static struct IDevmgrService *DevmgrServiceProxyObtain(struct HdfRemoteService *service)
{
    if (service != NULL) {
        struct DevmgrServiceProxy *demgrServicProxy =
            (struct DevmgrServiceProxy *)OsalMemCalloc(sizeof(struct DevmgrServiceProxy));
        if (demgrServicProxy != NULL) {
            demgrServicProxy->remote = service;
            DevmgrServiceProxyConstruct(demgrServicProxy);
            return &demgrServicProxy->super;
        }
    }
    HDF_LOGE("DevmgrServiceProxyObtain failed");
    return NULL;
}

struct HdfObject *DevmgrServiceProxyCreate(void)
{
    static struct IDevmgrService *instance = NULL;
    if (instance != NULL) {
        return (struct HdfObject *)instance;
    }
    struct IDevSvcManager *serviceManagerIf = NULL;
    struct DevSvcManagerClnt *serviceManager = (struct DevSvcManagerClnt *)DevSvcManagerClntGetInstance();
    if ((serviceManager == NULL) || (serviceManager->devSvcMgrIf == NULL)) {
        HDF_LOGE("Fail to Create Service Manager Client");
        return NULL;
    }
    serviceManagerIf = serviceManager->devSvcMgrIf;
    if (serviceManagerIf->GetService == NULL) {
        HDF_LOGE("Get Service is not implement!!!");
        return NULL;
    }
    struct HdfRemoteService *remote =
        (struct HdfRemoteService *)serviceManagerIf->GetService(serviceManagerIf, DEVICE_MANAGER_SERVICE);
    if (remote != NULL) {
        if (!HdfRemoteServiceSetInterfaceDesc(remote, "HDI.IDeviceManager.V1_0")) {
            HDF_LOGE("%{public}s: failed to init interface desc", __func__);
            HdfRemoteServiceRecycle(remote);
            return NULL;
        }
        instance = DevmgrServiceProxyObtain(remote);
    }

    return (struct HdfObject *)instance;
}

void DevmgrServiceProxyRelease(struct HdfObject *object)
{
    struct DevmgrServiceProxy *instance = (struct DevmgrServiceProxy *)object;
    if (instance != NULL) {
        if (instance->remote != NULL) {
            HdfRemoteServiceRecycle(instance->remote);
            instance->remote = NULL;
        }
        OsalMemFree(instance);
    }
}