import { SerializableItem } from "./serializableitem";

export interface Checkpoint<DataType> {
    id: number;
    description: string;
    data: DataType;
}

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

    private current: number;
    private data: SerializableItem<unknown, HistoryDataType>;
    public checkpoints: Checkpoint<HistoryDataType>[];
    private next: number;

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

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

        // Remove potential history which is not relevant anymore (maybe caused by undo ops)
        if (this.current < this.checkpoints.length - 1 && this.current > 0) {
            this.checkpoints.length = this.current + 1;
        }

        this.checkpoints.push(checkpoint);
        this.current++;
    }

    public get currentCheckpoint() {
        return this.checkpoints[this.current];
    }

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

    public resetToCheckpoint(id: number): Checkpoint<HistoryDataType> {
        const index = this.checkpoints.findIndex(
            (checkpoint) => checkpoint.id == id
        );

        if (index > 0) {
            this.current = index;
            return this.checkpoints[index];
        }
    }

    public undo(): Checkpoint<HistoryDataType> {
        if (this.hasUndoCheckpoints()) {
            this.current--;
        }
        return this.checkpoints[this.current];
    }

    public redo(): Checkpoint<HistoryDataType> {
        if (this.hasRedoCheckpoints()) {
            this.current++;
        }
        return this.checkpoints[this.current];
    }

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

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