import { _get, _post, _delete, toClipboard, toggleLoader, toDateString } from "../modules/common.js";
import { DiscordUser, newDiscordSearch } from "../modules/discord.js";

class DOMInvite implements Invite {
    updateNotify = (checkbox: HTMLInputElement) => {
        let state: { [code: string]: { [type: string]: boolean } } = {};
        let revertChanges: () => void;
        if (checkbox.classList.contains("inv-notify-expiry")) {
            revertChanges = () => { this.notifyExpiry = !this.notifyExpiry };
            state[this.code] = { "notify-expiry": this.notifyExpiry };
        } else {
            revertChanges = () => { this.notifyCreation = !this.notifyCreation };
            state[this.code] = { "notify-creation": this.notifyCreation };
        }
        _post("/invites/notify", state, (req: XMLHttpRequest) => {
            if (req.readyState == 4 && !(req.status == 200 || req.status == 204)) {
                revertChanges();
            }
        });
    }

    delete = () => _delete("/invites", { "code": this.code }, (req: XMLHttpRequest) => {
        if (req.readyState == 4 && (req.status == 200 || req.status == 204)) {
            this.remove();
            const inviteDeletedEvent = new CustomEvent("inviteDeletedEvent", { "detail": this.code });
            document.dispatchEvent(inviteDeletedEvent);
        }
    })

    private _label: string = "";
    get label(): string { return this._label; }
    set label(label: string) {
        this._label = label;
        const linkEl = this._codeArea.querySelector("a") as HTMLAnchorElement;
        if (label == "") {
            linkEl.textContent = this.code.replace(/-/g, '-');
        } else {
            linkEl.textContent = label;
        }
    }

    private _code: string = "None";
    get code(): string { return this._code; }
    set code(code: string) {
        this._code = code;
        let codeLink = window.location.href;
        for (let split of ["#", "?"]) {
            codeLink = codeLink.split(split)[0];
        }
        if (codeLink.slice(-1) != "/") { codeLink += "/"; }
        this._codeLink = codeLink + "invite/" + code;
        const linkEl = this._codeArea.querySelector("a") as HTMLAnchorElement;
        if (this.label == "") {
            linkEl.textContent = code.replace(/-/g, '-');
        }
        linkEl.href = this._codeLink;
    }
    private _codeLink: string;

    private _expiresIn: string;
    get expiresIn(): string { return this._expiresIn }
    set expiresIn(expiry: string) {
        this._expiresIn = expiry;
        this._infoArea.querySelector("span.inv-duration").textContent = expiry;
    }

    private _userExpiry: string;
    get userExpiryTime(): string { return this._userExpiry; }
    set userExpiryTime(d: string) {
        const expiry = this._middle.querySelector("span.user-expiry") as HTMLSpanElement;
        if (!d) {
            expiry.textContent = "";
        } else {
            expiry.textContent = window.lang.strings("userExpiry");
        }
        this._userExpiry = d;
        this._middle.querySelector("strong.user-expiry-time").textContent = d;
    }

    private _remainingUses: string = "1";
    get remainingUses(): string { return this._remainingUses; }
    set remainingUses(remaining: string) {
        this._remainingUses = remaining;
        this._middle.querySelector("strong.inv-remaining").textContent = remaining;
    }

    private _send_to: string = "";
    get send_to(): string { return this._send_to };
    set send_to(address: string) {
        this._send_to = address;
        const container = this._infoArea.querySelector(".tooltip") as HTMLDivElement;
        const icon = container.querySelector("i");
        const chip = container.querySelector("span.inv-email-chip");
        const tooltip = container.querySelector("span.content") as HTMLSpanElement;
        if (address == "") {
            container.classList.remove("mr-4");
            icon.classList.remove("ri-mail-line");
            icon.classList.remove("ri-mail-close-line");
            chip.classList.remove("~neutral");
            chip.classList.remove("~critical");
            chip.classList.remove("chip");
        } else {
            container.classList.add("mr-4");
            chip.classList.add("chip");
            if (address.includes("Failed")) {
                icon.classList.remove("ri-mail-line");
                icon.classList.add("ri-mail-close-line");
                chip.classList.remove("~neutral");
                chip.classList.add("~critical");
            } else {
                address = "Sent to " + address;
                icon.classList.remove("ri-mail-close-line");
                icon.classList.add("ri-mail-line");
                chip.classList.remove("~critical");
                chip.classList.add("~neutral");
            }
        }
        tooltip.textContent = address;
    }

