<% content_for :javascript do %>
<script>
const SUPERTABLE_WORKER_FILE = "<%= javascript_path 'supertable3_worker' %>";
let GLOBAL_SUPERTABLE_INSTANCE = null;
</script>

<%= javascript_include_tag 'javascripts/modal_popup' %>
<%= javascript_include_tag 'javascripts/supertable3' %>
<% end %>

<div id="schoolsTitle"><h1><%= LdapOrganisation.current.o %></h1></div>

<%= render 'shared/supertable_controls' %>

<div id="tableSchools"></div>

<% if current_user.organisation_owner? %>
<% content_for :page_tools do %>
<%=
toolbar do |tb|
  tb.link t('link.new_school'),
          href: new_school_path,
          owners: true,
          icon: 'plus'
end %>
<% end %>
<% end %>

<% content_for :post_load_javascript do %>
<script>
"use strict;"

<%= render partial: 'shared/supertable_translations', formats: [:js] %>

// Possible integrations, in order they're listed and with tooltips
const INTEGRATION_TYPES = [
    ["in", "<%= t('schools.integrations.in') %>" ],
    ["out", "<%= t('schools.integrations.out') %>"],
    ["inout", "<%= t('schools.integrations.inout') %>"],
    ["login", "<%= t('schools.integrations.login') %>"],
    ["password", "<%= t('schools.integrations.password') %>"],
    ["email", "<%= t('schools.integrations.email') %>"],
    ["other", "<%= t('schools.integrations.other') %>"]
];

