1/*
2 * Copyright (c) 2024 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/// <reference path='./import.ts' />
16
17const overrideMap = new Map<string, Map<string,
18  new (value: string | number | boolean | object) => ModifierWithKey<string | number | boolean | object>>>();
19
20
21overrideMap.set('ArkCheckboxComponent', new Map([
22  ['Symbol(width)', CheckboxWidthModifier],
23  ['Symbol(height)', CheckboxHeightModifier]
24]));
25
26overrideMap.set('ArkTextComponent', new Map([
27  ['Symbol(foregroundColor)', TextForegroundColorModifier]
28]));
29
30function applyAndMergeModifier<T, M extends ArkComponent, C extends ArkComponent>(instance: T, modifier: M): void {
31  let myMap = modifier._modifiersWithKeys as ModifierMap;
32  myMap.setOnChange((key: Symbol, value: AttributeModifierWithKey) => {
33    modifier._changed = !modifier._changed;
34  });
35
36  // @ts-ignore
37  let component: C = instance as C;
38  mergeMaps(component._modifiersWithKeys, modifier._modifiersWithKeys);
39}
40
41function copyModifierWithKey(obj: ModifierWithKey<string | number | boolean | object>): ModifierWithKey<string | number | boolean | object> {
42  let newObj: ModifierWithKey<string | number | boolean | object> = {
43    ...obj,
44    applyStage: function (node: number): boolean {
45      throw new Error('Function not implemented.');
46    },
47    applyPeer: function (node: number, reset: boolean): void {
48      throw new Error('Function not implemented.');
49    },
50    checkObjectDiff: function (): boolean {
51      throw new Error('Function not implemented.');
52    }
53  };
54  newObj.applyStage = obj?.applyStage;
55  newObj.applyPeer = obj?.applyPeer;
56  newObj.checkObjectDiff = obj?.checkObjectDiff;
57  return newObj;
58}
59
60function mergeMaps(stageMap: Map<Symbol, AttributeModifierWithKey>,
61  newMap: Map<Symbol, AttributeModifierWithKey>): Map<Symbol, AttributeModifierWithKey> {
62  newMap.forEach((value, key) => {
63    stageMap.set(key, copyModifierWithKey(value));
64  });
65
66  return stageMap;
67}
68
69class ModifierUtils {
70  static dirtyComponentSet: Set<ArkComponent | ArkSpanComponent> = new Set();
71  static dirtyFlag = false;
72  static timeoutId = -1;
73
74  static copyModifierWithKey(obj: ModifierWithKey<string | number | boolean | object>): ModifierWithKey<string | number | boolean | object> {
75    let newObj: ModifierWithKey<string | number | boolean | object> = {
76      ...obj,
77      applyStage: function (node: number): boolean {
78        throw new Error('Function not implemented.');
79      },
80      applyPeer: function (node: number, reset: boolean): void {
81        throw new Error('Function not implemented.');
82      },
83      checkObjectDiff: function (): boolean {
84        throw new Error('Function not implemented.');
85      }
86    };
87    newObj.applyStage = obj?.applyStage;
88    newObj.applyPeer = obj?.applyPeer;
89    newObj.checkObjectDiff = obj?.checkObjectDiff;
90    return newObj;
91  }
92
93  static mergeMaps(stageMap: Map<Symbol, AttributeModifierWithKey>,
94    newMap: Map<Symbol, AttributeModifierWithKey>): void {
95    newMap.forEach((value, key) => {
96      stageMap.set(key, this.copyModifierWithKey(value));
97    });
98  }
99
100  static mergeMapsEmplace<T0 extends string | number | boolean | object, M0 extends ModifierWithKey<T0>>(
101    stageMap: Map<Symbol, AttributeModifierWithKey>,
102    newMap: Map<Symbol, AttributeModifierWithKey>,
103    componentOverrideMap: Map<string, new (value: T0) => M0>): void {
104      newMap.forEach((value, key) => {
105        if (!key) {
106          ArkLogConsole.info('key of modifier map is undefined, ModifierWithKey is ' +
107            (value ? value.constructor.name.toString() : 'undefined'));
108        } else {
109          if (componentOverrideMap.has(key.toString())) {
110            //@ts-ignore
111            const newValue = new (componentOverrideMap.get(key.toString()))(value.stageValue);
112            stageMap.set(key, newValue);
113          } else {
114            stageMap.set(key, this.copyModifierWithKey(value));
115          }
116        }
117      });
118  }
119
120  static applyAndMergeModifier<T, M extends ArkComponent | ArkSpanComponent, C extends ArkComponent | ArkSpanComponent>(instance: T, modifier: M): void {
121    // @ts-ignore
122    let component: C = instance as C;
123    if (component.constructor.name && overrideMap.has(component.constructor.name)) {
124      const componentOverrideMap = overrideMap.get(component.constructor.name);
125      this.mergeMapsEmplace(component._modifiersWithKeys, modifier._modifiersWithKeys,
126        componentOverrideMap);
127    } else {
128      this.mergeMaps(component._modifiersWithKeys, modifier._modifiersWithKeys);
129    }
130  }
131
132  static applySetOnChange<T, M extends ArkComponent | ArkSpanComponent, C extends ArkComponent | ArkSpanComponent>(modifier: M): void {
133    // It is to make the stateMgmt can addRef of _changed,
134    // so that the modifier change can be observed by makeObserved when modifier._changed changed.
135    modifier._changed;
136    let myMap = modifier._modifiersWithKeys as ModifierMap;
137    if (modifier._classType === ModifierType.STATE) {
138      myMap.setOnChange((key: Symbol, value: AttributeModifierWithKey) => {
139        this.putDirtyModifier(modifier, value);
140      });
141    } else {
142      myMap.setOnChange((key: Symbol, value: AttributeModifierWithKey) => {
143        modifier._changed = !modifier._changed;
144      });
145    }
146  }
147
148  static putDirtyModifier<M extends ArkComponent | ArkSpanComponent>(
149    arkModifier: M, attributeModifierWithKey: ModifierWithKey<string | number | boolean | object>): void {
150    attributeModifierWithKey.value = attributeModifierWithKey.stageValue;
151    if (!arkModifier._weakPtr.invalid()) {
152      attributeModifierWithKey.applyPeer(arkModifier.nativePtr,
153        (attributeModifierWithKey.value === undefined || attributeModifierWithKey.value === null));
154    } else {
155      ArkLogConsole.info('pointer is invalid when putDirtyModifier in ' + (arkModifier ?
156        arkModifier.constructor.name.toString() : 'undefined') + ' of ' + (attributeModifierWithKey ?
157        attributeModifierWithKey.constructor.name.toString() : 'undefined'));
158    }
159    this.dirtyComponentSet.add(arkModifier);
160    if (!this.dirtyFlag) {
161      this.dirtyFlag = true;
162      this.requestFrame();
163    }
164  }
165
166  static requestFrame(): void {
167    const frameCallback = () => {
168      if (this.timeoutId !== -1) {
169        clearTimeout(this.timeoutId);
170      }
171      this.dirtyComponentSet.forEach(item => {
172        const nativePtrValid = !item._weakPtr.invalid();
173        if (item._nativePtrChanged && nativePtrValid) {
174          item._modifiersWithKeys.forEach((value, key) => {
175            value.applyPeer(item.nativePtr,
176              (value.value === undefined || value.value === null));
177          });
178          item._nativePtrChanged = false;
179        }
180        if (nativePtrValid) {
181          getUINativeModule().frameNode.markDirty(item.nativePtr, 0b100);
182        }
183      });
184      this.dirtyComponentSet.clear();
185      this.dirtyFlag = false;
186      this.timeoutId = -1;
187    };
188    if (this.timeoutId !== -1) {
189      clearTimeout(this.timeoutId);
190    }
191    this.timeoutId = setTimeout(frameCallback, 100);
192    getUINativeModule().frameNode.registerFrameCallback(frameCallback);
193  }
194}
195
196class ModifierMap {
197  private map_: Map<Symbol, AttributeModifierWithKey>;
198  private changeCallback: ((key: Symbol, value: AttributeModifierWithKey) => void) | undefined;
199
200  constructor() {
201    this.map_ = new Map();
202  }
203
204  public clear(): void {
205    this.map_.clear();
206  }
207
208  public delete(key: Symbol): boolean {
209    return this.map_.delete(key);
210  }
211
212  public forEach(callbackfn: (value: AttributeModifierWithKey, key: Symbol,
213    map: Map<Symbol, AttributeModifierWithKey>) => void, thisArg?: any): void {
214    this.map_.forEach(callbackfn, thisArg);
215  }
216  public get(key: Symbol): AttributeModifierWithKey | undefined {
217    return this.map_.get(key);
218  }
219  public has(key: Symbol): boolean {
220    return this.map_.has(key);
221  }
222  public set(key: Symbol, value: AttributeModifierWithKey): this {
223    const _a = this.changeCallback;
224    this.map_.set(key, value);
225    _a?.call(this, value);
226    return this;
227  }
228  public get size(): number {
229    return this.map_.size;
230  }
231  public entries(): IterableIterator<[Symbol, AttributeModifierWithKey]> {
232    return this.map_.entries();
233  }
234  public keys(): IterableIterator<Symbol> {
235    return this.map_.keys();
236  }
237  public values(): IterableIterator<AttributeModifierWithKey> {
238    return this.map_.values();
239  }
240  public [Symbol.iterator](): IterableIterator<[Symbol, AttributeModifierWithKey]> {
241    return this.map_.entries();
242  }
243  public get [Symbol.toStringTag](): string {
244    return 'ModifierMapTag';
245  }
246  public setOnChange(callback: (key: Symbol, value: AttributeModifierWithKey) => void): void {
247    this.changeCallback = callback;
248  }
249}
250
251class AttributeUpdater {
252  private _state: number;
253  private _attribute: ArkComponent;
254  private _isAttributeUpdater: boolean;
255
256  static StateEnum = {
257    INIT: 0,
258    UPDATE: 1
259  };
260
261  constructor() {
262    this._state = AttributeUpdater.StateEnum.INIT;
263    this._attribute = null;
264    this._isAttributeUpdater = true;
265  }
266
267  public get isAttributeUpdater(): boolean {
268    return this._isAttributeUpdater;
269  }
270
271  public get attribute(): ArkComponent {
272    return this._attribute;
273  }
274
275  public set attribute(value: ArkComponent) {
276    if (!this._attribute && value) {
277      this._attribute = value;
278    }
279  }
280
281  public get modifierState(): number {
282    return this._state;
283  }
284
285  public set modifierState(value: number) {
286    this._state = value;
287  }
288
289  initializeModifier(instance: ArkComponent): void {}
290
291  updateConstructorParams(...args: Object[]): void {
292    if (!this.attribute) {
293      ArkLogConsole.info('AttributeUpdater has not been initialized before updateConstructorParams.');
294      return;
295    }
296    this.attribute.initialize(args);
297  }
298}