/*
 * Copyright (c) 2022-2025 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 "adapter/ios/capability/editing/text_input_connection_impl.h"

#include "iOSTextInputDelegate.h"
#include "iOSTxtInputManager.h"
#import "KeyboardTypeMapper.h"

#include "adapter/ios/capability/editing/text_input_client_handler.h"
#include "frameworks/base/json/json_util.h"
#include "frameworks/base/utils/string_utils.h"
#include "base/log/event_report.h"
#include "base/log/log.h"

namespace OHOS::Ace::Platform {

TextInputConnectionImpl::TextInputConnectionImpl(
    const WeakPtr<TextInputClient>& client, const RefPtr<TaskExecutor>& taskExecutor)
    : TextInputConnection(client, taskExecutor)
{}

TextInputConnectionImpl::TextInputConnectionImpl(
    const WeakPtr<TextInputClient>& client, const RefPtr<TaskExecutor>& taskExecutor,const TextInputConfiguration& config)
    : TextInputConnection(client, taskExecutor)
{
  config_ = config;
}

void TextInputConnectionImpl::Show(bool isFocusViewChanged, int32_t instanceId) {
    auto renderTextField = client_.Upgrade();
    if (!renderTextField) {
        return;
    }
    TextInputAction actionType = config_.action;
    NSString *inputAction = TextInputActionUnspecified;
    if (actionType == TextInputAction::UNSPECIFIED) {
        inputAction = TextInputActionUnspecified;
    } else if (actionType == TextInputAction::GO) {
        inputAction = TextInputActionGo;
    } else if (actionType == TextInputAction::SEARCH) {
        inputAction = TextInputActionSearch;
    } else if (actionType == TextInputAction::SEND) {
        inputAction = TextInputActionSend;
    } else if (actionType == TextInputAction::NEXT) {
        inputAction = TextInputActionNext;
    } else if (actionType == TextInputAction::DONE) {
        inputAction = TextInputActionDone;
    } else if (actionType == TextInputAction::NEW_LINE){
        inputAction = TextInputActionNewline;
    }

    TextInputType inputType = config_.type;
    NSString *inputTypeName = TextInputTypeText;
    NSInteger obscureText = 0;
    if (inputType == TextInputType::TEXT) {
        inputTypeName = TextInputTypeText;
    } else if (inputType == TextInputType::MULTILINE) {
        inputTypeName = TextInputTypeMultiline;
    } else if (inputType == TextInputType::NUMBER || inputType == TextInputType::NUMBER_PASSWORD) {
        inputTypeName = TextInputTypeNumber;
    } else if (inputType == TextInputType::DATETIME) {
        inputTypeName = TextInputTypeDatetime;
    } else if (inputType == TextInputType::PHONE) {
        inputTypeName = TextInputTypePhone;
    } else if (inputType == TextInputType::EMAIL_ADDRESS) {
        inputTypeName = TextInputTypeEmailAddress;
    } else if (inputType == TextInputType::URL) {
        inputTypeName = TextInputTypeURL;
    } else if (inputType == TextInputType::VISIBLE_PASSWORD) {
        inputTypeName = TextInputTypeVisiblePassword;
        obscureText = 1;
    }

    NSString* inputFilter = [NSString stringWithUTF8String:config_.inputFilter.c_str()];
    int32_t maxLength = config_.maxLength;

    int32_t clientId = this->GetClientId();
    LOGE("vailclientid->Show clientId:%d inputaction:%d inputType:%d",clientId,actionType,inputType);
    NSDictionary *configure = @{
        @"actionLabel" : @"",
        @"autocorrect" : @(1),
        @"enableIMEPersonalizedLearning" : @(1),
        @"enableSuggestions" : @(1),
        @"inputAction" : inputAction,
        @"inputType" : @{@"decimal":@"",@"name":inputTypeName,@"signed":@""},
        @"keyboardAppearance" : @"Brightness.default",
        @"obscureText":@(obscureText),
        @"readOnly":@(0),
        @"smartDashesType":@(1),
        @"smartQuotesType":@(1),
        @"textCapitalization":@"TextCapitalization.none",
        @"inputFilter" : inputFilter,
        @"maxLength" : [NSNumber numberWithInt:maxLength]
    };

    auto value = renderTextField->GetInputEditingValue();
    NSString *text = [NSString stringWithCString:value.text.c_str() encoding:NSUTF8StringEncoding];
    NSString *selectionAffinity = @"TextAffinity.downstream";
    if(value.selection.affinity == TextAffinity::UPSTREAM){
        selectionAffinity = @"TextAffinity.upstream";
    }
    NSDictionary *stateDict = @{
        @"text":text?:@"",
        @"selectionBase":@(value.selection.baseOffset),
        @"selectionExtent":@(value.selection.extentOffset),
        @"selectionAffinity":selectionAffinity,
        @"composingBase":@(value.compose.baseOffset),
        @"composingExtent":@(value.compose.extentOffset),
        @"selectionIsDirectional":@(0)
    };
    LOGE("vailclientid->Show sb:%d,se:%d,cb:%d,ce:%d,text:%s",value.selection.baseOffset,value.selection.extentOffset,value.compose.baseOffset,value.compose.extentOffset,text.UTF8String);
    TextInputNoParamsBlock showCallback = ^{
        updateEditingClientBlock textInputCallback = ^(int client, NSDictionary *state){
            if(clientId == client && [state objectForKey:@"text"]){
                NSString *text = [state objectForKey:@"text"];
                NSString *appendText = [state objectForKey:@"appendText"];
                NSInteger selectionBase = [state[@"selectionBase"] intValue];
                NSInteger selectionExtent = [state[@"selectionExtent"] intValue];
                NSInteger composingBase = [state[@"composingBase"] intValue];
                NSInteger composingExtent = [state[@"composingExtent"] intValue];
                LOGE("vailclientid->Show textInputBlock ace:%d nav:%d text:%s",clientId,client,text.UTF8String);
                LOGE("vailclientid->Show textInputBlock sb:%d se:%d cb:%d ce:%d",selectionBase,selectionExtent,composingBase,composingExtent);
                auto textEditingValue = std::make_shared<TextEditingValue>();
                textEditingValue->text = text.UTF8String;
                textEditingValue->appendText = appendText.UTF8String;
                textEditingValue->UpdateSelection(selectionBase,selectionExtent);
                textEditingValue->UpdateCompose(composingBase,composingExtent);
                textEditingValue->isDelete = [state[@"isDelete"] boolValue];
                textEditingValue->unmarkText = [state[@"unmarkText"] boolValue];
                textEditingValue->discardedMarkedText = [state[@"discardedMarkedText"] boolValue];
                TextInputClientHandler::GetInstance().UpdateEditingValue(client, textEditingValue, needFireChangeEvent_);
                needFireChangeEvent_ = true;
            }
        };
        [iOSTxtInputManager sharedInstance].textInputBlock = textInputCallback;
        updateErrorTextBlock errorTextCallback = ^(int client, NSDictionary *state){
            if(clientId == client && [state objectForKey:@"errorText"]){
                NSString *errorText = [state objectForKey:@"errorText"];
                TextInputClientHandler::GetInstance().UpdateInputFilterErrorText(client, [errorText UTF8String]);
            }
        };
        [iOSTxtInputManager sharedInstance].errorTextBlock = errorTextCallback;
        performActionBlock textPerformCallback = ^(int action, int client){
            if(clientId == client){
                TextInputAction actionType = TextInputAction::DONE; 
                switch (action) {
                    case iOSTextInputActionUnspecified:
                        actionType = TextInputAction::UNSPECIFIED;
                        break;
                    case iOSTextInputActionGo:
                        actionType = TextInputAction::GO;
                        break;
                    case iOSTextInputActionSearch:
                        actionType = TextInputAction::SEARCH;
                        break;
                    case iOSTextInputActionSend:
                        actionType = TextInputAction::SEND;
                        break;
                    case iOSTextInputActionNext:
                        actionType = TextInputAction::NEXT;
                        break;
                    case iOSTextInputActionDone:
                        actionType = TextInputAction::DONE;
                        break;
                    case iOSTextInputActionNewline:
                        actionType = TextInputAction::NEW_LINE;
                        break; 
                    default:
                        break;
                }
                TextInputClientHandler::GetInstance().PerformAction(client, actionType);
                if(action == iOSTextInputActionDone){
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if ([iOSTxtInputManager sharedInstance]) {
                            [[iOSTxtInputManager sharedInstance] hideTextInput];
                            [iOSTxtInputManager sharedInstance].inputBoxY = 0.0;
                            [iOSTxtInputManager sharedInstance].inputBoxTopY = 0.0;
                            [iOSTxtInputManager sharedInstance].isDeclarative = false;
                        }
                    });
                }
            }
        };
        [iOSTxtInputManager sharedInstance].textPerformBlock = textPerformCallback;
        [iOSTxtInputManager sharedInstance].inputBoxY = renderTextField->GetEditingBoxY();
        [iOSTxtInputManager sharedInstance].inputBoxTopY = renderTextField->GetEditingBoxTopY();
        [iOSTxtInputManager sharedInstance].isDeclarative = renderTextField->GetEditingBoxModel();
        [[iOSTxtInputManager sharedInstance] setTextInputClient:clientId withConfiguration:configure];
        [[iOSTxtInputManager sharedInstance] setTextInputEditingState:stateDict];
        [[iOSTxtInputManager sharedInstance] showTextInput];
    };
    dispatch_main_async_safe(showCallback);
}

void TextInputConnectionImpl::SetEditingState(const TextEditingValue& value, int32_t instanceId, bool needFireChangeEvent) {
    needFireChangeEvent_ = needFireChangeEvent;
    NSString *text = [NSString stringWithCString:value.text.c_str() encoding:NSUTF8StringEncoding];
    NSString *selectionAffinity = @"TextAffinity.downstream";
    if(value.selection.affinity == TextAffinity::UPSTREAM){
        selectionAffinity = @"TextAffinity.upstream";
    }
    LOGE("vailclientid->SetEditingState sb:%d,se:%d,cb:%d,ce:%d,text:%s",value.selection.baseOffset,value.selection.extentOffset,value.compose.baseOffset,value.compose.extentOffset,text.UTF8String);
    NSDictionary *stateDict = @{
        @"text":text?:@"",
        @"selectionBase":@(value.selection.baseOffset),
        @"selectionExtent":@(value.selection.extentOffset),
        @"selectionAffinity":selectionAffinity,
        @"composingBase":@(value.compose.baseOffset),
        @"composingExtent":@(value.compose.extentOffset),
        @"selectionIsDirectional":@(0)
    };
    dispatch_main_async_safe(^{
        [[iOSTxtInputManager sharedInstance] setTextInputEditingState:stateDict];
    });
}

void TextInputConnectionImpl::Close(int32_t instanceId) {
    LOGE("vail->iOSTxtInput::Close");
    TextInputNoParamsBlock closeCallback = ^{
        [[iOSTxtInputManager sharedInstance] clearTextInputClient];
        [[iOSTxtInputManager sharedInstance] hideTextInput];
        [iOSTxtInputManager sharedInstance].inputBoxY = 0.0;
        [iOSTxtInputManager sharedInstance].inputBoxTopY = 0.0;
        [iOSTxtInputManager sharedInstance].isDeclarative = false;
    };
    dispatch_main_async_safe(closeCallback);
}

void TextInputConnectionImpl::FinishComposing(int32_t instanceId)
{
    LOGI("vailclientid->FinishComposing");
    TextInputNoParamsBlock finishComposingCallback = ^{
        [[iOSTxtInputManager sharedInstance] finishComposing];
    };
    dispatch_main_async_safe(finishComposingCallback);
}

}
// namespace OHOS::Ace::Platform