// All available columns, their names and types and other parameters
const COLUMN_DEFINITIONS = {
    id: {
        key: "id",
        title: "<%= t('columns.puavoid') %>",
        type: ST.ColumnType.NUMERIC,
        alias: ["puavoid"],
    },

    name: {
        key: "name",
        title: "<%= t('activeldap.attributes.school.displayName') %>",
        type: ST.ColumnType.STRING,
        transform: entry => [`<a href="${entry.link}">${ST.escapeHTML(entry.name)}</a>`, entry.name],
    },

    integrations: {
        key: "integrations",
        title: "<%= t('columns.schools.integrations') %>",
        type: ST.ColumnType.STRING,
        flags: ST.ColumnFlag.ARRAY,
        transform: entry => {
            let display = [],
                sortable = [];

            const available = Object.keys(entry.integrations);

            for (const integration of INTEGRATION_TYPES) {
                const type = integration[0],
                      title = integration[1];

                if (available.includes(type)) {
                    switch (type) {
                        case "in":
                            for (const system of entry.integrations[type])
                                display.push(`<li class="${type}" title="${title}">${system}<span class="dirIn"></span>Puavo</li>`);

                            break;

                        case "out":
                            for (const system of entry.integrations[type])
                                display.push(`<li class="${type}" title="${title}">Puavo<span class="dirOut"></span>${system}</li>`);

                            break;

                        case "inout":
                            for (const system of entry.integrations[type])
                                display.push(`<li class="${type}" title="${title}">Puavo<span class="dirInOut"></span>${system}</li>`);

                            break;

                        default:
                            for (const system of entry.integrations[type])
                                display.push(`<li class="${type}" title="${title}">${system}</li>`);

                            break;
                    }

                    sortable.push(entry.integrations[type]);
                }
            }

            sortable = sortable.flat();

            return [
                `<ul class="integrations inlineIntegrations">${display.join("<br>")}</ul>`,
                sortable,
                sortable
            ];
        }
    },

    prefix: {
        key: "prefix",
        title: "<%= t('activeldap.attributes.school.puavoSchoolNameAbbreviation') %>",
        type: ST.ColumnType.STRING,
    },

    boot_servers: {
        key: "boot_servers",
        title: "<%= t('activeldap.attributes.school.bootservers') %>",
        type: ST.ColumnType.STRING,
        transform: entry => {
            const plain = entry.boot_servers.map(s => s[0]).join(", "); // used for filtering and sorting

            return [
<% if current_user.organisation_owner? %>
                entry.boot_servers.map(s => `<a href="${s[1]}">${s[0]}</a>`).join("<br>"),
<% else %>
                entry.boot_servers.map(s => s[0]).join("<br>"),
<% end %>
                plain,
                plain
            ];
        }
    },

    tags: {
        key: "tags",
        title: "<%= t('columns.tags') %>",
        type: ST.ColumnType.STRING,
        flags: ST.ColumnFlag.ARRAY,
    },

    school_code: {
        key: "school_code",
        title: "<%= t('activeldap.attributes.school.puavoSchoolCode') %>",
        type: ST.ColumnType.STRING,
    },

    school_oid: {
        key: "school_oid",
        title: "<%= t('columns.schools.oid') %>",
        type: ST.ColumnType.STRING,
    },

    eid: {
        key: "eid",
        title: "<%= t('activeldap.attributes.school.puavoExternalId') %>",
        type: ST.ColumnType.STRING,
        flags: ST.ColumnFlag.F_NULLABLE,
        alias: ["external_id"]
    },

    num_members: {
        key: "num_members",
        title: "<%= t('schools.index.members') %>",
        type: ST.ColumnType.NUMERIC,
    },

    num_groups: {
        key: "num_groups",
        title: "<%= t('schools.index.groups') %>",
        type: ST.ColumnType.NUMERIC,
    },

    num_devices: {
        key: "num_devices",
        title: "<%= t('schools.index.devices') %>",
        type: ST.ColumnType.NUMERIC,
    },

    conf: {
        key: "conf",
        title: "<%= t('columns.puavoconf') %>",
        type: ST.ColumnType.STRING,
        flags: ST.ColumnFlag.ARRAY,
        alias: ["puavoconf", "puavo_conf"],
        export_name: "puavoconf",
    },

    desktop_image: {
        key: "desktop_image",
        title: "<%= t('activeldap.attributes.school.puavoDeviceImage') %>",
        type: ST.ColumnType.STRING,
        transform: e => {
            if (e.desktop_image === null)
                return [null, null, null];

            let display = "",
                value = e.desktop_image.file,
                filter = "";

            if (e.desktop_image.release !== null) {
                // Display the release name nicely
                display = `${value} <span class="releaseName">(${e.desktop_image.release})</span>`;
                filter = `${value} (${e.desktop_image.release})`;
            } else {
                display = value;
                filter = value;
            }

            return [display, value, filter];
        }
    },

    image_series: {
        key: "image_series",
        title: "<%= t('activeldap.attributes.school.puavoImageSeriesSourceURL') %>",
        type: ST.ColumnType.STRING,
        flags: ST.ColumnFlag.ARRAY,
    },

    allow_guest: {
        key: "allow_guest",
        title: "<%= t('activeldap.attributes.school.puavoAllowGuest') %>",
        type: ST.ColumnType.BOOL,
    },

    personal_device: {
        key: "personal_device",
        title: "<%= t('activeldap.attributes.school.puavoPersonalDevice') %>",
        type: ST.ColumnType.BOOL,
    },

    auto_updates: {
        key: "auto_updates",
        title: "<%= t('activeldap.attributes.school.puavoAutomaticImageUpdates') %>",
        type: ST.ColumnType.BOOL,
    },

    autopower_mode: {
        key: "autopower_mode",
        title: "<%= t('activeldap.attributes.school.puavoDeviceAutoPowerOffMode') %>",
        type: ST.ColumnType.STRING,
    },

    autopower_on: {
        key: "autopower_on",
        title: "<%= t('activeldap.attributes.school.puavoDeviceOnHour') %>",
        type: ST.ColumnType.NUMERIC,
    },

    autopower_off: {
        key: "autopower_off",
        title: "<%= t('activeldap.attributes.school.puavoDeviceOffHour') %>",
        type: ST.ColumnType.NUMERIC,
    },

    description: {
        key: "description",
        title: "<%= t('columns.description') %>",
        type: ST.ColumnType.STRING,
    },

    notes: {
        key: "notes",
        title: "<%= t('columns.notes') %>",
        type: ST.ColumnType.STRING,
        transform: e => {
            if (e.notes === null)
                return [null, null, null];

            return [ST.escapeHTML(e.notes).replaceAll("\r", "").replaceAll("\n", "<br>"), e.notes, e.notes];
        }
    },

    locale: {
        key: "locale",
        title: "<%= t('columns.schools.locale') %>",
        type: ST.ColumnType.STRING,
        flags: ST.ColumnFlag.F_NULLABLE,
    },

    timezone: {
        key: "timezone",
        title: "<%= t('columns.schools.timezone') %>",
        type: ST.ColumnType.STRING,
        flags: ST.ColumnFlag.F_NULLABLE,
    },
};

