1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "layout_parser.h"
17#include "component/component_factory.h"
18#include "components/ui_view.h"
19#include "json_visitor.h"
20#include "log/log.h"
21#include "view_api.h"
22#include "auto_layout.h"
23
24namespace Updater {
25namespace {
26constexpr auto DEFAULT_MODULE = "default";
27constexpr auto COMPONENT_MODULE = "coms";
28constexpr auto COMMON_LABEL = "Common";
29constexpr auto COMMON_TYPE = "type";
30}
31
32class LayoutParser::Impl {
33    using cJSONPtr = std::unique_ptr<cJSON, decltype(&cJSON_Delete)>;
34    AutoLayout &layout = AutoLayout::GetInstance();
35public:
36    bool LoadLayout(const std::vector<std::string> &layoutFiles, std::vector<UxPageInfo> &vec) const
37    {
38        layout.Init();
39        std::vector<UxPageInfo>().swap(vec);
40        UxPageInfo pageInfo = {};
41        for (const auto &file : layoutFiles) {
42            if (!LoadLayout(file, pageInfo)) {
43                LOG(ERROR) << file << " load failed";
44                return false;
45            }
46            vec.push_back(std::move(pageInfo));
47            pageInfo = {};
48        }
49        return true;
50    }
51    bool LoadLayout(const std::string &filename, UxPageInfo &pageInfo) const
52    {
53        JsonNode node {std::filesystem::path {filename}};
54        layout.SetJsonLocation(node);
55        // parse color, id, subpages
56        if (!Visit<SETVAL>(node, pageInfo)) {
57            LOG(ERROR) << "get page info (id, color, subpages) failed";
58            return false;
59        }
60
61        // parse view info
62        if (!ParseViewInfo(node, pageInfo.viewInfos)) {
63            LOG(ERROR) << "component Node parse failed";
64            return false;
65        }
66
67        return true;
68    }
69private:
70    bool ParseViewInfo(const JsonNode &root, std::vector<UxViewInfo> &vec) const
71    {
72        UxViewInfo info {};
73        std::vector<UxViewInfo>().swap(vec);
74        const JsonNode &defaultNode = root[DEFAULT_MODULE];
75        const JsonNode &componentNodes = root[COMPONENT_MODULE];
76        if (componentNodes.Type() != NodeType::ARRAY) {
77            LOG(ERROR) << "Please check whether json file has a coms field";
78            return false;
79        }
80        for (const auto &componentNode : componentNodes) {
81            const JsonNode &comNode = componentNode.get();
82            auto viewType = comNode[COMMON_TYPE].As<std::string>();
83            if (viewType == std::nullopt) {
84                LOG(ERROR) << "Component don't have a type field";
85                return false;
86            }
87            const JsonNode &commonDefault = defaultNode[COMMON_LABEL];
88            if (!Visit<SETVAL>(componentNode, commonDefault, info.commonInfo)) {
89                LOG(ERROR) << "set common info failed";
90                return false;
91            }
92
93            auto it = GetSpecificInfoMap<COMPONENT_TYPE_LIST>().find(*viewType);
94            if (it == GetSpecificInfoMap<COMPONENT_TYPE_LIST>().end()) {
95                LOG(ERROR) << "Can't recognize this type " << *viewType;
96                return false;
97            }
98            info.specificInfo = it->second();
99            auto visitor = [&comNode, &defaultNode] (auto &args) {
100                const JsonNode &defaultComNode = defaultNode[Traits<std::decay_t<decltype(args)>>::STRUCT_KEY];
101                return Visit<SETVAL>(comNode, defaultComNode, args);
102            };
103            if (!std::visit(visitor, info.specificInfo)) {
104                return false;
105            }
106            vec.push_back(std::move(info));
107            info = {};
108        }
109        return true;
110    }
111};
112
113LayoutParser::~LayoutParser() = default;
114
115LayoutParser::LayoutParser() : pImpl_(std::make_unique<Impl>()) { }
116
117LayoutParser &LayoutParser::GetInstance()
118{
119    static LayoutParser layoutParser;
120    return layoutParser;
121}
122
123bool LayoutParser::LoadLayout(const std::string &layoutFile, UxPageInfo &pageInfo) const
124{
125    return pImpl_->LoadLayout(layoutFile, pageInfo);
126}
127
128bool LayoutParser::LoadLayout(const std::vector<std::string> &layoutFiles, std::vector<UxPageInfo> &vec) const
129{
130    return pImpl_->LoadLayout(layoutFiles, vec);
131}
132}  // namespace Updater
133