"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GroupBlock = exports.RoleBlock = exports.BreaksBlock = exports.BeatDurationBlock = exports.CycleBlock = exports.RepeatBlock = exports.SectionBlock = exports.Block = exports.RawBlock = exports.RoleDef = void 0;
exports.isBlock = isBlock;
exports.isLine = isLine;
exports.isRawBlock = isRawBlock;
exports.findContainingBlock = findContainingBlock;
const entity_1 = require("./entity");
const core_1 = require("./core");
class RoleDef {
    constructor() {
        this.name = "";
        this.notesOnly = false;
        this.index = 0;
    }
}
exports.RoleDef = RoleDef;
class RawBlock extends entity_1.Entity {
    constructor(content, contentType = "md") {
        super();
        this.content = content;
        this.contentType = contentType;
        this.TYPE = "RawBlock";
    }
    debugValue() {
        return Object.assign(Object.assign({}, super.debugValue()), { content: this.content, contentType: this.contentType });
    }
}
exports.RawBlock = RawBlock;
function isBlock(item) {
    return item.TYPE === "Block";
}
function isLine(item) {
    return item.TYPE === "Line";
}
function isRawBlock(item) {
    return item.TYPE === "RawBlock";
}
class Block extends entity_1.Entity {
    constructor(blockType, parent = null, name = null) {
        super();
        this.TYPE = "Block";
        this.blockItems = [];
        this.localCycle = null;
        this.localAtomsPerBeat = null;
        this.localBreaks = null;
        this.localRoles = new Map();
        this._parentBlock = null;
        this._currRoleDef = null;
        this._currentLine = null;
        this.blockType = blockType;
        this.name = name;
        this._parentBlock = parent;
        if (parent) {
            this.setParent(parent);
        }
    }
    children() {
        return this.blockItems;
    }
    get parentBlock() {
        return this._parentBlock;
    }
    get cycle() {
        var _a, _b;
        if (this.localCycle !== null) {
            return this.localCycle;
        }
        return (_b = (_a = this.parentBlock) === null || _a === void 0 ? void 0 : _a.cycle) !== null && _b !== void 0 ? _b : null;
    }
    get atomsPerBeat() {
        var _a, _b;
        if (this.localAtomsPerBeat !== null) {
            return this.localAtomsPerBeat;
        }
        return (_b = (_a = this.parentBlock) === null || _a === void 0 ? void 0 : _a.atomsPerBeat) !== null && _b !== void 0 ? _b : 1;
    }
    get breaks() {
        var _a, _b;
        if (this.localBreaks !== null) {
            return this.localBreaks;
        }
        return (_b = (_a = this.parentBlock) === null || _a === void 0 ? void 0 : _a.breaks) !== null && _b !== void 0 ? _b : [];
    }
    getRole(name) {
        var _a, _b;
        const local = this.localRoles.get(name.toLowerCase());
        if (local) {
            return local;
        }
        return (_b = (_a = this.parentBlock) === null || _a === void 0 ? void 0 : _a.getRole(name)) !== null && _b !== void 0 ? _b : null;
    }
    get currRoleDef() {
        if (this._currRoleDef !== null) {
            return this._currRoleDef;
        }
        if (this.parentBlock) {
            return this.parentBlock.currRoleDef;
        }
        const roles = Array.from(this.localRoles.values());
        return roles.length > 0 ? roles[roles.length - 1] : null;
    }
    setCurrRole(name) {
        name = name.trim().toLowerCase();
        if (name === "") {
            throw new Error("Role name cannot be empty");
        }
        let roleDef = this.getRole(name);
        if (roleDef == null) {
            roleDef = this.newRoleDef(name, name === "sw");
        }
        this._currRoleDef = roleDef;
    }
    get currentLine() {
        if (this._currentLine === null) {
            return this.newLine();
        }
        return this._currentLine;
    }
    newLine() {
        if (this._currentLine !== null && this._currentLine.isEmpty) {
            this.removeBlockItem(this._currentLine);
        }
        this._currentLine = new core_1.Line();
        this.addBlockItem(this._currentLine);
        return this._currentLine;
    }
    newRoleDef(name, notesOnly = false) {
        name = name.trim().toLowerCase();
        if (name === "") {
            throw new Error("Role name cannot be empty");
        }
        if (this.localRoles.has(name)) {
            throw new Error(`Role '${name}' already exists in this block`);
        }
        const rd = new RoleDef();
        rd.name = name;
        rd.notesOnly = notesOnly;
        rd.index = this.localRoles.size;
        this.localRoles.set(name, rd);
        return rd;
    }
    addBlockItem(item) {
        item.setParent(this);
        this.blockItems.push(item);
    }
    removeBlockItem(item) {
        const index = this.blockItems.indexOf(item);
        if (index >= 0) {
            this.blockItems.splice(index, 1);
            item.setParent(null);
        }
        return index;
    }
    debugValue() {
        const out = Object.assign(Object.assign({}, super.debugValue()), { blockType: this.blockType, blockItems: this.blockItems.map((c) => c.debugValue()) });
        if (this.name) {
            out.name = this.name;
        }
        if (this.localCycle) {
            out.localCycle = this.localCycle.uuid;
        }
        if (this.localAtomsPerBeat !== null) {
            out.localAtomsPerBeat = this.localAtomsPerBeat;
        }
        if (this.localBreaks !== null) {
            out.localBreaks = this.localBreaks;
        }
        if (this.localRoles.size > 0) {
            out.localRoles = Array.from(this.localRoles.keys());
        }
        return out;
    }
}
exports.Block = Block;
function findContainingBlock(entity) {
    let current = entity.parent;
    while (current !== null) {
        if (current instanceof Block) {
            return current;
        }
        current = current.parent;
    }
    return null;
}
class SectionBlock extends Block {
    constructor(sectionName, parent = null) {
        super("section", parent, sectionName);
    }
    children() {
        const heading = new RawBlock(`# ${this.name}`, "md");
        return [heading, ...this.blockItems];
    }
}
exports.SectionBlock = SectionBlock;
class RepeatBlock extends Block {
    constructor(repeatCount, parent = null) {
        super("repeat", parent);
        this.repeatCount = repeatCount;
    }
    children() {
        if (this.repeatCount <= 0) {
            return this.blockItems;
        }
        const expanded = [];
        for (let i = 0; i < this.repeatCount; i++) {
            expanded.push(...this.blockItems);
        }
        return expanded;
    }
}
exports.RepeatBlock = RepeatBlock;
class CycleBlock extends Block {
    constructor(cycle, parent = null) {
        super("cycle", parent);
        this.localCycle = cycle;
    }
}
exports.CycleBlock = CycleBlock;
class BeatDurationBlock extends Block {
    constructor(atomsPerBeat, parent = null) {
        super("beatduration", parent);
        this.localAtomsPerBeat = atomsPerBeat;
    }
}
exports.BeatDurationBlock = BeatDurationBlock;
class BreaksBlock extends Block {
    constructor(breaks, parent = null) {
        super("breaks", parent);
        this.localBreaks = breaks;
    }
}
exports.BreaksBlock = BreaksBlock;
class RoleBlock extends Block {
    constructor(roleName, notesOnly, parent = null) {
        super("role", parent);
        this.newRoleDef(roleName, notesOnly);
    }
}
exports.RoleBlock = RoleBlock;
class GroupBlock extends Block {
    constructor(groupName, parent = null) {
        super("group", parent, groupName);
    }
}
exports.GroupBlock = GroupBlock;
//# sourceMappingURL=block.js.map