/*
 *   Copyright (c) Robin E.R. Davies
 *   All rights reserved.

 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 
 *   The above copyright notice and this permission notice shall be included in all
 *   copies or substantial portions of the Software.
 
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *   SOFTWARE.
 */


export interface IDialogStackable {
    getTag: () => string;
    isOpen: () => boolean;
    onDialogStackClose(): void;
}

interface DialogStackEntry {
    dialog: IDialogStackable | null;
    historyState: DialogStackHistoryState
};

let dialogStack: DialogStackEntry[] = [];


const DLG_STATE_MAGIC = "DlgState438327"
export interface DialogStackHistoryState {
    magic: string;
    topmost: boolean;
    stackDepth: number;
    tag: string;
    id: string;
    previousState: DialogStackHistoryState | null;
};

export function pushDialogStack(dialog: IDialogStackable): void {
    let stackId = Math.random().toString(36).substring(7);
    let dialogStackState: DialogStackHistoryState = {
        magic: DLG_STATE_MAGIC,
        topmost: false,
        stackDepth: dialogStack.length + 1,
        tag: dialog.getTag(),
        id: stackId, previousState:
            window.history.state as DialogStackHistoryState | null
    };
    window.history.replaceState(
        dialogStackState,
        "",
        "#" + dialog.getTag());

    // refresh reloads the page.
    let topState: DialogStackHistoryState = {
        topmost: true,
        magic: DLG_STATE_MAGIC,
        previousState: dialogStackState,
        tag: dialog.getTag(),
        id: stackId,
        stackDepth: dialogStack.length + 1
    };

    dialogStack.push(
        {
            dialog: dialog,
            historyState: dialogStackState
        });

    
    window.history.pushState(
        topState,
        "",
        "#" + dialog.getTag());

    if (!gWatchingHistory) {
        gWatchingHistory = true;
        window.addEventListener("popstate", handlePopState);
    }
}

let gWatchingHistory = false;
let gPopFromBack = true;

let handlePopState = (ev: PopStateEvent) => {
    let evTag: DialogStackHistoryState | null = ev.state as DialogStackHistoryState | null;
    if (!evTag) return;
    if (evTag.magic !== DLG_STATE_MAGIC) return;

    ev.stopPropagation();

    let id = evTag.id;
    let ix = dialogStack.length-1;
    while (ix >= 0) {
        let entry = dialogStack[ix];
        if (entry.historyState.id === id) {
            break;
        }
        ix--;
    }
    if (ix < 0) {
        // user must have navigated directly.
        // so just reload the page.
        dialogStack = [];
        window.location.reload();
        ev.stopPropagation();
        return;
    }

    while (dialogStack.length > ix) {
        let topEntry = dialogStack[dialogStack.length - 1];
        if (topEntry.dialog) {
            try {

                if (topEntry.dialog.isOpen()) {
                    topEntry.dialog.onDialogStackClose();
                }
            } catch (_e) {

            }
            topEntry.dialog = null;
        }
        dialogStack.pop();
    }
    let previousState  = evTag.previousState;
    if (previousState) 
    {
        let newState = {...previousState, topmost: true};
        window.history.replaceState(newState,"","#"+previousState.tag);
    } else {
        window.history.replaceState(null,"",window.location.pathname);
    }

    if (dialogStack.length != 0 && gPopFromBack) {
        if (dialogStack[dialogStack.length -1].dialog === null)
        {
            if (window.history.state != null)
            {
                window.setTimeout(() => {   window.history.back(); }, 0);
            } else {
                dialogStack.pop();
            }
        }
    }
    ev.stopPropagation();
}


export function popDialogStack(dialog: IDialogStackable): void {
    let ix = dialogStack.length-1;

    while (ix >= 0) {
        let entry = dialogStack[ix];
        if (entry.dialog === dialog) {
            break;
        }
        ix--;
    }
    if (ix < 0) 
    {
        return;
    }
    dialogStack[ix].dialog = null;
    if (dialogStack.length != 0 && dialogStack[dialogStack.length - 1].dialog === null)
    {
        window.history.back();
    }
}









// Close all dialogs higher in the dialog stack than "tag".
export function removeDialogStackEntriesAbove(tag: string) {
    while (dialogStack.length > 0) {
        let entry = dialogStack[dialogStack.length - 1];
        if (entry.dialog && entry.dialog.getTag() === tag) {
            return;
        }

        dialogStack.pop();

        if (entry.dialog && entry.dialog.isOpen()) {
            entry.dialog.onDialogStackClose();
        }
    }
}