// The default order for columns
const COLUMN_ORDER = [
    "id",
    "name",
    "prefix",
    "school_code",
    "school_oid",
    "eid",
    "integrations",
    "tags",
    "num_members",
    "num_groups",
    "num_devices",
    "boot_servers",
    "conf",
    "locale",
    "timezone",
    "desktop_image",
    "image_series",
    "allow_guest",
    "personal_device",
    "auto_updates",
    "autopower_mode",
    "autopower_on",
    "autopower_off",
    "description",
    "notes",
];

function schoolActions(entry)
{
    return `<a href="/users/schools/${entry.id[ST.INDEX_FILTERABLE]}/edit" class="btn"><%= t('link.edit') %></a>`;
}

<% if @is_owner %>

// Mass operation: set database field value
class MassSchoolSetFieldValue extends ST.MassOperation {
    constructor(parent, container)
    {
        super(parent, container);

        // NOTE: These entries are not listed in this order in the combo. They're sorted alphabetically by title.
        this.items = [
            { title: "<%= t('activeldap.attributes.school.puavoDeviceImage') %>", key: "image", type: "s" },
            { title: "<%= t('devices.index.mass_operations.set_field.puavoconf_replace') %>", key: "puavoconf", type: "json" },
            { title: "<%= t('activeldap.attributes.school.puavoAllowGuest') %>", key: "allow_guest", type: "tfd" },
            { title: "<%= t('activeldap.attributes.school.puavoAutomaticImageUpdates') %>", key: "automatic_updates", type: "tfd" },
            { title: "<%= t('activeldap.attributes.school.puavoPersonalDevice') %>", key: "personal_device", type: "tfd" },
            { title: "<%= t('activeldap.attributes.school.puavoDeviceAutoPowerOffMode') %>", key: "automatic_poweroff", type: "autopoweroff" },
            { title: "<%= t('activeldap.attributes.school.puavoDeviceOnHour') %>", key: "day_start", type: "s" },
            { title: "<%= t('activeldap.attributes.school.puavoDeviceOffHour') %>", key: "day_end", type: "s" },
            { title: "<%= t('activeldap.attributes.school.puavoImageSeriesSourceURL') %>", key: "image_source_url", type: "image_source_url" },
        ];

        this.currentUI = -1;
    }

    buildInterface()
    {
        let html = "";

        html +=
`<table class="settingsChild"><tr>
<th><label for="field_name"><%= t('devices.index.mass_operations.set_field.field') %></label></th>
<td><select id="field_name">`;

        // List the possible fields in alphabetical order
        let sorted = [];

        for (let i = 0; i < this.items.length; i++)
            sorted.push([this.items[i].title.toLowerCase(), i]);

        sorted.sort((a, b) => a[0].localeCompare(b[0]));

        for (const s of sorted) {
            const item = this.items[s[1]];

            html += `<option value="${s[1]}">${item.title}</option>`;
        }

        html +=
`</select></td></tr>
<tr>
<th><label for="field_value"><%= t('devices.index.mass_operations.set_field.value') %></label></th>
<td><div id="uiChild"></div></td></tr></table>`;

        this.container.innerHTML = html;

        // Initial selection and UI
        this.currentUI = sorted[0][1];
        this.container.querySelector("select#field_name").value = this.currentUI;
        this.buildChildUI(this.currentUI);

        this.container.querySelector("select#field_name").addEventListener("change", e => {
            this.currentUI = parseInt(e.target.value, 10);
            this.buildChildUI(this.currentUI);
        });
    }