    private _usedBy: { [name: string]: number };
    get usedBy(): { [name: string]: number } { return this._usedBy; }
    set usedBy(uB: { [name: string]: number }) {
        this._usedBy = uB;
        if (Object.keys(uB).length == 0) {
            this._right.classList.add("empty");
            this._userTable.innerHTML = `<p class="content">${window.lang.strings("inviteNoUsersCreated")}</p>`;
            return;
        }
        this._right.classList.remove("empty");
        let innerHTML = `
        <table class="table inv-table">
            <thead>
                <tr>
                    <th>${window.lang.strings("name")}</th>
                    <th>${window.lang.strings("date")}</th>
                </tr>
            </thead>
            <tbody>
        `;
        for (let username in uB) {
            innerHTML += `
                <tr>
                    <td>${username}</td>
                    <td>${toDateString(new Date(uB[username] * 1000))}</td>
                </tr>
            `;
        }
        innerHTML += `
            </tbody>
        </table>
        `;
        this._userTable.innerHTML = innerHTML;
    }

    private _createdUnix: number;
    get created(): number { return this._createdUnix; }
    set created(unix: number) {
        this._createdUnix = unix;
        const el = this._middle.querySelector("strong.inv-created");
        if (unix == 0) {
            el.textContent = window.lang.strings("unknown");
        } else {
            el.textContent = toDateString(new Date(unix*1000));
        }
    }
    
    private _notifyExpiry: boolean = false;
    get notifyExpiry(): boolean { return this._notifyExpiry }
    set notifyExpiry(state: boolean) {
        this._notifyExpiry = state;
        (this._left.querySelector("input.inv-notify-expiry") as HTMLInputElement).checked = state;
    }

    private _notifyCreation: boolean = false;
    get notifyCreation(): boolean { return this._notifyCreation }
    set notifyCreation(state: boolean) {
        this._notifyCreation = state;
        (this._left.querySelector("input.inv-notify-creation") as HTMLInputElement).checked = state;
    }

    private _profile: string;
    get profile(): string { return this._profile; }
    set profile(profile: string) { this.loadProfiles(profile); }
    loadProfiles = (selected?: string) => {
        const select = this._left.querySelector("select") as HTMLSelectElement;
        let noProfile = false;
        if (selected === "") {
            noProfile = true; 
        } else {
            selected = selected || select.value;
        }
        let innerHTML = `<option value="noProfile" ${noProfile ? "selected" : ""}>${window.lang.strings("inviteNoProfile")}</option>`;
        for (let profile of window.availableProfiles) {
            innerHTML += `<option value="${profile}" ${((profile == selected) && !noProfile) ? "selected" : ""}>${profile}</option>`;
        }
        select.innerHTML = innerHTML;
        this._profile = selected;
    };
    updateProfile = () => {
        const select = this._left.querySelector("select") as HTMLSelectElement;
        const previous = this.profile;
        let profile = select.value;
        if (profile == "noProfile") { profile = ""; }
        _post("/invites/profile", { "invite": this.code, "profile": profile }, (req: XMLHttpRequest) => {
            if (req.readyState == 4) {
                if (!(req.status == 200 || req.status == 204)) {
                    select.value = previous || "noProfile";
                } else {
                    this._profile = profile;
                }
            }
        });
    }

