<%
const {
    arrayClass,
    members,
    size,
    usedTypes,
    layoutClass,
    includeStructAccessors
} = locals

const StructTypeClass = arrayClass.replace('Array', 'Struct');
const StructArrayClass = arrayClass;
const StructArrayLayoutClass = layoutClass;
-%>
<%
// collect components
const components = [];
for (const member of members) {
    for (let c = 0; c < member.components; c++) {
        let name = member.name;
        if (member.components > 1) {
            name += c;
        }
        components.push({name, member, component: c});
    }
}

// exceptions for which we generate accessors on the array rather than a separate struct for performance
const useComponentGetters =
    StructArrayClass === 'GlyphOffsetArray' ||
    StructArrayClass === 'SymbolLineVertexArray' ||
    StructArrayClass === 'FillExtrusionCentroidArray';

if (includeStructAccessors && !useComponentGetters) {
-%>
class <%=StructTypeClass%> extends Struct {
    _structArray: <%=StructArrayClass%>;
<%
for (const {name, member, component} of components) {
    const elementOffset = `this._pos${member.size.toFixed(0)}`;
    const componentOffset = (member.offset / member.size + component).toFixed(0);
    const index = `${elementOffset} + ${componentOffset}`;
    const componentAccess = `this._structArray.${member.view}[${index}]`;
-%>
    get <%=name%>(): number { return <%=componentAccess%>; }
<%
// generate setters for properties that are updated during runtime symbol placement; others are read-only
if (name === 'crossTileID' || name === 'placedOrientation' || name === 'hidden' || name === 'flipState' || name === 'zOffset') {
-%>
    set <%=name%>(x: number) { <%=componentAccess%> = x; }
<%
}
}
-%>
}

<%=StructTypeClass%>.prototype.size = <%=size%>;

export type <%=StructTypeClass.replace('Struct', '')%> = <%=StructTypeClass%>;

<%
} // end 'if (includeStructAccessors)'
-%>
/**
 * @private
 */
export class <%=StructArrayClass%> extends <%=StructArrayLayoutClass%> {
<%
if (useComponentGetters) {
    for (const member of members) {
        for (let c = 0; c < member.components; c++) {
            if (!includeStructAccessors) continue;
            let name = `get${member.name}`;
            if (member.components > 1) {
                name += c;
            }
            const componentOffset = (member.offset / member.size + c).toFixed(0);
            const componentStride = size / member.size;
-%>
    <%=name%>(index: number): number { return this.<%=member.view%>[index * <%=componentStride%> + <%=componentOffset%>]; }
<%
        }
    }
} else if (includeStructAccessors) { // get(i)
-%>
    /**
     * Return the <%=StructTypeClass%> at the given location in the array.
     * @param {number} index The index of the element.
     * @private
     */
    get(index: number): <%=StructTypeClass%> {
        assert(!this.isTransferred);
        assert(index >= 0);
        assert(index < this.length);
        return new <%=StructTypeClass%>(this, index);
    }
<%
}
-%>
}

register(<%=StructArrayClass%>, '<%=StructArrayClass%>');
