/*
 * Copyright (c) 2022-2026 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/clipboard/clipboard_impl.h"

#include "base/image/pixel_map.h"
#include "frameworks/base/utils/utils.h"
#include "multiType_record_impl.h"
#include "pasteData_impl.h"

#import <Foundation/Foundation.h>
#import <UIKit/UIApplication.h>
#import <UIKit/UIKit.h>
#import <MobileCoreServices/UTCoreTypes.h>
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
#import <UniformTypeIdentifiers/UTCoreTypes.h>
#endif

namespace OHOS::Ace::Platform {

static NSString *const CUSTOM_SPAN_TYPE = @"com.arkuix.custom-span-type";

void ClipboardImpl::AddPixelMapRecord(const RefPtr<PasteDataMix>& pasteData, const RefPtr<PixelMap>& pixmap) {}
void ClipboardImpl::AddImageRecord(const RefPtr<PasteDataMix>& pasteData, const std::string& uri) {}
void ClipboardImpl::AddTextRecord(const RefPtr<PasteDataMix>& pasteData, const std::string& selectedStr)
{
    CHECK_NULL_VOID(pasteData);
    auto pasteDataImpl = AceType::DynamicCast<PasteDataImpl>(pasteData);
    CHECK_NULL_VOID(pasteDataImpl);
    auto multiTypeRecord = AceType::MakeRefPtr<MultiTypeRecordImpl>();
    if (selectedStr.empty()) {
        LOGE("Text data is empty.");
        return;
    }
    multiTypeRecord->SetPlainText(selectedStr);
    pasteDataImpl->AddRecord(multiTypeRecord);
}

void ClipboardImpl::AddSpanStringRecord(const RefPtr<PasteDataMix>& pasteData, std::vector<uint8_t>& data)
{
    CHECK_NULL_VOID(pasteData);
    auto pasteDataImpl = AceType::DynamicCast<PasteDataImpl>(pasteData);
    CHECK_NULL_VOID(pasteDataImpl);
    auto multiTypeRecord = AceType::MakeRefPtr<MultiTypeRecordImpl>();
    if (data.empty()) {
        LOGE("SpanString data is empty.");
        return;
    }
    multiTypeRecord->SetSpanStringBuffer(data);
    pasteDataImpl->AddRecord(multiTypeRecord);
}

void ClipboardImpl::AddMultiTypeRecord(
    const RefPtr<PasteDataMix>& pasteData, const RefPtr<MultiTypeRecordMix>& multiTypeRecord)
{
    CHECK_NULL_VOID(pasteData);
    auto pasteDataImpl = AceType::DynamicCast<PasteDataImpl>(pasteData);
    CHECK_NULL_VOID(pasteDataImpl);

    auto multiTypeRecordImpl = AceType::DynamicCast<MultiTypeRecordImpl>(multiTypeRecord);
    CHECK_NULL_VOID(multiTypeRecordImpl);
    if (multiTypeRecordImpl->GetUri().empty()){
        pasteDataImpl->AddRecord(multiTypeRecordImpl);
    }
}

void ClipboardImpl::SetData(const RefPtr<PasteDataMix>& pasteData, CopyOptions copyOption)
{
    auto peData = AceType::DynamicCast<PasteDataImpl>(pasteData);
    CHECK_NULL_VOID(peData);
    taskExecutor_->PostTask(
        [peData]() {
            auto records = peData->GetRecords();
            UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
            NSMutableArray<NSDictionary<NSString *, id> *> *items = [NSMutableArray array];
            for (auto it = records.rbegin(); it != records.rend(); ++it) {
                auto multiTypeRecord = AceType::DynamicCast<MultiTypeRecordImpl>(*it);
                if (!multiTypeRecord) {
                    continue;
                }
                NSMutableDictionary *item = [NSMutableDictionary dictionary];
                if (!multiTypeRecord->GetPlainText().empty()) {
                    NSString *plainText = [NSString stringWithUTF8String:multiTypeRecord->GetPlainText().c_str()];
                    [item setObject:plainText forKey:UTTypeUTF8PlainText.identifier];
                }
                if (!multiTypeRecord->GetSpanStringBuffer().empty()) {
                    NSData *spanData = [NSData dataWithBytes:multiTypeRecord->GetSpanStringBuffer().data()
                                                    length:multiTypeRecord->GetSpanStringBuffer().size()];
                    [item setObject:spanData forKey:CUSTOM_SPAN_TYPE];
                }
                [items addObject:item];
            }
            [pasteboard setItems:items];
        },
        TaskExecutor::TaskType::PLATFORM, "ArkUIClipboardSetMixDataWithCopyOption", PriorityType::IMMEDIATE);
}

void ClipboardImpl::GetData(const std::function<void(const std::string&, bool isLastRecord)>& textCallback,
    const std::function<void(const RefPtr<PixelMap>&, bool isLastRecord)>& pixelMapCallback,
    const std::function<void(const std::string&, bool isLastRecord)>& urlCallback, bool syncMode)
{}

void ClipboardImpl::GetSpanStringData(
    const std::function<void(std::vector<std::vector<uint8_t>>&, const std::string&, bool&)>& callback, bool syncMode)
{
    if (callback) {
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        NSArray<NSDictionary<NSString *, id> *> *items = pasteboard.items;
        std::vector<std::vector<uint8_t>> arrays;
        std::string text = "";
        bool isMultiTypeRecord = false;
        bool isFromAutoFill = false;
        NSArray *types = @[];
        if (@available(iOS 15.0, *)) {
            types = @[UTTypeUTF8PlainText.identifier, UTTypePlainText.identifier, UTTypeUTF16PlainText.identifier,
                        UTTypeUTF16ExternalPlainText.identifier];
        } else {
            types = @[(NSString *)kUTTypeUTF8PlainText, (NSString *)kUTTypePlainText, (NSString *)kUTTypeUTF16PlainText,
                        (NSString *)kUTTypeUTF16ExternalPlainText];
        }
        for (NSDictionary<NSString *, id> *item in items) {
            NSString *plainText = nil;
            for (NSString *type in types) {
                plainText = item[type];
                if (plainText) {
                    break;
                }
            }
            NSData *spanData = item[@"com.arkuix.custom-span-type"];

            if (plainText) {
                text += plainText.UTF8String;
            }

            if (spanData && plainText != nil) {
                const unsigned char *bytes = static_cast<const unsigned char *>(spanData.bytes);
                arrays.emplace_back(std::vector<uint8_t>(bytes, bytes + spanData.length));
            }
        }
        callback(arrays, text, isMultiTypeRecord);
    }
}

void ClipboardImpl::GetSpanStringData(
    const std::function<void(std::vector<std::vector<uint8_t>>&, const std::string&, bool&, bool&)>& callback,
    bool syncMode)
{
    if (callback) {
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        NSArray<NSDictionary<NSString *, id> *> *items = pasteboard.items;
        std::vector<std::vector<uint8_t>> arrays;
        std::string text = "";
        bool isMultiTypeRecord = false;
        bool isFromAutoFill = false;
        NSString *type = @"";
        if (@available(iOS 15.0, *)) {
            type = UTTypeUTF8PlainText.identifier;
        } else {
            type = (NSString *)kUTTypeUTF8PlainText;
        }
        for (NSDictionary<NSString *, id> *item in [items reverseObjectEnumerator]) {
            NSString *plainText = nil;
            plainText = item[type];
            if (plainText && [plainText isKindOfClass:[NSString class]]) {
                text += plainText.UTF8String;
            }
            NSData *spanData = item[CUSTOM_SPAN_TYPE];
            if (spanData && plainText != nil) {
                const unsigned char *bytes = static_cast<const unsigned char *>(spanData.bytes);
                arrays.emplace_back(std::vector<uint8_t>(bytes, bytes + spanData.length));
            }
        }
        callback(arrays, text, isMultiTypeRecord, isFromAutoFill);
    }
}

RefPtr<PasteDataMix> ClipboardImpl::CreatePasteDataMix()
{
    return AceType::MakeRefPtr<PasteDataImpl>();
}

void ClipboardImpl::SetData(const std::string& data, CopyOptions copyOption, bool isDragData)
{
    if (taskExecutor_) {
        taskExecutor_->PostTask([weak = AceType::WeakClaim(RawPtr(taskExecutor_)),data]{
            auto executor = weak.Upgrade();
            if(executor){
                executor->PostTask([data]{
                    UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                    NSMutableDictionary *item = [NSMutableDictionary dictionary];
                    NSString *plainText = [NSString stringWithCString:data.c_str() encoding:NSUTF8StringEncoding];
                    if (@available(iOS 15.0, *)) {
                        [item setObject:plainText forKey:UTTypeUTF8PlainText.identifier];
                    } else {
                        [item setObject:plainText forKey:(NSString *)kUTTypeUTF8PlainText];
                    }
                    [pasteboard setItems:@[item]];
                },TaskExecutor::TaskType::BACKGROUND, "ArkUI-XClipboardImplSetDataBackground");
            }
        },TaskExecutor::TaskType::PLATFORM, "ArkUI-XClipboardImplSetDataPlatform");
    }
}

void ClipboardImpl::GetData(const std::function<void(const std::string&, bool)>& callback, bool syncMode)
{
    if (callback) {
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        NSArray<NSDictionary<NSString *, id> *> *items = pasteboard.items;
        NSMutableString *allText = [NSMutableString string];
        NSString *type = @"";
        if (@available(iOS 15.0, *)) {
            type = UTTypeUTF8PlainText.identifier;
        } else {
            type = (NSString *)kUTTypeUTF8PlainText;
        }
        for (NSDictionary<NSString *, id> *item in [items reverseObjectEnumerator]) {
            NSString *plainText = @"";
            plainText = item[type];
            if (plainText && [plainText isKindOfClass:[NSString class]]) {
                [allText appendString:plainText];
            }
        }
        auto data = allText.UTF8String;
        callback(data, false);
    }
}

void ClipboardImpl::GetData(const std::function<void(const std::string&)>& callback, bool syncMode)
{
    if (callback) {
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        NSArray<NSDictionary<NSString *, id> *> *items = pasteboard.items;
        NSMutableString *allText = [NSMutableString string];
        for (NSDictionary<NSString *, id> *item in items) {
            NSString *plainText = @"";
            if (@available(iOS 15.0, *)) {
                plainText = item[UTTypeUTF8PlainText.identifier];
            } else {
                plainText = item[(NSString *)kUTTypeUTF8PlainText];
            }
            if (plainText) {
                [allText appendString:plainText];
            }
        }
        auto data = allText.UTF8String;
        callback(data);
    }
}

void ClipboardImpl::HasData(const std::function<void(bool hasData, bool isAutoFill)>& callback)
{
    if (callback) {
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        if (pasteboard) {
            bool hasCustomData = [pasteboard containsPasteboardTypes:@[CUSTOM_SPAN_TYPE] inItemSet:nil];
            callback(pasteboard.hasStrings || hasCustomData, false);
        }
    }
}

void ClipboardImpl::HasDataType(const std::function<void(bool hasData, bool isAutoFill)>& callback,
    const std::vector<std::string>& mimeTypes)
{
    HasData(callback);
}

void ClipboardImpl::SetPixelMapData(const RefPtr<PixelMap>& pixmap, CopyOptions copyOption)
{
    if (!taskExecutor_ || !callbackSetClipboardPixmapData_) {
        LOGE("Failed to set the pixmap data to clipboard.");
        return;
    }
    taskExecutor_->PostTask([callbackSetClipboardPixmapData = callbackSetClipboardPixmapData_,
                                pixmap] { callbackSetClipboardPixmapData(pixmap); },
        TaskExecutor::TaskType::UI, "ArkUI-XClipboardImplSetPixelMapData");
}

void ClipboardImpl::GetPixelMapData(const std::function<void(const RefPtr<PixelMap>&)>& callback, bool syncMode)
{
    if (!taskExecutor_ || !callbackGetClipboardPixmapData_ || !callback) {
        LOGE("Failed to get the pixmap data from clipboard.");
        return;
    }
    taskExecutor_->PostTask([callbackGetClipboardPixmapData = callbackGetClipboardPixmapData_,
                                callback] { callback(callbackGetClipboardPixmapData()); },
        TaskExecutor::TaskType::UI, "ArkUI-XClipboardImplGetPixelMapData");
}

void ClipboardImpl::RegisterCallbackSetClipboardPixmapData(CallbackSetClipboardPixmapData callback)
{
    callbackSetClipboardPixmapData_ = callback;
}

void ClipboardImpl::RegisterCallbackGetClipboardPixmapData(CallbackGetClipboardPixmapData callback)
{
    callbackGetClipboardPixmapData_ = callback;
}

void ClipboardImpl::Clear()
{
    if (taskExecutor_) {
        taskExecutor_->PostTask([weak = AceType::WeakClaim(RawPtr(taskExecutor_))]{
            auto executor = weak.Upgrade();
            if(executor){
                executor->PostTask([]{
                    UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                    pasteboard.items = @[];
                },TaskExecutor::TaskType::BACKGROUND, "ArkUI-XClipboardImplClearBackground");
            }
        },TaskExecutor::TaskType::PLATFORM, "ArkUI-XClipboardImplClearPlatform");
    }
}

} // namespace OHOS::Ace::Platform