import Emitter from '../../Utilities/Emitter';
import { Subject } from 'rxjs';
import Helper from '../Helpers/Helper';
// ~ 8 hours
const MAX_TIMEFRAME_SIZE = 86400000;
export class DataService {
    constructor(adaptable) {
        this.adaptable = adaptable;
        this.on = (eventName, callback) => this.emitter.on(eventName, callback);
        this.emit = (eventName, data) => this.emitter.emit(eventName, data);
        this.adaptable = adaptable;
        this.emitter = new Emitter();
        this.cellDataChangeLogSubject$ = new Subject();
        this.cellDataChangeLog$ = this.cellDataChangeLogSubject$.asObservable();
        this.rowDataChangeLogSubject$ = new Subject();
        this.rowDataChangeLog$ = this.rowDataChangeLogSubject$.asObservable();
        this.undoChangeLog = new Map();
        this.undoChangeTimers = new Map();
    }
    destroy() {
        this.emitter.destroy();
        this.emitter = null;
        this.cellDataChangeLogSubject$.complete();
        this.cellDataChangeLogSubject$ = null;
        this.cellDataChangeLog$ = null;
        this.rowDataChangeLogSubject$.complete();
        this.rowDataChangeLogSubject$ = null;
        this.rowDataChangeLog$ = null;
        this.undoChangeLog.clear();
        this.undoChangeLog = null;
        this.undoChangeTimers.clear();
        this.undoChangeTimers = null;
    }
    CreateCellDataChangedEvent(cellDataChangedInfo) {
        if (cellDataChangedInfo.newValue != cellDataChangedInfo.oldValue) {
            this.emitter.emitSync('CellDataChanged', cellDataChangedInfo);
            this.adaptable.api.eventApi.internalApi.fireCellChangedEvent(cellDataChangedInfo);
            const dataChangeLogEntry = this.extractDataChangeLogEntry(cellDataChangedInfo);
            this.cellDataChangeLogSubject$.next(dataChangeLogEntry);
        }
    }
    CreateRowDataChangedEvent(rowDataChangedInfo) {
        this.emitter.emitSync('RowDataChanged', rowDataChangedInfo);
        this.adaptable.api.eventApi.internalApi.fireRowChangedEvent(rowDataChangedInfo);
        this.rowDataChangeLogSubject$.next(rowDataChangedInfo);
    }
    // we need this temporary "shared memory" because the value change and the AG Grid cellChanged event are asynchronous
    // in the first phase we keep the undone change
    logUndoChange(change) {
        const { primaryKeyValue, column, oldValue, newValue } = change;
        const undoChangeKey = this.getUndoChangeKey(primaryKeyValue, column.columnId, oldValue, newValue);
        this.undoChangeLog.set(undoChangeKey, change);
        // normally the extractUndoChange() should be executed right after this method
        // just to be sure that no 'zombie' undo change remains here (if something goes wrong with the undo value change)
        // we manually extract the change after a 2 seconds
        // why 2 and not 3 or 1? no good reason, only seems like a reasonable waiting time :)
        const UNDO_WAIT = 2000;
        const timeoutId = setTimeout(() => {
            this.adaptable.logger.warn(`Undo change was NOT handled, this should NOT happen: PK(${change.primaryKeyValue}) Col(${change.column}) RevertedValue(${change.newValue}) NewValue(${change.oldValue})`);
            this.extractUndoChange(change);
        }, UNDO_WAIT);
        this.undoChangeTimers.set(undoChangeKey, timeoutId);
    }
    // in the second phase, when AG-Grid triggers the cellChange event, we check if it corresponds to an undone change
    extractUndoChange(change) {
        const { primaryKeyValue, column, oldValue, newValue } = change;
        // this is post-undo, so we have to swap the new & old values
        const undoChangeKey = this.getUndoChangeKey(primaryKeyValue, column.columnId, newValue, oldValue);
        const result = this.undoChangeLog.get(undoChangeKey);
        this.undoChangeLog.delete(undoChangeKey);
        const timeoutId = this.undoChangeTimers.get(undoChangeKey);
        if (timeoutId) {
            clearTimeout(timeoutId);
        }
        return result;
    }
    getUndoChangeKey(primaryKeyValue, columnId, previousValue, newValue) {
        return JSON.stringify({
            primaryKeyValue,
            columnId,
            previousValue,
            newValue,
        });
    }
    extractDataChangeLogEntry(cellDataChangedInfo) {
        // create rowData snapshot
        const rowData = cellDataChangedInfo.rowData
            ? Helper.cloneObject(cellDataChangedInfo.rowData)
            : null;
        // strip down rowNode properties as it is pretty "heavy" on the memory
        const rowNode = rowData ? { data: rowData } : null;
        return {
            ...cellDataChangedInfo,
            rowNode,
            rowData,
        };
    }
}