    private _container: HTMLDivElement;

    private _header: HTMLDivElement;
    private _codeArea: HTMLDivElement;
    private _infoArea: HTMLDivElement;

    private _details: HTMLDivElement;
    private _left: HTMLDivElement;
    private _middle: HTMLDivElement;
    private _right: HTMLDivElement;
    private _userTable: HTMLDivElement;

    // whether the details card is expanded.
    get expanded(): boolean {
        return this._details.classList.contains("focused");
    }
    set expanded(state: boolean) {
        const toggle = (this._infoArea.querySelector("input.inv-toggle-details") as HTMLInputElement);
        if (state) {
            this._details.classList.remove("unfocused");
            this._details.classList.add("focused");
            toggle.previousElementSibling.classList.add("rotated");
            toggle.previousElementSibling.classList.remove("not-rotated");
        } else {
            this._details.classList.add("unfocused");
            this._details.classList.remove("focused");
            toggle.previousElementSibling.classList.remove("rotated");
            toggle.previousElementSibling.classList.add("not-rotated");
        }
    }

    constructor(invite: Invite) {
        // first create the invite structure, then use our setter methods to fill in the data.
        this._container = document.createElement('div') as HTMLDivElement;
        this._container.classList.add("inv");

        this._header = document.createElement('div') as HTMLDivElement;
        this._container.appendChild(this._header);
        this._header.classList.add("card", "dark:~d_neutral", "@low", "inv-header", "elem-pad", "no-pad", "flex-expand", "row", "mt-2", "overflow-y");

        this._codeArea = document.createElement('div') as HTMLDivElement;
        this._header.appendChild(this._codeArea);
        this._codeArea.classList.add("inv-codearea");
        this._codeArea.innerHTML = `
        <a class="invite-link text-black dark:text-white font-mono bg-inherit mr-4" href=""></a>
        <span class="button ~info @low" title="${window.lang.strings("copy")}"><i class="ri-file-copy-line"></i></span>
        `;
        const copyButton = this._codeArea.querySelector("span.button") as HTMLSpanElement;
        copyButton.onclick = () => { 
            toClipboard(this._codeLink);
            const icon = copyButton.children[0];
            icon.classList.remove("ri-file-copy-line");
            icon.classList.add("ri-check-line");
            copyButton.classList.remove("~info");
            copyButton.classList.add("~positive");
            setTimeout(() => {
                icon.classList.remove("ri-check-line");
                icon.classList.add("ri-file-copy-line");
                copyButton.classList.remove("~positive");
                copyButton.classList.add("~info");
            }, 800);
        };

        this._infoArea = document.createElement('div') as HTMLDivElement;
        this._header.appendChild(this._infoArea);
        this._infoArea.classList.add("inv-infoarea");
        this._infoArea.innerHTML = `
        <div class="tooltip left">
            <span class="inv-email-chip"><i></i></span>
            <span class="content sm"></span>
        </div>
        <span class="inv-duration mr-4"></span>
        <span class="button ~critical @low inv-delete">${window.lang.strings("delete")}</span>
        <label>
            <i class="icon clickable ri-arrow-down-s-line not-rotated"></i>
            <input class="inv-toggle-details unfocused" type="checkbox">
        </label>
        `;
        
        (this._infoArea.querySelector(".inv-delete") as HTMLSpanElement).onclick = this.delete;

        const toggle = (this._infoArea.querySelector("input.inv-toggle-details") as HTMLInputElement);
        toggle.onchange = () => { this.expanded = !this.expanded; };
        this._header.onclick = (event: Event) => { 
            if (event.target == this._header) {
                this.expanded = !this.expanded; 
            }
        };

        this._details = document.createElement('div') as HTMLDivElement;
        this._container.appendChild(this._details);
        this._details.classList.add("card", "~neutral", "@low", "mt-2", "no-pad", "inv-details");
        const detailsInner = document.createElement('div') as HTMLDivElement;
        this._details.appendChild(detailsInner);
        detailsInner.classList.add("inv-row", "flex-expand", "row", "elem-pad", "align-top");

        this._left = document.createElement('div') as HTMLDivElement;
        detailsInner.appendChild(this._left);
        this._left.classList.add("inv-profilearea");
        let innerHTML = `
        <p class="supra mb-2 top">${window.lang.strings("profile")}</p>
        <div class="select ~neutral @low inv-profileselect inline-block mb-2">
            <select>
                <option value="noProfile" selected>${window.lang.strings("inviteNoProfile")}</option>
            </select>
        </div>
        `;
        if (window.notificationsEnabled) {
            innerHTML += `
            <p class="label supra mb-2">${window.lang.strings("notifyEvent")}</p>
            <label class="switch block">
                <input class="inv-notify-expiry" type="checkbox">
                <span>${window.lang.strings("notifyInviteExpiry")}</span>
            </label>
            <label class="switch block">
                <input class="inv-notify-creation" type="checkbox">
                <span>${window.lang.strings("notifyUserCreation")}</span>
            </label>
            `;
        }
        this._left.innerHTML = innerHTML;
        (this._left.querySelector("select") as HTMLSelectElement).onchange = this.updateProfile;
        
        if (window.notificationsEnabled) {
            const notifyExpiry = this._left.querySelector("input.inv-notify-expiry") as HTMLInputElement;
            notifyExpiry.onchange = () => { this._notifyExpiry = notifyExpiry.checked; this.updateNotify(notifyExpiry); };

            const notifyCreation = this._left.querySelector("input.inv-notify-creation") as HTMLInputElement;
            notifyCreation.onchange = () => { this._notifyCreation = notifyCreation.checked; this.updateNotify(notifyCreation); };
        }

        this._middle = document.createElement('div') as HTMLDivElement;
        detailsInner.appendChild(this._middle);
        this._middle.classList.add("block");
        this._middle.innerHTML = `
        <p class="supra mb-4 top">${window.lang.strings("inviteDateCreated")} <strong class="inv-created"></strong></p>
        <p class="supra mb-4">${window.lang.strings("inviteRemainingUses")} <strong class="inv-remaining"></strong></p>
        <p class="supra mb-4"><span class="user-expiry"></span> <strong class="user-expiry-time"></strong></p>
        `;

        this._right = document.createElement('div') as HTMLDivElement;
        detailsInner.appendChild(this._right);
        this._right.classList.add("card", "~neutral", "@low", "inv-created-users");
        this._right.innerHTML = `<strong class="supra table-header">${window.lang.strings("inviteUsersCreated")}</strong>`;
        this._userTable = document.createElement('div') as HTMLDivElement;
        this._right.appendChild(this._userTable);


        this.expanded = false;
        this.update(invite);

        document.addEventListener("profileLoadEvent", () => { this.loadProfiles(); }, false);
        document.addEventListener("timefmt-change", () => {
            this.created = this.created;
            this.usedBy = this.usedBy;
        });
    }

