(function (app) {
    'use strict';

    function renderHeader(theadEl, columns, sortState, filterMap, onHeaderClick, onFilterChange) {
        const util = app.modules.util;
        const filters = app.modules.filters;
        const sort = app.modules.sort;
        theadEl.innerHTML = '';

        const titleRow = util.h('tr', { className: 'zddc-table__title-row' });
        const filterRow = util.h('tr', { className: 'zddc-table__filter-row' });

        for (let i = 0; i < columns.length; i++) {
            const col = columns[i];
            const indicator = sort.indicator(sortState, col.field);
            const th = util.h('th', {
                className: 'zddc-table__th',
                'data-field': col.field,
                style: col.width ? 'width:' + col.width : null,
                onClick: function (ev) { onHeaderClick(col.field, ev.shiftKey); }
            }, col.title || col.field, indicator);
            titleRow.appendChild(th);

            const td = util.h('td', { className: 'zddc-table__filter-cell' });
            // Every column gets the same text-contains filter input, even
            // enum columns — keeps the filter row visually uniform and
            // doesn't constrain users to picking from the enum (a
            // case-insensitive substring match works for both free-text
            // and enum data).
            const f = filterMap[col.field] || filters.defaultFilterFor(col);
            const input = util.h('input', {
                type: 'text',
                className: 'zddc-table__filter-text',
                placeholder: 'filter…',
                'aria-label': 'Filter ' + (col.title || col.field),
                value: typeof f.value === 'string' ? f.value : '',
                onInput: function (ev) {
                    onFilterChange(col.field, { kind: 'contains', value: ev.target.value });
                }
            });
            td.appendChild(input);
            filterRow.appendChild(td);
        }

        theadEl.appendChild(titleRow);
        theadEl.appendChild(filterRow);
    }

    function renderBody(tbodyEl, rows, columns) {
        const util = app.modules.util;
        const editor = app.modules.editor;
        tbodyEl.innerHTML = '';
        for (let i = 0; i < rows.length; i++) {
            const row = rows[i];
            const tr = util.h('tr', {
                className: 'zddc-table__row' + (row.editable ? ' zddc-table__row--editable' : ' zddc-table__row--readonly'),
                'data-url': row.url,
                'data-editable': row.editable ? '1' : '0'
            });
            const rowId = editor ? editor.rowKey(row) : (row.url || '');
            if (editor) {
                editor.attachToRow(tr, rowId);
            }
            for (let c = 0; c < columns.length; c++) {
                const col = columns[c];
                // Editor's draft buffer overrides the row's stored value
                // until Phase 3 commits it. Falls back to row.data when
                // no draft is present.
                const value = editor
                    ? editor.effectiveCellValue(row, col)
                    : util.resolveField(row.data, col.field);
                const text = util.formatCell(value, col.format);
                const td = util.h('td', { className: 'zddc-table__cell' }, text);
                if (editor) {
                    editor.attachToCell(td, i, c);
                }
                tr.appendChild(td);
            }
            tbodyEl.appendChild(tr);
        }
    }

    function renderRowCount(el, displayed, total) {
        if (!el) return;
        if (displayed === total) {
            el.textContent = total + (total === 1 ? ' row' : ' rows');
        } else {
            el.textContent = displayed + ' of ' + total + ' rows';
        }
    }

    app.modules.render = {
        header: renderHeader,
        body: renderBody,
        rowCount: renderRowCount
    };
})(window.tablesApp);
