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
16interface DataCollectionChangeListener {
17  batchUpdate(operations: BatchOperation[]): void;
18}
19
20interface ItemsDeleted {
21  kind: 'deleted';
22  startIndex: number;
23  count: number;
24}
25
26interface ItemsAdded {
27  kind: 'added';
28  startIndex: number;
29  count: number;
30}
31
32interface ItemUpdated {
33  kind: 'updated';
34  index: number;
35}
36
37interface CollectionReloaded {
38  kind: 'reloaded';
39  totalCount: number;
40}
41
42interface ItemsSwapped {
43  kind: 'swapped';
44  a: number;
45  b: number;
46}
47
48interface ItemMoved {
49  kind: 'moved';
50  from: number;
51  to: number;
52}
53
54type BatchOperation = ItemsDeleted | ItemsAdded | ItemUpdated | CollectionReloaded | ItemsSwapped | ItemMoved;
55
56// eslint-disable-next-line @typescript-eslint/no-unused-vars
57class DataSourceObserver implements DataChangeListener {
58  private dataSource: IDataSource;
59
60  constructor(private readonly simpleChangeListener: DataCollectionChangeListener) {}
61
62  onDataReloaded(): void {
63    this.simpleChangeListener.batchUpdate([
64      {
65        kind: 'reloaded',
66        totalCount: this.dataSource.totalCount(),
67      },
68    ]);
69  }
70
71  onDataAdded(index: number): void {
72    this.simpleChangeListener.batchUpdate([
73      {
74        kind: 'added',
75        startIndex: index,
76        count: 1,
77      },
78    ]);
79  }
80
81  onDataAdd(index: number): void {
82    this.onDataAdded(index);
83  }
84
85  onDataMoved(from: number, to: number): void {
86    this.simpleChangeListener.batchUpdate([
87      {
88        kind: 'swapped',
89        a: from,
90        b: to,
91      },
92    ]);
93  }
94
95  onDataMove(from: number, to: number): void {
96    this.onDataMoved(from, to);
97  }
98
99  onDataDeleted(index: number): void {
100    this.simpleChangeListener.batchUpdate([
101      {
102        kind: 'deleted',
103        startIndex: index,
104        count: 1,
105      },
106    ]);
107  }
108
109  onDataDelete(index: number): void {
110    this.onDataDeleted(index);
111  }
112
113  onDataChanged(index: number): void {
114    this.simpleChangeListener.batchUpdate([
115      {
116        kind: 'updated',
117        index,
118      },
119    ]);
120  }
121
122  onDataChange(index: number): void {
123    this.onDataChanged(index);
124  }
125
126  onDatasetChange(dataOperations: DataOperation[]): void {
127    const operations: BatchOperation[] = [];
128    dataOperations.forEach((operation) => {
129      switch (operation.type) {
130        case 'add':
131        case 'delete':
132          if (operation.count === undefined || operation.count > 0) {
133            operations.push({
134              kind: operation.type === 'add' ? 'added' : 'deleted',
135              startIndex: operation.index,
136              count: operation.count ?? 1,
137            });
138          }
139          break;
140        case 'change':
141          operations.push({
142            kind: 'updated',
143            index: operation.index,
144          });
145          break;
146        case 'reload':
147          operations.push({
148            kind: 'reloaded',
149            totalCount: this.dataSource.totalCount(),
150          });
151          break;
152        case 'exchange':
153          operations.push({
154            kind: 'swapped',
155            a: operation.index.start,
156            b: operation.index.end,
157          });
158          break;
159        case 'move':
160          operations.push({
161            kind: 'moved',
162            from: operation.index.from,
163            to: operation.index.to,
164          });
165          break;
166        default:
167          assertNever(operation);
168      }
169    });
170    this.simpleChangeListener.batchUpdate(operations);
171  }
172
173  setDataSource(dataSource: IDataSource): void {
174    if (this.dataSource) {
175      this.dataSource.unregisterDataChangeListener(this);
176    }
177    this.dataSource = dataSource;
178    this.dataSource.registerDataChangeListener(this);
179    this.onDataReloaded();
180  }
181}
182