segment/segmentHelper.tstypescript
/**
 * @module og/segment/SegmentHelper
 */

import { N, W, S, E } from "../quadTree/quadTree";

type IndexTypeArray = Uint32Array;
type IndexesTable = [IndexTypeArray[][], IndexTypeArray[][], IndexTypeArray[][], IndexTypeArray[][]];

function NewIndexesTypedArray(arr: number[]): IndexTypeArray {
    return new Uint32Array(arr);
}

function NewIndexesTypedArrayEmpty(size: number): IndexTypeArray {
    return new Uint32Array(size);
}

function createCenterBodyIndexes(size: number): IndexTypeArray {
    let indexes = [];

    let i0 = 1,
        j0 = 1;

    let i1 = 1,
        j1 = 1;

    let ind1 = 0,
        ind2 = 0,
        nr = 0;
    for (let i = i0; i < size - 1 - i1; i++) {
        for (let j = j0; j < size - j1; j++) {
            ind1 = i * size + j;
            nr = (i + 1) * size;
            ind2 = nr + j;
            indexes.push(ind1, ind2);
        }
        indexes.push(ind2, nr + j0);
    }
    indexes.push(indexes[indexes.length - 1], size * size - size);

    return NewIndexesTypedArray(indexes);
}

function createWestNeighborSkirt(size: number, deltaGr: number): IndexTypeArray {
    let indexes = [];
    const grCount = (size - 1) / deltaGr;
    const b = size * size - size;
    let k = 0;
    for (let i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        let rind = b - size * i - size + 1,
            lind = b - size * k;
        indexes.push(lind, rind);
    }

    if (deltaGr === size - 1) {
        indexes.push(size);
        indexes.push(0);
    }

    return NewIndexesTypedArray(indexes);
}

function createNorthNeighborSkirt(size: number, deltaGr: number): IndexTypeArray {
    let indexes = [];
    const grCount = (size - 1) / deltaGr;
    let k = 0;
    for (let i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        let rind = size + i + 1,
            lind = k;
        indexes.push(lind, rind);
    }

    if (deltaGr === size - 1) {
        indexes.push(size - 2);
        indexes.push(size - 1);
    }

    return NewIndexesTypedArray(indexes);
}

function createEastNeighborSkirt(size: number, deltaGr: number): IndexTypeArray {
    let indexes = [];
    const grCount = (size - 1) / deltaGr;
    let k = 0;
    for (let i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        let rind = size * (i + 1) + size - 2,
            lind = size + size * k - 1;
        indexes.push(lind, rind);
    }

    if (deltaGr === size - 1) {
        indexes.push(size * (size - 1) - 1);
        indexes.push(size * size - 1);
    }

    return NewIndexesTypedArray(indexes);
}

function createSouthNeighborSkirt(size: number, deltaGr: number): IndexTypeArray {
    let indexes = [];
    const grCount = (size - 1) / deltaGr;
    let k = 0;
    const rb = size * (size - 1) - 2;
    const lb = size * size - 1;
    for (let i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        let rind = rb - i,
            lind = lb - k;
        indexes.push(lind, rind);
    }

    if (deltaGr === size - 1) {
        indexes.push(size * size - size + 1);
    }
    indexes.push(size * size - size);

    return NewIndexesTypedArray(indexes);
}

function initIndexesBodySkirts(pow: number): IndexesTable {
    let table: IndexesTable = [[], [], [], []];

    for (let i = 0; i <= pow; i++) {
        let d = Math.pow(2, i),
            d1 = d + 1;

        table[N][i] = [];
        table[W][i] = [];
        table[S][i] = [];
        table[E][i] = [];

        for (let j = 0; j <= pow; j++) {
            let dd = Math.pow(2, j);
            table[W][i][j] = createWestNeighborSkirt(d1, dd);
            table[N][i][j] = createNorthNeighborSkirt(d1, dd);
            table[E][i][j] = createEastNeighborSkirt(d1, dd);
            table[S][i][j] = createSouthNeighborSkirt(d1, dd);
        }
    }
    return table;
}

function initIndexBodiesTable(pow: number): IndexTypeArray[] {
    let table = [];
    for (let i = 0; i <= pow; i++) {
        const d = Math.pow(2, i);
        table[i] = createCenterBodyIndexes(d + 1);
    }
    return table;
}

function createTextureCoords(size: number): Uint16Array {
    let texCoords = new Uint16Array((size + 1) * (size + 1) * 2);
    let k = 0;
    for (let i = 0; i <= size; i++) {
        for (let j = 0; j <= size; j++) {
            texCoords[k++] = (j / size) * 0xffff;
            texCoords[k++] = (i / size) * 0xffff;
        }
    }
    return texCoords;
}

class SegmentHelper {
    protected _maxGridSize: number;
    public centerIndexesTable: IndexTypeArray[];
    public skirtsIndexesTable: IndexesTable;

    constructor(maxGridSize: number = 0) {
        this._maxGridSize = maxGridSize;
        this.centerIndexesTable = initIndexBodiesTable(this._maxGridSize);
        this.skirtsIndexesTable = initIndexesBodySkirts(this._maxGridSize);
    }

    public get maxGridSize(): number {
        return this._maxGridSize;
    }

    public init() {
        this.centerIndexesTable = initIndexBodiesTable(this._maxGridSize);
        this.skirtsIndexesTable = initIndexesBodySkirts(this._maxGridSize);
    }

    public setMaxGridSize(gridSize: number) {
        this._maxGridSize = gridSize;
        this.init();
    }

    public createSegmentIndexes(size: number, sidesSizes: [number, number, number, number]) {
        if (size) {
            let c = this.centerIndexesTable[size],
                w = this.skirtsIndexesTable[W][size][sidesSizes[W]],
                n = this.skirtsIndexesTable[N][size][sidesSizes[N]],
                e = this.skirtsIndexesTable[E][size][sidesSizes[E]],
                s = this.skirtsIndexesTable[S][size][sidesSizes[S]];

            let indexes = NewIndexesTypedArrayEmpty(c.length + w.length + n.length + e.length + s.length);

            indexes.set(c, 0);
            indexes.set(w, c.length);
            indexes.set(n, c.length + w.length);
            indexes.set(e, c.length + w.length + n.length);
            indexes.set(s, c.length + w.length + n.length + e.length);

            return indexes;
        } else {
            return NewIndexesTypedArray([0, 2, 1, 3]);
        }
    }

    public initTextureCoordsTable(pow: number): Uint16Array[] {
        let table = [];
        for (let i = 0; i <= pow; i++) {
            const d = Math.pow(2, i);
            table[i] = createTextureCoords(d);
        }
        return table;
    }
}

let instance = new SegmentHelper();

export function getInstance() {
    return instance;
}