    update = (invite: Invite) => {
        this.code = invite.code;
        this.created = invite.created;
        this.send_to = invite.send_to;
        this.expiresIn = invite.expiresIn;
        if (window.notificationsEnabled) {
            this.notifyCreation = invite.notifyCreation;
            this.notifyExpiry = invite.notifyExpiry;
        }
        this.profile = invite.profile;
        this.remainingUses = invite.remainingUses;
        this.usedBy = invite.usedBy;
        if (invite.label) {
            this.label = invite.label;
        }
        this.userExpiryTime = invite.userExpiryTime || "";
    }

    asElement = (): HTMLDivElement => { return this._container; }

    remove = () => { this._container.remove(); }
}

export class inviteList implements inviteList {
    private _list: HTMLDivElement;
    private _empty: boolean;
    // since invite reload sends profiles, this event it broadcast so the createInvite object can load them.
    private _profileLoadEvent = new CustomEvent("profileLoadEvent");

    invites: { [code: string]: DOMInvite };

    constructor() {
        this._list = document.getElementById('invites') as HTMLDivElement;
        this.empty = true;
        this.invites = {};
        document.addEventListener("newInviteEvent", () => { this.reload(); }, false);
        document.addEventListener("inviteDeletedEvent", (event: CustomEvent) => {
            const code = event.detail;
            const length = Object.keys(this.invites).length - 1; // store prior as Object.keys is undefined when there are no keys
            delete this.invites[code];
            if (length == 0) {
                this.empty = true;
            }
        }, false);
    }

