/*
 * Copyright (c) 2024 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 "object_resource.h"

#include <meta/api/metadata_util.h>
#include <meta/base/memfile.h>
#include <meta/ext/serialization/serializer.h>
#include <meta/interface/intf_object_registry.h>
#include <meta/interface/serialization/intf_exporter.h>
#include <meta/interface/serialization/intf_exporter_state.h>
#include <meta/interface/serialization/intf_importer.h>

#include "resource_placeholder.h"

META_BEGIN_NAMESPACE()

BASE_NS::Uid ObjectResource::GetResourceType() const
{
    return type_.ToUid();
}
void ObjectResource::SetResourceType(const ObjectId& id)
{
    type_ = id;
}
ReturnError ObjectResource::Export(IExportContext& c) const
{
    if (SerialiseAsResourceId(c) && !c.IsTopLevelObject()) {
        if (!type_.IsValid()) {
            CORE_LOG_W("Invalid resource type");
            return GenericError::FAIL;
        }
        return ExportResourceId(c);
    }
    return Serializer(c) & NamedValue("resourceType", type_) & AutoSerialize();
}
ReturnError ObjectResource::Import(IImportContext& c)
{
    return Serializer(c) & NamedValue("resourceType", type_) & AutoSerialize();
}

BASE_NS::string ObjectResourceType::GetResourceName() const
{
    return "ObjectResource";
}
BASE_NS::Uid ObjectResourceType::GetResourceType() const
{
    return type_.ToUid();
}
CORE_NS::IResource::Ptr ObjectResourceType::LoadResource(const StorageInfo& s) const
{
    CORE_NS::IResource::Ptr res;
    if (s.payload) {
        if (auto importer = GetObjectRegistry().Create<IFileImporter>(META_NS::ClassId::JsonImporter)) {
            importer->SetUserContext(interface_pointer_cast<IObject>(s.context));
            importer->SetResourceManager(s.self);
            res = interface_pointer_cast<CORE_NS::IResource>(importer->Import(*s.payload));
            if (!res || res->GetResourceType() != type_.ToUid()) {
                CORE_LOG_W("Invalid resource");
                return nullptr;
            }
        }
    }
    return res;
}
bool ObjectResourceType::SaveResource(const CORE_NS::IResource::ConstPtr& p, const StorageInfo& s) const
{
    auto rexport = [&](CORE_NS::IFile& sink) {
        bool res = true;
        if (!p || p->GetResourceType() != type_.ToUid()) {
            CORE_LOG_W("Invalid resource");
            return false;
        }
        if (auto exporter = GetObjectRegistry().Create<IFileExporter>(META_NS::ClassId::JsonExporter)) {
            exporter->SetUserContext(interface_pointer_cast<IObject>(s.context));
            exporter->SetResourceManager(s.self);
            exporter->SetMetadata(META_NS::SerMetadataValues()
                                      .SetVersion({1, 0})
                                      .SetType("ObjectResource")
                                      .Set("sub-type", type_.ToString()));
            res = exporter->Export(sink, interface_pointer_cast<IObject>(p));
        }
        return res;
    };
    bool res = true;
    if (s.payload) {
        res = rexport(*s.payload);
    } else if (s.options && p) {
        // the options are also used to collect dependencies, if so, do a dummy save to serialise and collect
        if (auto collect = interface_cast<META_NS::ICollectResources>(s.context)) {
            MemFile temp;
            res = rexport(temp);
        }
    }
    return res;
}
bool ObjectResourceType::ReloadResource(const StorageInfo& s, const CORE_NS::IResource::Ptr&) const
{
    return false;
}
void ObjectResourceType::SetResourceType(const ObjectId& id)
{
    type_ = id;
}

bool ObjectResourceOptions::ApplyOptions(CORE_NS::IResource&, const CORE_NS::ResourceContextPtr& context) const
{
    return false;
}
bool ObjectResourceOptions::UpdateOptions(const CORE_NS::IResource&, const CORE_NS::ResourceContextPtr& context)
{
    return false;
}
bool ObjectResourceOptions::Merge(const IResourceOptions&)
{
    return false;
}

bool ObjectResourceOptions::Load(
    CORE_NS::IFile& options, const CORE_NS::ResourceManagerPtr& rman, const CORE_NS::ResourceContextPtr& context)
{
    bool res = false;
    auto importer = GetObjectRegistry().Create<IFileImporter>(META_NS::ClassId::JsonImporter);
    if (!importer) {
        return false;
    }
    importer->SetResourceManager(rman);
    importer->SetUserContext(interface_pointer_cast<IObject>(context));
    if (auto obj = importer->Import(options)) {
        if (auto oro = interface_cast<IObjectResourceOptions>(obj)) {
            SetBaseResource(oro->GetBaseResource());
            if (auto in = interface_cast<IMetadata>(obj)) {
                GetAttachmentContainer(true)->RemoveAll();
                META_NS::Clone(*in, *this);
                res = true;
            }
        }
    }
    return res;
}
bool ObjectResourceOptions::Save(
    CORE_NS::IFile& options, const CORE_NS::ResourceManagerPtr& rman, const CORE_NS::ResourceContextPtr& context) const
{
    auto exporter = GetObjectRegistry().Create<IFileExporter>(META_NS::ClassId::JsonExporter);
    if (!exporter) {
        return false;
    }
    exporter->SetResourceManager(rman);
    exporter->SetUserContext(interface_pointer_cast<IObject>(context));
    exporter->SetMetadata(META_NS::SerMetadataValues().SetVersion({1, 0}).SetType("ObjectResourceOptions"));
    if (!exporter->Export(options, GetSelf())) {
        return false;
    }
    if (auto collect = interface_cast<META_NS::ICollectResources>(context)) {
        auto bres = GetBaseResource();
        if (bres.IsValid()) {
            collect->AddResource(CORE_NS::ResourceIdContext{bres});
        }
    }
    return true;
}
IProperty::Ptr ObjectResourceOptions::GetProperty(BASE_NS::string_view name)
{
    auto i = GetSelf<META_NS::IMetadata>();
    return i ? i->GetProperty(name, META_NS::MetadataQuery::EXISTING) : nullptr;
}
void ObjectResourceOptions::SetProperty(const IProperty::Ptr& p)
{
    if (auto i = GetSelf<META_NS::IMetadata>()) {
        if (auto prop = GetProperty(p->GetName())) {
            i->RemoveProperty(prop);
        }
        i->AddProperty(p);
    }
}
META_NS::ReturnError ObjectResourceOptions::Export(META_NS::IExportContext& c) const
{
    return Serializer(c) & AutoSerialize() & NamedValue("baseResource.name", baseResource_.name) &
           NamedValue("baseResource.group", baseResource_.group);
}
META_NS::ReturnError ObjectResourceOptions::Import(META_NS::IImportContext& c)
{
    return Serializer(c) & AutoSerialize() & NamedValue("baseResource.name", baseResource_.name) &
           NamedValue("baseResource.group", baseResource_.group);
}

META_END_NAMESPACE()