/*
 * 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 "devhost_service_full.h"
#include "dev_attribute_serialize.h"
#include "devmgr_service_clnt.h"
#include "hdf_base.h"
#include "hdf_device_info.h"
#include "hdf_device_node.h"
#include "hdf_log.h"
#include "osal_message.h"
#include "power_state_token.h"

#define HDF_LOG_TAG devhost_service_full

static int32_t DevHostServiceFullDispatchMessage(struct HdfMessageTask *task, struct HdfMessage *msg)
{
    struct DevHostServiceFull *hostService =
        HDF_SLIST_CONTAINER_OF(struct HdfMessageTask, task, struct DevHostServiceFull, task);

    int status = HDF_SUCCESS;
    switch (msg->messageId) {
        case DEVHOST_MESSAGE_ADD_DEVICE: {
            struct HdfDeviceInfo *attribute = (struct HdfDeviceInfo *)msg->data[0];
            status = DevHostServiceAddDevice(&hostService->super.super, attribute);
            if (status != HDF_SUCCESS) {
                HDF_LOGE("DevHostServiceAddDevice failed and return %{public}d", status);
            }
            break;
        }
        case DEVHOST_MESSAGE_DEL_DEVICE: {
            devid_t devid = (devid_t)((uintptr_t)msg->data[0]);
            status = DevHostServiceDelDevice(&hostService->super.super, devid);
            if (status != HDF_SUCCESS) {
                HDF_LOGE("DevHostServiceDelDevice failed and return %{public}d", status);
            }
            break;
        }
        default: {
            HDF_LOGE("DevHostServiceFullDispatchMessage unknown message %{public}d", msg->messageId);
            break;
        }
    }

    return status;
}

static int DevHostServiceFullOpsDevice(struct IDevHostService *devHostService, uintptr_t parm, int cmdCode)
{
    if (devHostService == NULL) {
        HDF_LOGE("input is null");
        return HDF_FAILURE;
    }
    struct DevHostServiceFull *inst = (struct DevHostServiceFull *)devHostService;
    struct HdfMessageTask *task = &inst->task;
    struct HdfMessage *message = HdfMessageObtain(0);
    if (message == NULL) {
        HDF_LOGE("HdfMessageObtain(0) return null");
        return HDF_ERR_MALLOC_FAIL;
    }

    message->messageId = cmdCode;
    message->data[0] = (void *)parm;
    return task->SendMessage(task, message, true);
}

static int DevHostServiceFullAddDevice(struct IDevHostService *devHostService, const struct HdfDeviceInfo *attribute)
{
    return DevHostServiceFullOpsDevice(devHostService, (uintptr_t)attribute, DEVHOST_MESSAGE_ADD_DEVICE);
}

static int DevHostServiceFullDelDevice(struct IDevHostService *devHostService, devid_t devid)
{
    return DevHostServiceFullOpsDevice(devHostService, (uintptr_t)devid, DEVHOST_MESSAGE_DEL_DEVICE);
}

static int DevHostServiceFullDispatchPowerState(struct HdfDevice *device, uint32_t state)
{
    struct HdfDeviceNode *deviceNode = NULL;
    int ret = HDF_SUCCESS;
    int result = HDF_SUCCESS;

    if (IsPowerWakeState(state)) {
        DLIST_FOR_EACH_ENTRY(deviceNode, &device->devNodes, struct HdfDeviceNode, entry) {
            if (deviceNode->powerToken != NULL) {
                ret = PowerStateChange(deviceNode->powerToken, state);
                if (ret != HDF_SUCCESS) {
                    HDF_LOGE("device %{public}s failed to resume(%{public}d) %{public}d",
                        deviceNode->driver->entry->moduleName, state, ret);
                    result = HDF_FAILURE;
                }
            }
        }
    } else {
        DLIST_FOR_EACH_ENTRY_REVERSE(deviceNode, &device->devNodes, struct HdfDeviceNode, entry) {
            if (deviceNode->powerToken != NULL) {
                ret = PowerStateChange(deviceNode->powerToken, state);
                if (ret != HDF_SUCCESS) {
                    HDF_LOGE("device %{public}s failed to suspend(%{public}d) %{public}d",
                        deviceNode->driver->entry->moduleName, state, ret);
                    result = HDF_FAILURE;
                }
            }
        }
    }

    return result;
}

static uint32_t SysEventToPowerState(uint32_t sysEvent)
{
    switch (sysEvent) {
        case KEVENT_POWER_SUSPEND:
            return POWER_STATE_SUSPEND;
        case KEVENT_POWER_DISPLAY_OFF:
            return POWER_STATE_DOZE_SUSPEND;
        case KEVENT_POWER_RESUME:
            return POWER_STATE_RESUME;
        case KEVENT_POWER_DISPLAY_ON:
            return POWER_STATE_DOZE_RESUME;
        default:
            return POWER_STATE_MAX;
    }
}

static int OnSysEventReceived(
    struct HdfSysEventNotifyNode *self, uint64_t eventClass, uint32_t event, const char *content)
{
    (void)(content);
    if (self == NULL) {
        return HDF_ERR_INVALID_PARAM;
    }

    struct DevHostService *hostService = CONTAINER_OF(self, struct DevHostService, sysEventNotifyNode);
    HDF_LOGI("host receive eventClass=%{public}llu, event=%{public}u", (unsigned long long)eventClass, event);
    return hostService->super.PmNotify(&hostService->super, SysEventToPowerState(event));
}

static int DevHostServiceFullStartService(struct IDevHostService *service)
{
    struct DevHostService *hostService = (struct DevHostService *)service;
    if (hostService == NULL) {
        HDF_LOGE("Start device service failed, hostService is null");
        return HDF_FAILURE;
    }

    int ret = DevmgrServiceClntAttachDeviceHost(hostService->hostId, service);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("failed to start host service, attach host error %{public}d", ret);
        return ret;
    }

    hostService->sysEventNotifyNode.callback = OnSysEventReceived;
    ret = HdfSysEventNotifyRegister(&hostService->sysEventNotifyNode, HDF_SYSEVENT_CLASS_POWER);
    if (ret != HDF_SUCCESS) {
        HDF_LOGW("failed to register power event listener");
    } else {
        HDF_LOGD("host register power event listener success");
    }

    return HDF_SUCCESS;
}

int DevHostServiceFullPmNotify(struct IDevHostService *service, uint32_t state)
{
    struct DevHostService *hostService = (struct DevHostService *)service;
    int result = HDF_SUCCESS;

    if (hostService == NULL || !IsValidPowerState(state)) {
        return HDF_ERR_INVALID_PARAM;
    }

    struct HdfDevice *device = NULL;
    if (IsPowerWakeState(state)) {
        DLIST_FOR_EACH_ENTRY_REVERSE(device, &hostService->devices, struct HdfDevice, node) {
            if (DevHostServiceFullDispatchPowerState(device, state) != HDF_SUCCESS) {
                result = HDF_FAILURE;
            }
        }
    } else {
        DLIST_FOR_EACH_ENTRY(device, &hostService->devices, struct HdfDevice, node) {
            if (DevHostServiceFullDispatchPowerState(device, state)) {
                result = HDF_FAILURE;
            }
        }
    }

    return result;
}

void DevHostServiceFullConstruct(struct DevHostServiceFull *inst)
{
    struct IDevHostService *hostServiceIf = &inst->super.super;
    static struct IHdfMessageHandler handler = {
        .Dispatch = DevHostServiceFullDispatchMessage
    };
    DevHostServiceConstruct(&inst->super);
    hostServiceIf->AddDevice = DevHostServiceFullAddDevice;
    hostServiceIf->DelDevice = DevHostServiceFullDelDevice;
    hostServiceIf->StartService = DevHostServiceFullStartService;
    hostServiceIf->PmNotify = DevHostServiceFullPmNotify;
    HdfMessageLooperConstruct(&inst->looper);
    HdfMessageTaskConstruct(&inst->task, &inst->looper, &handler);
}

void DevHostServiceFullDestruct(struct DevHostServiceFull *inst)
{
    if (inst != NULL) {
        DevHostServiceDestruct(&inst->super);
        if (inst->looper.Stop != NULL) {
            inst->looper.Stop(&inst->looper);
        }
    }
}