    get empty(): boolean { return this._empty; }
    set empty(state: boolean) {
        this._empty = state;
        if (state) {
            this.invites = {};
            this._list.classList.add("empty");
            this._list.innerHTML = `
            <div class="inv inv-empty">
                <div class="card dark:~d_neutral @low inv-header flex-expand mt-2">
                    <div class="inv-codearea">
                        <span class="text-black dark:text-white font-mono bg-inherit">${window.lang.strings("inviteNoInvites")}</span>
                    </div>
                </div>
            </div>
            `;
        } else {
            this._list.classList.remove("empty");
            if (this._list.querySelector(".inv-empty")) {
                this._list.textContent = '';
            }
        }
    }

    add = (invite: Invite) => {
        let domInv = new DOMInvite(invite);
        this.invites[invite.code] = domInv;
        if (this.empty) { this.empty = false; }
        this._list.appendChild(domInv.asElement());
    }

    reload = () => _get("/invites", null, (req: XMLHttpRequest) => {
        if (req.readyState == 4) {
            let data = req.response;
            if (req.status == 200) {
                window.availableProfiles = data["profiles"];
                document.dispatchEvent(this._profileLoadEvent);
            }
            if (data["invites"] === undefined || data["invites"] == null || data["invites"].length == 0) {
                this.empty = true;
                return;
            }
            // get a list of all current inv codes on dom
            // every time we find a match in resp, delete from list
            // at end delete all remaining in list from dom
            let invitesOnDOM: { [code: string]: boolean } = {};
            for (let code in this.invites) { invitesOnDOM[code] = true; }
            for (let inv of (data["invites"] as Array<any>)) {
                const invite = parseInvite(inv);
                if (invite.code in this.invites) {
                    this.invites[invite.code].update(invite);
                    delete invitesOnDOM[invite.code];
                } else {
                    this.add(invite);
                }
            }
            for (let code in invitesOnDOM) {
                this.invites[code].remove();
                delete this.invites[code];
            }
        }
    })
}
    

function parseInvite(invite: { [f: string]: string | number | { [name: string]: number } | boolean }): Invite {
    let parsed: Invite = {};
    parsed.code = invite["code"] as string;
    parsed.send_to = invite["send_to"] as string || "";
    parsed.label = invite["label"] as string || "";
    let time = "";
    let userExpiryTime = "";
    const fields = ["months", "days", "hours", "minutes"];
    let prefixes = [""];
    if (invite["user-expiry"] as boolean) { prefixes.push("user-"); }
    for (let i = 0; i < fields.length; i++) {
        for (let j = 0; j < prefixes.length; j++) {
            if (invite[prefixes[j]+fields[i]]) {
                let abbreviation = fields[i][0];
                if (fields[i] == "months") {
                    abbreviation += fields[i][1];
                }
                let text = `${invite[prefixes[j]+fields[i]]}${abbreviation} `;
                if (prefixes[j] ==  "user-") {
                    userExpiryTime += text;
                } else {
                    time += text;
                }
            }
        }
    }
    parsed.expiresIn = window.lang.var("strings", "inviteExpiresInTime", time.slice(0, -1));
    parsed.userExpiry = invite["user-expiry"] as boolean;
    parsed.userExpiryTime = userExpiryTime.slice(0, -1);
    parsed.remainingUses = invite["no-limit"] ? "∞" : String(invite["remaining-uses"])
    parsed.usedBy = invite["used-by"] as { [name: string]: number } || {} ;
    parsed.created = invite["created"] as number || 0;
    parsed.profile = invite["profile"] as string || "";
    parsed.notifyExpiry = invite["notify-expiry"] as boolean || false;
    parsed.notifyCreation = invite["notify-creation"] as boolean || false;
    return parsed;
}

