export type HistoryHandler = {
    undo: (action: HistoryAction) => void;
    redo: (action: HistoryAction) => void;
}

interface HistoryEvent
{
    source: string;
    event: string;
    actions: HistoryAction[];
}
interface HistoryAction
{
    element: any;
    from?: any;
    to?: any;
}

export class History
{
    private handlers: Record<string, { handlers: Record<string, HistoryHandler>, any?: (action: HistoryAction) => any }>;
    private history: HistoryEvent[];
    private position: number;

    constructor()
    {
        this.handlers = {};
        this.history = [];
        this.position = -1;
    }
    get last()
    {
        return this.history.length > 0 && this.position > -1 ? this.history[this.position] : undefined;
    }
    get undoable()
    {
        return this.history && this.position !== -1;
    }
    get redoable()
    {
        return this.history && this.position < this.history.length - 1;
    }
    undo()
    {
        const last = this.last;
        if(!last)
            return;

        last.actions.forEach(e => {
            this.handlers[last.source] && this.handlers[last.source].handlers[last.event]?.undo(e)
            this.handlers[last.source] && this.handlers[last.source].any && this.handlers[last.source].any!(e);
        });
        
        this.position--;
    }
    redo()
    {
        if(!this.history || this.history.length - 1 <= this.position)
            return;

        this.position++;

        const last = this.last;
        if(!last)
        {
            this.position--;
            return;
        }

        last.actions.forEach(e => {
            this.handlers[last.source] && this.handlers[last.source].handlers[last.event]?.redo(e)
            this.handlers[last.source] && this.handlers[last.source].any && this.handlers[last.source].any!(e);
        });
    }
    add(source: string, event: string, actions: HistoryAction[], apply: boolean = false)
    {
        this.position++;
        this.history.splice(this.position, history.length - this.position, { source, event, actions });

        if(apply)
            actions.forEach(e => {
                this.handlers[source] && this.handlers[source].handlers[event]?.redo(e);
                this.handlers[source] && this.handlers[source].any && this.handlers[source].any(e);
            });
    }
    register(source: string, handlers: Record<string, HistoryHandler>, any?: (action: HistoryAction) => any)
    {
        this.handlers[source] = { handlers, any };
    }
    unregister(source: string)
    {
        delete this.handlers[source];
    }
}