    canProceed()
    {
        if (this.items[this.currentUI].type == "json") {
            // Does the textarea contain valid JSON? This is the only time we can validate the value.
            const v = this.getValue();

            if (v.length == 0) {
                // Empty strings are not valid JSON, but we still let them through
                // because then the database field can be cleared (ie. set to null)
                return true;
            }

            try {
                const o = JSON.parse(v);

                if (!o || typeof(o) !== "object")
                    throw new Error("");        // just display the error message below
            } catch (e) {
                window.alert("<%= t('devices.index.mass_operations.set_field.invalid_json') %>");
                return false;
            }
        } else if (this.items[this.currentUI].type == "xml") {
            // Validate the monitors XML configuration
            const value = this.getValue();

            if (value == "") {
                // Permit the configuration to be cleared
                return true;
            }

            try {
                const result = new DOMParser().parseFromString(value, "application/xml");

                // Of course this isn't straightforward...
                if (result.getElementsByTagName("parsererror").length != 0)
                    throw new Error("invalid XML");
            } catch (e) {
                console.log(e);
                window.alert("<%= t('devices.index.mass_operations.set_field.invalid_xml') %>");
                return false;
            }
        }

        return true;
    }

    buildChildUI(index)
    {
        const stringWarning = `<p class="validityWarning margin-0"><%= t('devices.index.mass_operations.set_field.validity_warning_string') %></p>`,
              jsonWarning = `<p class="validityWarning margin-0"><%= t('devices.index.mass_operations.set_field.validity_warning_json') %></p>`,
              xmlWarning = `<p class="validityWarning margin-0"><%= t('devices.index.mass_operations.set_field.validity_warning_xml') %></p>`,
              imageSourceWarning = `<p class="validityWarning margin-0"><%= t('devices.index.mass_operations.set_field.validity_warning_image_source') %></p>`;

        const item = this.items[index];

        let ui = "";

        switch (item.type) {
            case "s":           // String
            default:
                ui = `<input type="text" id="field_value" size="40">${stringWarning}`;
                break;

            case "ms":          // Multiline string
                ui = `<textarea id="field_value" rows="5" cols="80"></textarea>${stringWarning}`;
                break;

            case "json":        // Same as multiline string, but displays a note about the JSON
                ui = `<textarea id="field_value" rows="5" cols="80"></textarea>${jsonWarning}`;
                break;

            case "xml":         // Same as multiline string, but the contents are validated as XML
                ui = `<textarea id="field_value" rows="5" cols="80"></textarea>${xmlWarning}`;
                break;

            case "tfd":         // Tri-state true/false/default "boolean"
                ui =
`<div class="flex-rows">
<label><input type="radio" name="value-radio" id="value-radio-default" checked><%= t('devices.index.mass_operations.set_field.default') %></label>
<label><input type="radio" name="value-radio" id="value-radio-yes"><%= t('devices.index.mass_operations.set_field.yes') %></label>
<label><input type="radio" name="value-radio" id="value-radio-no"><%= t('devices.index.mass_operations.set_field.no') %></label>
</div>`;
                break;

            case "autopoweroff":    // Custom UI for the automatic poweroff setting
                ui =
`<div class="flex-rows">
<label><input type="radio" name="value-radio" id="value-radio-default" checked><%= t('devices.index.mass_operations.set_field.default') %></label>
<label><input type="radio" name="value-radio" id="value-radio-off"><%= t('devices.index.mass_operations.set_field.off') %></label>
<label><input type="radio" name="value-radio" id="value-radio-custom"><%= t('devices.index.mass_operations.set_field.custom') %></label>
</div>`;
                break;

            case "image_source_url":    // Multiline image sources string (one URL per line)
                ui = `<textarea id="field_value" rows="5" cols="80"></textarea>${imageSourceWarning}`;
                break;
        }

        this.container.querySelector("div#uiChild").innerHTML = ui;
    }

    getOperationParameters()
    {
        return {
            field: this.items[this.currentUI].key,
            value: this.getValue()
        };
    }

    prepareItem(item)
    {
        return { state: "ready" };
    }