export class createInvite {
    private _sendToEnabled = document.getElementById("create-send-to-enabled") as HTMLInputElement;
    private _sendTo = document.getElementById("create-send-to") as HTMLInputElement;
    private _discordSearch: HTMLSpanElement;
    private _userExpiryToggle = document.getElementById("create-user-expiry-enabled") as HTMLInputElement;
    private _uses = document.getElementById('create-uses') as HTMLInputElement;
    private _infUses = document.getElementById("create-inf-uses") as HTMLInputElement;
    private _infUsesWarning = document.getElementById('create-inf-uses-warning') as HTMLParagraphElement;
    private _createButton = document.getElementById("create-submit") as HTMLSpanElement;
    private _profile = document.getElementById("create-profile") as HTMLSelectElement;
    private _label = document.getElementById("create-label") as HTMLInputElement;

    private _months = document.getElementById("create-months") as HTMLSelectElement;
    private _days = document.getElementById("create-days") as HTMLSelectElement;
    private _hours = document.getElementById("create-hours") as HTMLSelectElement;
    private _minutes = document.getElementById("create-minutes") as HTMLSelectElement;
    private _userMonths = document.getElementById("user-months") as HTMLSelectElement;
    private _userDays = document.getElementById("user-days") as HTMLSelectElement;
    private _userHours = document.getElementById("user-hours") as HTMLSelectElement;
    private _userMinutes = document.getElementById("user-minutes") as HTMLSelectElement;

    private _invDurationButton = document.getElementById('radio-inv-duration') as HTMLInputElement;
    private _userExpiryButton = document.getElementById('radio-user-expiry') as HTMLInputElement;
    private _invDuration = document.getElementById('inv-duration');
    private _userExpiry = document.getElementById('user-expiry');

    private _sendToDiscord: (passData: string) => void;

    // Broadcast when new invite created
    private _newInviteEvent = new CustomEvent("newInviteEvent");
    private _firstLoad = true;

    private _count: Number = 30;
    private _populateNumbers = () => {
        const fieldIDs = ["months", "days", "hours", "minutes"];
        const prefixes = ["create-", "user-"];
        for (let i = 0; i < fieldIDs.length; i++) {
            for (let j = 0; j < prefixes.length; j++) { 
                const field = document.getElementById(prefixes[j] + fieldIDs[i]);
                field.textContent = '';
                for (let n = 0; n <= this._count; n++) {
                   const opt = document.createElement("option") as HTMLOptionElement;
                   opt.textContent = ""+n;
                   opt.value = ""+n;
                   field.appendChild(opt);
                }
            }
        }
    }

    get label(): string { return this._label.value; }
    set label(label: string) { this._label.value = label; }

    get sendToEnabled(): boolean {
        return this._sendToEnabled.checked;
    }
    set sendToEnabled(state: boolean) {
        this._sendToEnabled.checked = state;
        this._sendTo.disabled = !state;
        if (state) {
            this._sendToEnabled.parentElement.classList.remove("~neutral");
            this._sendToEnabled.parentElement.classList.add("~urge");
            if (window.discordEnabled) {
                this._discordSearch.classList.remove("~neutral");
                this._discordSearch.classList.add("~urge");
                this._discordSearch.onclick = () => this._sendToDiscord("");
            }
        } else {
            this._sendToEnabled.parentElement.classList.remove("~urge");
            this._sendToEnabled.parentElement.classList.add("~neutral");
            if (window.discordEnabled) {
                this._discordSearch.classList.remove("~urge");
                this._discordSearch.classList.add("~neutral");
                this._discordSearch.onclick = null;
            }
        }
    }

