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
16// eslint-disable-next-line @typescript-eslint/no-unused-vars
17class PrefetchCount {
18  private readonly MAX_SCREENS: number = 4;
19  private readonly speedCoef: number = 2.5;
20  private maxItems = 0;
21  private prefetchCountValueInternal = 0;
22  private currentMaxItemsInternal = 0;
23  private currentMinItemsInternal = 0;
24
25  constructor(
26    private readonly itemsOnScreen: IItemsOnScreenProvider,
27    private readonly prefetchRangeRatio: PrefetchRangeRatio,
28    private readonly logger: ILogger = dummyLogger,
29  ) {
30    this.itemsOnScreen = itemsOnScreen;
31    this.itemsOnScreen.register(() => {
32      this.updateLimits();
33    });
34    this.prefetchRangeRatio.register(() => {
35      this.updateLimits();
36    });
37  }
38
39  get prefetchCountValue(): number {
40    return this.prefetchCountValueInternal;
41  }
42
43  set prefetchCountValue(v: number) {
44    this.prefetchCountValueInternal = v;
45    this.logger.debug(`{"tm":${Date.now()},"prefetch_count":${v}}`);
46  }
47
48  get currentMaxItems(): number {
49    return this.currentMaxItemsInternal;
50  }
51
52  get currentMinItems(): number {
53    return this.currentMinItemsInternal;
54  }
55
56  getPrefetchCountByRatio(ratio: number): number {
57    this.itemsOnScreen.updateSpeed(this.itemsOnScreen.visibleRange.start, this.itemsOnScreen.visibleRange.end - 1);
58    const minItems = Math.min(
59      this.currentMaxItems,
60      Math.ceil(this.speedCoef * this.itemsOnScreen.speed * this.currentMaxItems),
61    );
62    const prefetchCount = minItems + Math.ceil(ratio * (this.currentMaxItems - minItems));
63    this.logger.debug(
64      `speed: ${this.itemsOnScreen.speed}, minItems: ${minItems}, ratio: ${ratio}, prefetchCount: ${prefetchCount}`,
65    );
66    return prefetchCount;
67  }
68
69  getRangeToFetch(totalCount: number): IndexRange {
70    const visibleRange = this.itemsOnScreen.visibleRange;
71    let start = 0;
72    let end = 0;
73    switch (this.itemsOnScreen.direction) {
74      case 'UNKNOWN':
75        start = Math.max(0, visibleRange.start - Math.round(this.prefetchCountValue));
76        end = Math.min(totalCount, visibleRange.end + Math.round(this.prefetchCountValue));
77        break;
78      case 'UP':
79        start = Math.max(0, visibleRange.start - Math.round(this.prefetchCountValue));
80        end = Math.min(totalCount, visibleRange.end + Math.round(this.prefetchCountValue * 0.5));
81        break;
82      case 'DOWN':
83        start = Math.max(0, visibleRange.start - Math.round(this.prefetchCountValue * 0.5));
84        end = Math.min(totalCount, visibleRange.end + Math.round(this.prefetchCountValue));
85        break;
86    }
87    if (start > end) {
88      start = end;
89    }
90    return new IndexRange(start, end);
91  }
92
93  private updateLimits(): void {
94    this.maxItems = Math.max(this.currentMinItems, Math.ceil(this.MAX_SCREENS * this.itemsOnScreen.meanValue));
95    this.updateCurrentLimit();
96  }
97
98  private updateCurrentLimit(): void {
99    this.currentMaxItemsInternal = Math.max(
100      this.currentMinItems,
101      Math.ceil(this.maxItems * this.prefetchRangeRatio.maxRatio),
102    );
103    this.currentMinItemsInternal = Math.ceil(this.maxItems * this.prefetchRangeRatio.minRatio);
104  }
105}
106