    getValue()
    {
        const ui = this.container.querySelector("div#uiChild");

        // Retrieve the value. There are no event handlers, so dig it from
        // the DOM directly.
        switch (this.items[this.currentUI].type) {
            case "s":
            default:
                return ui.querySelector("input").value;

            case "ms":
            case "json":
            case "xml":
                return ui.querySelector("textarea").value;

            case "tfd":
                if (ui.querySelector("input#value-radio-yes").checked)
                    return 1;
                else if (ui.querySelector("input#value-radio-no").checked)
                    return 0;

                // "default"
                return -1;

            case "autopoweroff":
                if (ui.querySelector("input#value-radio-custom").checked)
                    return "custom";
                else if (ui.querySelector("input#value-radio-off").checked)
                    return "off";

                return "default";

            case "image_source_url":
                return ui.querySelector("textarea").value.split("\n").map(r => r.trim());
        }
    }
};

// Mass operation: puavo-conf editing. Not as pretty as the interactive puavo-conf editor, but I can't cram it in this space.
// Or maybe I can? Who knows...
class MassSchoolPuavoconfEditor extends ST.MassOperation {
    constructor(parent, container)
    {
        super(parent, container);

        // Same settings as below
        this.key = "";
        this.value = null;
        this.type = "string";
        this.action = "add";
    }

    buildInterface()
    {
        this.container.innerHTML =
`<p class="margin-0 padding-0"><%= t('devices.index.mass_operations.puavoconf.json_warning').html_safe %></p>
<table class="settingsChild">
<tr>
    <th><label for="pc_key"><%= t('devices.index.mass_operations.puavoconf.key') %></label></th>
    <td><input type="text" id="pc_key" size="50"></td>
</tr>
<tr>
    <th><label for="pc_value"><%= t('devices.index.mass_operations.puavoconf.value') %></label></th>
    <td>
        <input type="text" id="pc_value" size="50">
        <p class="validityWarning margin-0 padding-0"><%= t('devices.index.mass_operations.puavoconf.validity_warning') %></p>
    </td>
</tr>
<tr>
    <th><%= t('devices.index.mass_operations.puavoconf.type') %></th>
    <td>
        <div class="flex-columns gap-10px">
            <label><input type="radio" name="pc_type" id="type_string" checked><%= t('devices.index.mass_operations.puavoconf.type_string') %></label>
            <label><input type="radio" name="pc_type" id="type_int"><%= t('devices.index.mass_operations.puavoconf.type_int') %></label>
            <label><input type="radio" name="pc_type" id="type_bool"><%= t('devices.index.mass_operations.puavoconf.type_bool') %></label>
        </div>
    </td>
</tr>
<tr>
    <th><%= t('devices.index.mass_operations.puavoconf.action') %></th>
    <td>
        <div class="flex-rows">
            <label><input type="radio" name="pc_action" id="action_add" checked><%= t('devices.index.mass_operations.puavoconf.add') %></label>
            <label><input type="radio" name="pc_action" id="action_remove"><%= t('devices.index.mass_operations.puavoconf.remove') %></label>
        </div>
    </td>
</tr>
</table>`;

        // Setup event handling
        this.container.querySelector("#pc_key").addEventListener("input", e => this.key = e.target.value.trim());
        this.container.querySelector("#pc_value").addEventListener("input", e => this.value = e.target.value.trim());
        this.container.querySelector("#type_string").addEventListener("click", () => this.type = "string");
        this.container.querySelector("#type_int").addEventListener("click", () => this.type = "int");
        this.container.querySelector("#type_bool").addEventListener("click", () => this.type = "bool");
        this.container.querySelector("#action_add").addEventListener("click", () => this.action = "add");
        this.container.querySelector("#action_remove").addEventListener("click", () => this.action = "remove");
    }

    canProceed()
    {
        // We only care about the key's length. The value can be empty.
        if (this.key.length == 0) {
            window.alert("<%= t('devices.index.mass_operations.puavoconf.empty_key') %>");
            return false;
        }

        return true;
    }

    getOperationParameters()
    {
        return {
            key: this.key,
            value: this.value,
            type: this.type,
            action: this.action
        };
    }

    prepareItem(item)
    {
        return { state: "ready" };
    }
};

// Mass tags editor
class MassSchoolTagsEditor extends ST.MassOperation {
    constructor(parent, container)
    {
        super(parent, container);

        this.tags = "";
        this.action = "add";
    }