    get infiniteUses(): boolean {
        return this._infUses.checked;
    }
    set infiniteUses(state: boolean) {
        this._infUses.checked = state;
        this._uses.disabled = state;
        if (state) {
            this._infUses.parentElement.classList.remove("~neutral");
            this._infUses.parentElement.classList.add("~urge");
            this._infUsesWarning.classList.remove("unfocused");
        } else {
            this._infUses.parentElement.classList.remove("~urge");
            this._infUses.parentElement.classList.add("~neutral");
            this._infUsesWarning.classList.add("unfocused");
        }
    }
    
    get uses(): number { return this._uses.valueAsNumber; }
    set uses(n: number) { this._uses.valueAsNumber = n; }

    private _checkDurationValidity = () => {
        if (this.months + this.days + this.hours + this.minutes == 0) {
            this._createButton.setAttribute("disabled", "");
            this._createButton.onclick = null;
        } else {
            this._createButton.removeAttribute("disabled");
            this._createButton.onclick = this.create;
        }
    }

    get months(): number {
        return +this._months.value;
    }
    set months(n: number) {
        this._months.value = ""+n;
        this._checkDurationValidity();
    }
    get days(): number {
        return +this._days.value;
    }
    set days(n: number) {
        this._days.value = ""+n;
        this._checkDurationValidity();
    }
    get hours(): number {
        return +this._hours.value;
    }
    set hours(n: number) {
        this._hours.value = ""+n;
        this._checkDurationValidity();
    }
    get minutes(): number {
        return +this._minutes.value;
    }
    set minutes(n: number) {
        this._minutes.value = ""+n;
        this._checkDurationValidity();
    }
    get userExpiry(): boolean {
        return this._userExpiryToggle.checked;
    }
    set userExpiry(enabled: boolean) {
        this._userExpiryToggle.checked = enabled;
        const parent = this._userExpiryToggle.parentElement;
        if (enabled) {
            parent.classList.add("~urge");
            parent.classList.remove("~neutral");
        } else {
            parent.classList.add("~neutral");
            parent.classList.remove("~urge");
        }
        this._userMonths.disabled = !enabled;
        this._userDays.disabled = !enabled;
        this._userHours.disabled = !enabled;
        this._userMinutes.disabled = !enabled;
    }
    get userMonths(): number {
        return +this._userMonths.value;
    }
    set userMonths(n: number) {
        this._userMonths.value = ""+n;
    }
    get userDays(): number {
        return +this._userDays.value;
    }
    set userDays(n: number) {
        this._userDays.value = ""+n;
    }
    get userHours(): number {
        return +this._userHours.value;
    }
    set userHours(n: number) {
        this._userHours.value = ""+n;
    }
    get userMinutes(): number {
        return +this._userMinutes.value;
    }
    set userMinutes(n: number) {
        this._userMinutes.value = ""+n;
    }

    get sendTo(): string { return this._sendTo.value; }
    set sendTo(address: string) { this._sendTo.value = address; }

    get profile(): string { 
        const val = this._profile.value;
        if (val == "noProfile") {
            return "";
        }
        return val;
    }
    set profile(p: string) {
        if (p == "") { p = "noProfile"; }
        this._profile.value = p;
    }

    loadProfiles = () => {
        let innerHTML = `<option value="noProfile">${window.lang.strings("inviteNoProfile")}</option>`;
        for (let profile of window.availableProfiles) {
            innerHTML += `<option value="${profile}">${profile}</option>`;
        }
        let selected = this.profile;
        this._profile.innerHTML = innerHTML;
        if (this._firstLoad) {
            this.profile = window.availableProfiles[0] || "";
            this._firstLoad = false;
        } else {
            this.profile = selected;
        }
    }

