1/*
2 * Copyright (c) 2021 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 "ecmascript/intl/global_intl_helper.h"
17
18#include "ecmascript/intl/locale_helper.h"
19#include "ecmascript/ecma_macros.h"
20#include "ecmascript/ecma_vm.h"
21#include "ecmascript/global_env.h"
22#include "ecmascript/object_factory.h"
23#include "ecmascript/js_object.h"
24#include "ecmascript/mem/c_string.h"
25
26namespace panda::ecmascript::intl {
27GlobalIntlHelper::GlobalIntlHelper(JSThread *thread,
28    const GlobalFormatterType matterType)
29{
30    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
31    auto iter = optionMaps.find(matterType);
32    if (iter == optionMaps.end()) {
33        switch (matterType) {
34            case GlobalFormatterType::Collator:
35                InitCollatorData(globalConst);
36                break;
37            case GlobalFormatterType::SimpleDateFormatDate:
38            case GlobalFormatterType::SimpleDateFormatTime:
39            case GlobalFormatterType::DateFormatter:
40                InitDateData(globalConst);
41                break;
42            case GlobalFormatterType::NumberFormatter:
43                InitNumberData(globalConst);
44                break;
45        }
46    }
47}
48
49uint64_t *GlobalIntlHelper::ConvertDateToUnit(uint64_t timestamp)
50{
51    const uint64_t baseYear = 1900;
52    const uint64_t baseTimeSec = 60;
53    const uint64_t baseTimeUTC = 8;
54    const uint64_t baseTime = 1000;
55    auto milli = timestamp + static_cast<uint64_t>(baseTimeUTC *
56        baseTimeSec * baseTimeSec * baseTime);
57    auto mTime = std::chrono::milliseconds(milli);
58    auto tp = std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>(mTime);
59    auto tt = std::chrono::system_clock::to_time_t(tp);
60    std::tm *now = std::gmtime(&tt);
61    dateUnitArr = new uint64_t[6] {
62        static_cast<uint64_t>(now->tm_year + baseYear),
63        static_cast<uint64_t>(now->tm_mon + 1),
64        static_cast<uint64_t>(now->tm_mday),
65        static_cast<uint64_t>(now->tm_hour),
66        static_cast<uint64_t>(now->tm_min),
67        static_cast<uint64_t>(now->tm_sec)
68    };
69    return dateUnitArr;
70}
71
72void GlobalIntlHelper::InitCollatorData(const GlobalEnvConstants *globalConst)
73{
74    std::map<std::string, JSHandle<JSTaggedValue>> collatorMap;
75    collatorMap["localeMatcher"] = globalConst->GetHandledLocaleMatcherString();
76    collatorMap["usage"] = globalConst->GetHandledUsageString();
77    collatorMap["sensitivity"] = globalConst->GetHandledSensitivityString();
78    collatorMap["ignorePunctuation"] = globalConst->GetHandledIgnorePunctuationString();
79    collatorMap["numeric"] = globalConst->GetHandledNumericString();
80    collatorMap["caseFirst"] = globalConst->GetHandledCaseFirstString();
81    collatorMap["collation"] = globalConst->GetHandledCollationString();
82    optionMaps.insert(make_pair(GlobalFormatterType::Collator, collatorMap));
83}
84
85void GlobalIntlHelper::InitDateData(const GlobalEnvConstants *globalConst)
86{
87    std::map<std::string, JSHandle<JSTaggedValue>> collatorMap;
88    collatorMap["dateStyle"] = globalConst->GetHandledDateStyleString();
89    collatorMap["timeStyle"] = globalConst->GetHandledTimeStyleString();
90    collatorMap["calendar"] = globalConst->GetHandledCalendarString();
91    collatorMap["numberingSystem"] = globalConst->GetHandledNumberingSystemString();
92    collatorMap["localeMatcher"] = globalConst->GetHandledLocaleMatcherString();
93    collatorMap["timeZone"] = globalConst->GetHandledTimeZoneString();
94    collatorMap["hour12"] = globalConst->GetHandledHour12String();
95    collatorMap["hourCycle"] = globalConst->GetHandledHourCycleString();
96    collatorMap["formatMatcher"] = globalConst->GetHandledFormatMatcherString();
97    collatorMap["weekday"] = globalConst->GetHandledWeekdayString();
98    collatorMap["era"] = globalConst->GetHandledEraString();
99    collatorMap["year"] = globalConst->GetHandledYearString();
100    collatorMap["month"] = globalConst->GetHandledMonthString();
101    collatorMap["day"] = globalConst->GetHandledDayString();
102    collatorMap["hour"] = globalConst->GetHandledHourString();
103    collatorMap["minute"] = globalConst->GetHandledMinuteString();
104    collatorMap["second"] = globalConst->GetHandledSecondString();
105    collatorMap["fractionalSecondDigits"] = globalConst->GetHandledFractionalSecondDigitsString();
106    collatorMap["timeZoneName"] = globalConst->GetHandledTimeZoneNameString();
107    optionMaps.insert(make_pair(GlobalFormatterType::DateFormatter, collatorMap));
108}
109
110void GlobalIntlHelper::InitNumberData(const GlobalEnvConstants *globalConst)
111{
112    std::map<std::string, JSHandle<JSTaggedValue>> collatorMap;
113    collatorMap["localeMatcher"] = globalConst->GetHandledLocaleMatcherString();
114    collatorMap["numberingSystem"] = globalConst->GetHandledNumberingSystemString();
115    collatorMap["notation"] = globalConst->GetHandledNotationString();
116    collatorMap["compactDisplay"] = globalConst->GetHandledCompactDisplayString();
117    collatorMap["useGrouping"] = globalConst->GetHandledUserGroupingString();
118    collatorMap["signDisplay"] = globalConst->GetHandledSignDisplayString();
119    collatorMap["style"] = globalConst->GetHandledStyleString();
120    collatorMap["currency"] = globalConst->GetHandledCurrencyString();
121    collatorMap["currencySign"] = globalConst->GetHandledCurrencySignString();
122    collatorMap["currencyDisplay"] = globalConst->GetHandledCurrencyDisplayString();
123    collatorMap["unit"] = globalConst->GetHandledUnitString();
124    collatorMap["unitDisplay"] = globalConst->GetHandledUnitDisplayString();
125    collatorMap["minimumIntegerDigits"] = globalConst->GetHandledMinimumIntegerDigitsString();
126    collatorMap["minimumFractionDigits"] = globalConst->GetHandledMinimumFractionDigitsString();
127    collatorMap["maximumFractionDigits"] = globalConst->GetHandledMaximumFractionDigitsString();
128    collatorMap["minimumSignificantDigits"] = globalConst->GetHandledMinimumSignificantDigitsString();
129    collatorMap["maximumSignificantDigits"] = globalConst->GetHandledMaximumSignificantDigitsString();
130    optionMaps.insert(make_pair(GlobalFormatterType::NumberFormatter, collatorMap));
131}
132
133std::map<std::string, std::string> GlobalIntlHelper::OptionsToMap(JSThread *thread,
134    const JSHandle<JSTaggedValue> &options, GlobalFormatterType types)
135{
136    std::map<std::string, std::string> inputOptions;
137    JSHandle<JSObject> optionsObject;
138    if (options->IsUndefined()) {
139        return inputOptions;
140    } else {
141        optionsObject = JSTaggedValue::ToObject(thread, options);
142    }
143    if (optionMaps.size() == 0) {
144        LOG_ECMA(ERROR) << "GlobalIntlHelper::OptionsToMap size is zero";
145        return inputOptions;
146    }
147
148    auto matterType = types;
149    if (types == GlobalFormatterType::SimpleDateFormatDate ||
150        types == GlobalFormatterType::SimpleDateFormatTime) {
151        matterType = GlobalFormatterType::DateFormatter;
152    }
153
154    auto iter = optionMaps.find(matterType);
155    if (iter != optionMaps.end()) {
156        for (auto &opt : optionMaps[matterType]) {
157            OperationResult operationResult = JSObject::GetProperty(thread, optionsObject, opt.second);
158            if (!operationResult.GetValue()->IsUndefined()) {
159                std::string valueStr = std::string(EcmaConvertToStr(JSTaggedValue::ToString(thread,
160                    operationResult.GetValue())));
161                auto inOpt = inputOptions.find(opt.first);
162                if (inOpt != inputOptions.end()) {
163                    inputOptions[opt.first] = valueStr;
164                } else {
165                    inputOptions.insert(make_pair(opt.first, valueStr));
166                }
167            }
168        }
169    }
170    return OptionsWithDataFormatter(inputOptions, types);
171}
172
173std::map<std::string, std::string> GlobalIntlHelper::OptionsWithDataFormatter(std::map<std::string,
174    std::string> &options, GlobalFormatterType &types)
175{
176    std::vector<std::string> all;
177    if (types == GlobalFormatterType::DateFormatter) {
178        all = {"year", "month", "day", "hour", "minute", "second"};
179    }
180    if (types == GlobalFormatterType::SimpleDateFormatDate) {
181        all = {"year", "month", "day"};
182    }
183    if (types == GlobalFormatterType::SimpleDateFormatTime) {
184        all = {"hour", "minute", "second"};
185    }
186    for (auto &item : all) {
187        auto iter = options.find(item);
188        if (iter == options.end()) {
189            options[item] = "numeric";
190        }
191    }
192    return options;
193}
194
195int64_t GlobalIntlHelper::DoubleToInt64(double value)
196{
197    return static_cast<int64_t>(round(value));
198}
199
200std::string GlobalIntlHelper::EcmaConvertToStr(const JSHandle<EcmaString> &string)
201{
202    return std::string(ConvertToString(*string, StringConvertedUsage::LOGICOPERATION));
203}
204
205std::vector<std::string> GlobalIntlHelper::LocalesToVector(JSThread *thread,
206    const JSHandle<JSTaggedValue> &locales)
207{
208    JSHandle<TaggedArray> tArray = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
209    return TaggedArrayToVector(thread, tArray);
210}
211
212std::vector<std::string> GlobalIntlHelper::TaggedArrayToVector(JSThread *thread,
213    JSHandle<TaggedArray> &taggedarray)
214{
215    std::vector<std::string> availableStringLocales;
216    JSMutableHandle<EcmaString> availableItem(thread, JSTaggedValue::Undefined());
217    uint32_t availablecalesLength = taggedarray->GetLength();
218    for (uint32_t i = 0; i < availablecalesLength; i++) {
219        availableItem.Update(taggedarray->Get(thread, i));
220        availableStringLocales.emplace_back(intl::LocaleHelper::ConvertToStdString(availableItem));
221    }
222    return availableStringLocales;
223}
224}  // panda::ecmascript::base
225