/*
 * Copyright (C) 2022 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 "accessibility_element_operator_stub.h"
#include "accessibility_element_operator_callback_proxy.h"
#include "accessibility_element_info_parcel.h"
#include "hilog_wrapper.h"
#include <cinttypes>

#define SWITCH_BEGIN(code) switch (code) {
#define SWITCH_CASE(case_code, func)     \
    case case_code: {                    \
        result_code = func(data, reply); \
        break;                           \
    }

#define SWITCH_END()                                                                                \
    default: {                                                                                      \
        result_code = ERR_CODE_DEFAULT;                                                             \
        HILOG_WARN("AccessibilityElementOperatorStub::OnRemoteRequest, default case, need check."); \
        break;                                                                                      \
    }                                                                                               \
        }

#define ACCESSIBILITY_ELEMENT_OPERATOR_STUB_CASES()                                                               \
    SWITCH_CASE(AccessibilityInterfaceCode::SEARCH_BY_ACCESSIBILITY_ID, HandleSearchElementInfoByAccessibilityId) \
    SWITCH_CASE(AccessibilityInterfaceCode::SEARCH_BY_TEXT, HandleSearchElementInfosByText)                       \
    SWITCH_CASE(AccessibilityInterfaceCode::FIND_FOCUSED_INFO, HandleFindFocusedElementInfo)                      \
    SWITCH_CASE(AccessibilityInterfaceCode::FOCUS_FIND, HandleFocusFind)                                          \
    SWITCH_CASE(AccessibilityInterfaceCode::PERFORM_ACTION_ELEMENT, HandleExecuteAction)                          \
    SWITCH_CASE(AccessibilityInterfaceCode::CURSOR_POSITION, HandleGetCursorPosition)                             \
    SWITCH_CASE(AccessibilityInterfaceCode::CLEAR_FOCUS, HandleClearFocus)                                        \
    SWITCH_CASE(AccessibilityInterfaceCode::OUTSIDE_TOUCH, HandleOutsideTouch)                                    \
    SWITCH_CASE(AccessibilityInterfaceCode::SET_CHILDTREEID, HandleSetChildTreeIdAndWinId)                        \
    SWITCH_CASE(AccessibilityInterfaceCode::SET_BELONGTREEID, HandleSetBelongTreeId)                              \
    SWITCH_CASE(AccessibilityInterfaceCode::SET_PARENTWINDOWID, HandleSetParentWindowId)                          \
    SWITCH_CASE(AccessibilityInterfaceCode::SEARCH_BY_WINDOW_ID, HandleSearchDefaultFocusedByWindowId)            \
    SWITCH_CASE(AccessibilityInterfaceCode::SEARCH_BY_SPECIFIC_PROPERTY, HandleSearchElementInfoBySpecificProperty) \
    SWITCH_CASE(AccessibilityInterfaceCode::ASAC_FOCUS_MOVE_SEARCH_WITH_CONDITION, HandleFocusMoveSearchWithCondition) \
    SWITCH_CASE(AccessibilityInterfaceCode::ASAC_UPDATE_ACCESSIBILITY_ELEMENT_INFO,                               \
        HandleUpdateCustomAccessibilityProperty)                                                                  \
    SWITCH_CASE(AccessibilityInterfaceCode::ASAC_ADD_ACCESSIBILITY_VIRTUAL_NODE,                                  \
        HandleAddAccessibilityVirtualNode)                                                                        \
    SWITCH_CASE(AccessibilityInterfaceCode::ASAC_REMOVE_ACCESSIBILITY_VIRTUAL_NODE,                               \
        HandleRemoveAccessibilityVirtualNode)                                                                     \

namespace OHOS {
namespace Accessibility {
constexpr int32_t ERR_CODE_DEFAULT = -1000;

AccessibilityElementOperatorStub::AccessibilityElementOperatorStub()
{
}

AccessibilityElementOperatorStub::~AccessibilityElementOperatorStub()
{
}

int AccessibilityElementOperatorStub::OnRemoteRequest(uint32_t code, MessageParcel &data,
    MessageParcel &reply, MessageOption &option)
{
    HILOG_DEBUG("cmd = %{public}u, flags = %{public}d", code, option.GetFlags());
    std::u16string descriptor = AccessibilityElementOperatorStub::GetDescriptor();
    std::u16string remoteDescriptor = data.ReadInterfaceToken();
    if (descriptor != remoteDescriptor) {
        HILOG_INFO("local descriptor is not equal to remote");
        return ERR_INVALID_STATE;
    }

    ErrCode result_code = ERR_NONE;
    SWITCH_BEGIN(code)
    ACCESSIBILITY_ELEMENT_OPERATOR_STUB_CASES()
    SWITCH_END()

    if (result_code != ERR_CODE_DEFAULT) {
        return result_code;
    }

    return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
}

ErrCode AccessibilityElementOperatorStub::HandleSearchElementInfoByAccessibilityId(MessageParcel &data,
    MessageParcel &reply)
{
    HILOG_DEBUG();

    int64_t elementId = data.ReadInt64();
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }

    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    int32_t mode = data.ReadInt32();
    bool isFilter = data.ReadBool();
    SearchElementInfoByAccessibilityId(elementId, requestId, callback, mode, isFilter);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleSearchDefaultFocusedByWindowId(MessageParcel &data,
    MessageParcel &reply)
{
    HILOG_DEBUG();
 
    int32_t windowId = data.ReadInt32();
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }
 
    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    int32_t mode = data.ReadInt32();
    bool isFilter = data.ReadBool();
    SearchDefaultFocusedByWindowId(windowId, requestId, callback, mode, isFilter);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleSearchElementInfosByText(MessageParcel &data,
    MessageParcel &reply)
{
    HILOG_DEBUG();

    int64_t elementId = data.ReadInt64();
    std::string text = data.ReadString();
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }

    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    SearchElementInfosByText(elementId, text, requestId, callback);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleFindFocusedElementInfo(MessageParcel &data,
    MessageParcel &reply)
{
    HILOG_DEBUG();

    int64_t elementId = data.ReadInt64();
    int32_t focusType = data.ReadInt32();
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }

    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    FindFocusedElementInfo(elementId, focusType, requestId, callback);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleFocusFind(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();

    int64_t elementId = data.ReadInt64();
    int32_t direction = data.ReadInt32();
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }

    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    FocusMoveSearch(elementId, direction, requestId, callback);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleExecuteAction(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();
    std::vector<std::string> argumentKey;
    std::vector<std::string> argumentValue;
    int64_t elementId = data.ReadInt64();
    int32_t action = data.ReadInt32();
    if (!data.ReadStringVector(&argumentKey)) {
        return ERR_TRANSACTION_FAILED;
    }
    if (!data.ReadStringVector(&argumentValue)) {
        return ERR_TRANSACTION_FAILED;
    }
    if (argumentKey.size() != argumentValue.size()) {
        return ERR_TRANSACTION_FAILED;
    }
    std::map<std::string, std::string> arguments;
    for (size_t i = 0;i < argumentKey.size(); i++) {
        arguments.insert(std::pair<std::string, std::string>(argumentKey[i], argumentValue[i]));
    }
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }

    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    ExecuteAction(elementId, action, arguments, requestId, callback);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleGetCursorPosition(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();
    int64_t elementId = data.ReadInt64();
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }

    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    GetCursorPosition(elementId, requestId, callback);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleClearFocus(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();

    ClearFocus();

    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleOutsideTouch(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();

    OutsideTouch();

    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleSetChildTreeIdAndWinId(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();
    int64_t elementId = data.ReadInt64();
    int32_t treeId = data.ReadInt32();
    int32_t childWindowId = data.ReadInt32();
    SetChildTreeIdAndWinId(elementId, treeId, childWindowId);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleSetBelongTreeId(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();
    int32_t treeId = data.ReadInt32();

    SetBelongTreeId(treeId);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleSetParentWindowId(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();
    int32_t iParentWindowId = data.ReadInt32();

    SetParentWindowId(iParentWindowId);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleSearchElementInfoBySpecificProperty(MessageParcel &data,
    MessageParcel &reply)
{
    int64_t elementId = data.ReadInt64();

    SpecificPropertyParam param;
    param.propertyTarget = data.ReadString();
    param.propertyType = static_cast<SEARCH_TYPE>(data.ReadUint32());

    int32_t requestId = data.ReadInt32();

    HILOG_DEBUG("elementId:%{public}" PRId64 ", propertyTarget:%{public}s, propertyType:%{public}u, "
        "requestId:%{public}d", elementId, param.propertyTarget.c_str(),
        static_cast<uint32_t>(param.propertyType), requestId);

    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }

    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }

    SearchElementInfoBySpecificProperty(elementId, param, requestId, callback);
    return NO_ERROR;
}

ErrCode AccessibilityElementOperatorStub::HandleFocusMoveSearchWithCondition(MessageParcel &data, MessageParcel &reply)
{
    HILOG_DEBUG();
    sptr<AccessibilityElementInfoParcel> info = data.ReadStrongParcelable<AccessibilityElementInfoParcel>();
    if (info == nullptr) {
        HILOG_ERROR("ReadStrongParcelable<AccessibilityElementInfoParcel> failed");
        return ERR_INVALID_VALUE;
    }
    AccessibilityFocusMoveParam param;
    param.direction = static_cast<FocusMoveDirection>(data.ReadInt32());
    param.condition = static_cast<DetailCondition>(data.ReadInt32());
    param.type = static_cast<FocusRuleType>(data.ReadInt32());
    param.parentId = data.ReadInt64();
    param.detectParent = data.ReadBool();
    int requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }
    sptr<IAccessibilityElementOperatorCallback> callback = iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr");
        return ERR_INVALID_VALUE;
    }
    FocusMoveSearchWithCondition(*info, param, requestId, callback);
    return NO_ERROR;
}

bool AccessibilityElementOperatorStub::ReadAccessibilityVirtualNode(MessageParcel &data,
    AccessibilityVirtualNode &accessibilityVirtualNode)
{
    accessibilityVirtualNode.SetId(data.ReadInt64());
    accessibilityVirtualNode.SetText(data.ReadString());
    accessibilityVirtualNode.SetAccessibilityText(data.ReadString());
    accessibilityVirtualNode.SetAccessibilityGroup(data.ReadBool());
    accessibilityVirtualNode.SetAccessibilityLevel(data.ReadString());

    Rect rect;
    int32_t leftTopX = data.ReadInt32();
    int32_t leftTopY = data.ReadInt32();
    int32_t rightBottomX = data.ReadInt32();
    int32_t rightBottomY = data.ReadInt32();
    rect.SetLeftTopScreenPostion(leftTopX, leftTopY);
    rect.SetRightBottomScreenPostion(rightBottomX, rightBottomY);
    accessibilityVirtualNode.SetRect(rect);

    accessibilityVirtualNode.SetCheckable(data.ReadBool());
    accessibilityVirtualNode.SetChecked(data.ReadBool());
    accessibilityVirtualNode.SetClickable(data.ReadBool());
    accessibilityVirtualNode.SetEnabled(data.ReadBool());
    accessibilityVirtualNode.SetSelected(data.ReadBool());
    accessibilityVirtualNode.SetCustomComponentType(data.ReadString());

    Accessibility::AccessibilityVirtualPoint point;
    point.SetX(data.ReadInt32());
    point.SetY(data.ReadInt32());
    accessibilityVirtualNode.SetPoint(point);
    accessibilityVirtualNode.SetAccessibilityFocused(data.ReadBool());
    accessibilityVirtualNode.SetParentId(data.ReadInt64());
    int32_t childNodeCount = data.ReadInt32();
    std::vector<int64_t> childNodeIds;
    for (int32_t i = 0; i < childNodeCount; i++) {
        childNodeIds.push_back(data.ReadInt64());
    }
    accessibilityVirtualNode.SetChildNodeIds(childNodeIds);
    accessibilityVirtualNode.SetElementId(data.ReadInt64());
    accessibilityVirtualNode.SetWindowId(data.ReadInt32());
    accessibilityVirtualNode.SetSupportAction(data.ReadUint64());
    return true;
}

ErrCode AccessibilityElementOperatorStub::HandleUpdateCustomAccessibilityProperty(MessageParcel &data,
    MessageParcel &reply)
{
    HILOG_INFO();
    int64_t elementId = data.ReadInt64();
    AccessibilityVirtualNode accessibilityVirtualNode;
    
    if (!ReadAccessibilityVirtualNode(data, accessibilityVirtualNode)) {
        HILOG_ERROR("ReadAccessibilityVirtualNode failed");
        return ERR_INVALID_VALUE;
    }

    int32_t requestId = data.ReadInt32();

    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }
    sptr<IAccessibilityElementOperatorCallback> callback =
        iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr.");
        return ERR_INVALID_VALUE;
    }
    UpdateCustomAccessibilityProperty(elementId, accessibilityVirtualNode, requestId, callback);
    return NO_ERROR;
}
 
ErrCode AccessibilityElementOperatorStub::HandleAddAccessibilityVirtualNode(MessageParcel &data,
    MessageParcel &reply)
{
    HILOG_DEBUG();
    int64_t rootId = data.ReadInt64();
    int32_t nodeCount = data.ReadInt32();
    std::vector<AccessibilityVirtualNode> nodes;
    for (int32_t i = 0; i < nodeCount; i++) {
        AccessibilityVirtualNode node;
        if (!ReadAccessibilityVirtualNode(data, node)) {
            HILOG_ERROR("ReadAccessibilityVirtualNode failed for node[%{public}d]", i);
            return ERR_INVALID_VALUE;
        }
        nodes.push_back(node);
    }
    int32_t requestId = data.ReadInt32();
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }
    sptr<IAccessibilityElementOperatorCallback> callback =
        iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr.");
        return ERR_INVALID_VALUE;
    }
    AddAccessibilityVirtualNode(rootId, nodes, requestId, callback);
    return NO_ERROR;
}
 
ErrCode AccessibilityElementOperatorStub::HandleRemoveAccessibilityVirtualNode(MessageParcel &data,
    MessageParcel &reply)
{
    HILOG_DEBUG();
    int64_t id = data.ReadInt64();
    int32_t requestId = data.ReadInt32();
 
    sptr<IRemoteObject> remote = data.ReadRemoteObject();
    if (remote == nullptr) {
        HILOG_ERROR("remote is nullptr.");
        return ERR_INVALID_VALUE;
    }
    sptr<IAccessibilityElementOperatorCallback> callback =
        iface_cast<IAccessibilityElementOperatorCallback>(remote);
    if (callback == nullptr) {
        HILOG_ERROR("callback is nullptr.");
        return ERR_INVALID_VALUE;
    }
    RemoveAccessibilityVirtualNode(id, requestId, callback);
    return NO_ERROR;
}
} // namespace Accessibility
} // namespace OHOS