    create = () => {
        toggleLoader(this._createButton);
        let userExpiry = this.userExpiry;
        if (this.userMonths == 0 && this.userDays == 0 && this.userHours == 0 && this.userMinutes == 0) {
            userExpiry = false;
        }
        let send = {
            "months": this.months,
            "days": this.days,
            "hours": this.hours,
            "minutes": this.minutes,
            "user-expiry": userExpiry,
            "user-months": this.userMonths,
            "user-days": this.userDays,
            "user-hours": this.userHours,
            "user-minutes": this.userMinutes,
            "multiple-uses": (this.uses > 1 || this.infiniteUses),
            "no-limit": this.infiniteUses,
            "remaining-uses": this.uses,
            "send-to": this.sendToEnabled ? this.sendTo : "",
            "profile": this.profile,
            "label": this.label
        };
        _post("/invites", send, (req: XMLHttpRequest) => {
            if (req.readyState == 4) {
                if (req.status == 200 || req.status == 204) {
                    document.dispatchEvent(this._newInviteEvent);
                }
                toggleLoader(this._createButton);
            }
        });
    }

    constructor() {
        this._populateNumbers();
        this.months = 0;
        this.days = 0;
        this.hours = 0;
        this.minutes = 30;
        this._infUses.onchange = () => { this.infiniteUses = this.infiniteUses; };
        this.infiniteUses = false;
        this._sendToEnabled.onchange = () => { this.sendToEnabled = this.sendToEnabled; };
        this.userExpiry = false;
        this._userExpiryToggle.onchange = () => { this.userExpiry = this._userExpiryToggle.checked; }
        this._userMonths.disabled = true;
        this._userDays.disabled = true;
        this._userHours.disabled = true;
        this._userMinutes.disabled = true;
        this._createButton.onclick = this.create;
        this.sendTo = "";
        this.uses = 1;
        this.label = "";

        const checkDuration = () => {
            const invSpan = this._invDurationButton.nextElementSibling as HTMLSpanElement;
            const userSpan = this._userExpiryButton.nextElementSibling as HTMLSpanElement;
            if (this._invDurationButton.checked) {
                this._invDuration.classList.remove("unfocused");
                this._userExpiry.classList.add("unfocused");
                invSpan.classList.add("@high");
                invSpan.classList.remove("@low");
                userSpan.classList.add("@low");
                userSpan.classList.remove("@high");
            } else if (this._userExpiryButton.checked) {
                this._userExpiry.classList.remove("unfocused");
                this._invDuration.classList.add("unfocused");
                invSpan.classList.add("@low");
                invSpan.classList.remove("@high");
                userSpan.classList.add("@high");
                userSpan.classList.remove("@low");
            }
        };

        this._userExpiryButton.checked = false;
        this._invDurationButton.checked = true;
        this._userExpiryButton.onchange = checkDuration;
        this._invDurationButton.onchange = checkDuration;

        this._days.onchange = this._checkDurationValidity;
        this._months.onchange = this._checkDurationValidity;
        this._hours.onchange = this._checkDurationValidity;
        this._minutes.onchange = this._checkDurationValidity;
        document.addEventListener("profileLoadEvent", () => { this.loadProfiles(); }, false);

        if (!window.emailEnabled && !window.discordEnabled) {
            document.getElementById("create-send-to-container").classList.add("unfocused");
        }

        if (window.discordEnabled) {
            this._discordSearch = document.getElementById("create-send-to-search") as HTMLSpanElement;
            this._sendToDiscord = newDiscordSearch(
                window.lang.strings("findDiscordUser"),
                window.lang.strings("searchDiscordUser"),
                window.lang.strings("select"),
                (user: DiscordUser) => {
                    this.sendTo = user.name;
                    window.modals.discord.close();
                }
            );
        }
        this.sendToEnabled = false;
    }
}