import { SerializableItem } from "./serializableitem";

interface SavePoint<DataType> {
    description: string;
    data: DataType;
}

export class History<HistoryDataType> {
    public maxCheckpoints: number;
    public currentCheckpoint: number;

    private data: SerializableItem<unknown, HistoryDataType>;
    private checkpoints: SavePoint<HistoryDataType>[];

    constructor(
        data: SerializableItem<unknown, HistoryDataType>,
        maxCheckpoints = 20,
        initialMessage = "New History"
    ) {
        this.data = data;
        this.maxCheckpoints = maxCheckpoints;
        this.checkpoints = [];
        this.currentCheckpoint = -1;
        this.checkpoint(initialMessage);
    }

    checkpoint(description: string) {
        const checkpointData = this.data.toHistorySerializableObject();
        const checkpoint = {
            description: description,
            data: JSON.parse(JSON.stringify(checkpointData)), // deepcopy
        };

        // Remove potential history which is not relevant anymore (maybe caused by undo ops)
        this.currentCheckpoint++;
        this.checkpoints.length = this.currentCheckpoint;

        this.checkpoints.push(checkpoint);
    }

    public historyDescription(): Array<string> {
        return this.checkpoints.map((savepoint) => savepoint.description);
    }

    public undo(): SavePoint<HistoryDataType> {
        if (this.hasUndoCheckpoints()) {
            return this.checkpoints[this.currentCheckpoint--];
        } else {
            return this.checkpoints[0];
        }
    }

    public redo(): SavePoint<HistoryDataType> {
        if (this.hasRedoCheckpoints()) {
            return this.checkpoints[this.currentCheckpoint++];
        } else {
            return this.checkpoints[this.checkpoints.length - 1];
        }
    }

    public hasUndoCheckpoints(): boolean {
        return this.currentCheckpoint > 0;
    }

    public hasRedoCheckpoints(): boolean {
        return this.currentCheckpoint < this.checkpoints.length;
    }
}