    buildInterface()
    {
        this.container.innerHTML =
`<p class="margin-0 padding-0"><%= t('devices.index.mass_operations.tags.warning').html_safe %></p>
<table class="settingsChild">
<tr>
    <th><label for="tags_tag"><%= t('devices.index.mass_operations.tags.tag') %></label></th>
    <td>
        <input type="text" id="tags_tag" size="50">
        <p class="validityWarning margin-0 padding-0"><%= t('devices.index.mass_operations.tags.help') %></p>
    </td>
</tr>
<tr>
    <th><%= t('devices.index.mass_operations.tags.action') %></th>
    <td>
        <div class="flex-rows">
            <label><input type="radio" name="tags_action" id="action_add" checked><%= t('devices.index.mass_operations.tags.add') %></label>
            <label><input type="radio" name="tags_action" id="action_remove"><%= t('devices.index.mass_operations.tags.remove') %></label>
        </div>
    </td>
</tr>
</table>`;

        // Setup event handling
        this.container.querySelector("#tags_tag").addEventListener("input", e => this.tags = e.target.value.trim());
        this.container.querySelector("#action_add").addEventListener("click", () => this.action = "add");
        this.container.querySelector("#action_remove").addEventListener("click", () => this.action = "remove");
    }

    canProceed()
    {
        if (this.tags.length == 0) {
            window.alert("<%= t('devices.index.mass_operations.tags.empty_tag') %>");
            return false;
        }

        return true;
    }

    getOperationParameters()
    {
        return {
            tags: this.tags,
            type: this.type,
            action: this.action
        };
    }

    prepareItem(item)
    {
        return { state: "ready" };
    }
};

const MASS_OPERATIONS = [
    {
        operation: "set_field",
        title: "<%= t('devices.index.mass_operations.set_field.title') %>",
        haveSettings: true,
        cls: MassSchoolSetFieldValue
    },
    {
        operation: "puavoconf_edit",
        title: "<%= t('devices.index.mass_operations.puavoconf.title') %>",
        haveSettings: true,
        cls: MassSchoolPuavoconfEditor
    },
    {
        operation: "tags_edit",
        title: "<%= t('devices.index.mass_operations.tags.title') %>",
        haveSettings: true,
        cls: MassSchoolTagsEditor
    },
];

<% end %>

const DEFAULT_COLUMNS = ["name", "prefix", "school_code", "school_oid", "eid", "integrations", "num_members", "num_groups", "num_devices", "boot_servers"];
const DEFAULT_SORTING = { column: "name", dir: ST.SortOrder.ASCENDING };

const RAW_DATA = <%= raw JSON.pretty_generate(@data) %>;

function* synthesizeData(raw)
{
    for (const [_, s] of Object.entries(raw.schools)) {
        let s2 = {...s};

        // The supertable code always wants a "school_id" for everything, in our case
        // it's the same as the puavoId.
        s2.school_id = s.id;

        // Unpack bootserver links
        s2.boot_servers = [];

        for (const hostname of s.boot_servers)
            s2.boot_servers.push([hostname, raw.bootservers[hostname]]);

        yield s2;
    }
}

document.addEventListener("DOMContentLoaded", () => {
    modalPopup.setupGlobalEvents();

    GLOBAL_SUPERTABLE_INSTANCE = new ST.SuperTable(
        // Main container DIV
        document.getElementById("tableSchools"),

        // Settings
        {
            id: "schools",
            locale: "<%= supertable_sorting_locale %>",
            csvPrefix: `<%= LdapOrganisation.current.o %>-schools`,
<% if @is_owner %>
            enableSelection: true,
<% end %>
            enablePagination: false,
            columnDefinitions: COLUMN_DEFINITIONS,
            columnOrder: COLUMN_ORDER,
            actions: schoolActions,
<% if @is_owner %>
            massOperations: MASS_OPERATIONS,
            massOperationsEndpoint: "<%= schools_mass_operations_path %>",
<% end %>
            defaultColumns: DEFAULT_COLUMNS,
            defaultSorting: DEFAULT_SORTING,
            staticData: RAW_DATA,
            preFilterFunction: synthesizeData,
        }
    );
});

</script>
<% end %>
