import { Log } from '../util/index';
/*
 IMPORTANT!
 Note #1: Dependency cycles - These classes must be in a single file (module) because of commonjs and circular dependency issues.
 Note #2 Dependent ordering - Important! : Because of typescript's 'extends' function, order matters in this file!  super classes must be first!
 */
// Skipped in initial port: BarChart, BarcodeScanner, BarOrientation, Defaults, GaugeChart
const XML_CELL = 'Cell';
const XML_FORM = 'Form';
const XML_GRID = 'Grid';
const XML_PAGE = 'Page';
const XML_BUTTON = 'Button';
const XML_CHECKBOX = 'CheckBox';
const XML_DATE_PICKER = 'DatePicker';
const XML_IMAGE = 'Image';
const XML_LABEL = 'Label';
const XML_SIGNATURE_CAPTURE = 'SignatureCapture';
const XML_TEXT_AREA = 'TextArea';
const XML_TEXT_FIELD = 'TextField';
const XML_TIME_PICKER = 'TimePicker';
const XML_VALUE_PICKER = 'ValuePicker';
const XML_CHILDREN = 'Children';
const XML_ALLOW_ANNOTATIONS = 'AllowAnnotations';
const XML_ALLOW_PICKER = 'AllowPicker';
const XML_ALLOW_PICK_OPTIONS = 'AllowPickOptions';
const XML_ALPHA = 'Alpha';
const XML_ASPECT_MODE = 'AspectMode';
const XML_BACKGROUND_COLOR = 'BackgroundColor';
const XML_BINDING = 'Binding';
const XML_BLUE = 'Blue';
const XML_BOLD = 'Bold';
const XML_BORDER_COLOR = 'BorderColor';
const XML_BORDER_WIDTHS = 'BorderWidths';
const XML_BOTTOM = 'Bottom';
const XML_CAP_INSETS = 'CapInsets';
const XML_CAPTURE_BOUNDS = 'CaptureBounds';
const XML_CHECKED_COLOR = 'CheckedColor';
const XML_COLUMN = 'Column';
const XML_ENABLED_IN_READ_MODE = 'EnabledInReadMode';
const XML_ENTRY_SEQ = 'EntrySeq';
const XML_GREEN = 'Green';
const XML_FILL_PARENT = 'FillParent';
const XML_HEIGHT = 'Height';
const XML_ID = 'Id';
const XML_ITALIC = 'Italic';
const XML_LAYOUT = 'Layout';
const XML_LEFT = 'Left';
const XML_LINE_COLOR = 'LineColor';
const XML_LINE_WIDTH = 'LineWidth';
const XML_NUMBER_OF_LINES = 'NumberOfLines';
const XML_ORIGIN = 'Origin';
const XML_PADDING = 'Padding';
const XML_RADIO_GROUP = 'RadioGroup';
const XML_RED = 'Red';
const XML_REFRESH_TIMER = 'RefreshTimer';
const XML_RESIZE_MODE = 'ResizeMode';
const XML_RIGHT = 'Right';
const XML_ROW = 'Row';
const XML_SIZE = 'Size';
const XML_TEXT = 'Text';
const XML_TEXT_ALIGNMENT = 'TextAlignment';
const XML_TEXT_COLOR = 'TextColor';
const XML_TOP = 'Top';
const XML_UNCHECKED_COLOR = 'UncheckedColor';
const XML_UNDERLINE = 'Underline';
const XML_UOM = 'UOM';
const XML_URL = 'URL';
const XML_WIDTH = 'Width';
const XML_X = 'X';
const XML_Y = 'Y';
export class TextAttributes {
    constructor() {
        this.bold = false;
        this.italic = false;
        this.underline = false;
        this.numberOfLines = 1;
    }
}
/**
 * *********************************
 */
let GenID = 1; //  Generate a unique number if need be for IDs
export class Spec {
    constructor(node) {
        this.nodeChildDict = {};
        if (node) {
            PrintUtil.forEachChildNode(node, (n) => {
                this.nodeChildDict[n.nodeName] = n;
            });
        }
    }
}
export class Component extends Spec {
    constructor(parentContainer, node, overrideLayout) {
        super(node);
        this._parentContainer = parentContainer;
        if (node) {
            PrintUtil.ifChild(this.nodeChildDict[XML_BACKGROUND_COLOR], (n) => {
                this._backgroundColor = new Color(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_BINDING], (n) => {
                this._binding = new Binding(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_ID], (n) => {
                this._id = PrintUtil.singleChildText(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_LAYOUT], (n) => {
                this._layout = new Layout(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_PADDING], (n) => {
                this._padding = new Edges(n);
            });
            if (!this.id) {
                this._id = 'GenID-' + GenID++;
            }
        }
    }
    initComponentWith(overrideLayout) {
        if (overrideLayout) {
            this._layout = overrideLayout;
        }
        if (!this.id) {
            this._id = 'Generated-' + GenID++;
        }
    }
    get backgroundColor() {
        return this._backgroundColor;
    }
    get binding() {
        return this._binding;
    }
    get id() {
        return this._id;
    }
    get layout() {
        return this._layout;
    }
    get padding() {
        return this._padding;
    }
    get actualHeights() {
        return this._actualHeights;
    }
    get actualWidths() {
        return this._actualWidths;
    }
    get actualX() {
        return this._actualX;
    }
    get actualY() {
        return this._actualY;
    }
    get height() {
        return this._height;
    }
    get parentContainer() {
        return this._parentContainer;
    }
    get textAttributes() {
        return this._textAttributes;
    }
    get width() {
        return this._width;
    }
    get x() {
        return this._x;
    }
    get y() {
        return this._y;
    }
}
export class Container extends Component {
    constructor(parentContainer, node, overrideContainerWidth) {
        super(parentContainer, node);
        this._children = [];
        if (this.nodeChildDict[XML_CHILDREN]) {
            PrintUtil.forEachChildNode(this.nodeChildDict[XML_CHILDREN], (n) => {
                const c = ComponentFactory.fromNode(n, this);
                if (c) {
                    this._children.push(c);
                }
            });
        }
    }
    get children() {
        return this._children;
    }
    get containerWidth() {
        if (!this._containerWidth) {
            this.calcAndAssignContainerWidth(this.parentContainer);
        }
        return this._containerWidth;
    }
    assignChildren(c) {
        this._children = c;
    }
    assignContainerWidth(width) {
        this._containerWidth = width;
    }
    assignParentWidth(parentWidth) {
        this.assignContainerWidth(this.layout.sumOfWidths(parentWidth));
    }
    calcAndAssignContainerWidth(parentContainer) {
        // Overriden by Form and Cell for altered behavior
        const parentWidth = parentContainer.containerWidth;
        if (this.layout == null) {
            throw Error('Bogus');
        }
        this.assignContainerWidth(this.layout.sumOfWidths(parentWidth));
    }
    initContainerWith(overrideLayout, overideChildren) {
        this.initComponentWith(overrideLayout);
        if (overideChildren) {
            this.assignChildren(overideChildren);
        }
    }
}
export class PrintProperty extends Spec {
    constructor(node) {
        super(node);
    }
}
/**
 * *********************************
 */
export var AspectMode;
(function (AspectMode) {
    AspectMode[AspectMode["None"] = 0] = "None";
    AspectMode[AspectMode["Fit"] = 1] = "Fit";
    AspectMode[AspectMode["Fill"] = 2] = "Fill";
})(AspectMode || (AspectMode = {}));
export var BindingType;
(function (BindingType) {
    BindingType[BindingType["Data"] = 0] = "Data";
    BindingType[BindingType["Meta"] = 1] = "Meta";
})(BindingType || (BindingType = {}));
export var FormMode;
(function (FormMode) {
    FormMode[FormMode["Display"] = 0] = "Display";
    FormMode[FormMode["Edit"] = 1] = "Edit";
})(FormMode || (FormMode = {}));
export var ResizeMode;
(function (ResizeMode) {
    ResizeMode[ResizeMode["Stretch"] = 0] = "Stretch";
    ResizeMode[ResizeMode["Tile"] = 1] = "Tile";
})(ResizeMode || (ResizeMode = {}));
export var RichNumUsage;
(function (RichNumUsage) {
    RichNumUsage[RichNumUsage["Undefined"] = 0] = "Undefined";
    RichNumUsage[RichNumUsage["Absolute"] = 1] = "Absolute";
    RichNumUsage[RichNumUsage["FillParent"] = 2] = "FillParent";
    RichNumUsage[RichNumUsage["PercentOfParent"] = 3] = "PercentOfParent";
})(RichNumUsage || (RichNumUsage = {}));
export var RichNumUsageRef;
(function (RichNumUsageRef) {
    RichNumUsageRef[RichNumUsageRef["Undefined"] = 0] = "Undefined";
    RichNumUsageRef[RichNumUsageRef["Absolute"] = 1] = "Absolute";
    RichNumUsageRef[RichNumUsageRef["FillParent"] = 2] = "FillParent";
    RichNumUsageRef[RichNumUsageRef["HorizontalCenter"] = 3] = "HorizontalCenter";
    RichNumUsageRef[RichNumUsageRef["HorizontalLeft"] = 4] = "HorizontalLeft";
    RichNumUsageRef[RichNumUsageRef["HorizontalRight"] = 5] = "HorizontalRight";
    RichNumUsageRef[RichNumUsageRef["PercentOfParent"] = 6] = "PercentOfParent";
    RichNumUsageRef[RichNumUsageRef["Remainder"] = 7] = "Remainder";
    RichNumUsageRef[RichNumUsageRef["VerticalBottom"] = 8] = "VerticalBottom";
    RichNumUsageRef[RichNumUsageRef["VerticalCenter"] = 9] = "VerticalCenter";
    RichNumUsageRef[RichNumUsageRef["VerticalTop"] = 10] = "VerticalTop";
})(RichNumUsageRef || (RichNumUsageRef = {})); // Just for reference
export var TextAlignmentUsage;
(function (TextAlignmentUsage) {
    TextAlignmentUsage[TextAlignmentUsage["Left"] = 0] = "Left";
    TextAlignmentUsage[TextAlignmentUsage["Center"] = 1] = "Center";
    TextAlignmentUsage[TextAlignmentUsage["Right"] = 2] = "Right";
})(TextAlignmentUsage || (TextAlignmentUsage = {}));
export var ValuePlacement;
(function (ValuePlacement) {
    ValuePlacement[ValuePlacement["absolute"] = 0] = "absolute";
    ValuePlacement[ValuePlacement["none"] = 1] = "none";
})(ValuePlacement || (ValuePlacement = {}));
export var ValueType;
(function (ValueType) {
    ValueType[ValueType["Undefined"] = 0] = "Undefined";
    ValueType[ValueType["Boolean"] = 1] = "Boolean";
    ValueType[ValueType["Date"] = 2] = "Date";
    ValueType[ValueType["DateTime"] = 3] = "DateTime";
    ValueType[ValueType["Decimal"] = 4] = "Decimal";
    ValueType[ValueType["Float"] = 5] = "Float";
    ValueType[ValueType["Integer"] = 6] = "Integer";
    ValueType[ValueType["LargeBinary"] = 7] = "LargeBinary";
    ValueType[ValueType["LargeString"] = 8] = "LargeString";
    ValueType[ValueType["String"] = 9] = "String";
    ValueType[ValueType["Time"] = 10] = "Time";
})(ValueType || (ValueType = {}));
export class Binding extends PrintProperty {
    constructor(node) {
        super(node);
        this._type = BindingType.Data;
        this._path = PrintUtil.singleChildText(node);
    }
    get constantValue() {
        // Constants are of the form propName[c], where c is the constant
        let w = null;
        if (this.hasConstant) {
            const left = this.path.indexOf('[');
            const right = this.path.indexOf(']');
            w = this.path.substr(left + 1, right - left - 1);
        }
        return w;
    }
    get hasAction() {
        return this.path.indexOf('catavolt:action') > -1;
    }
    get hasConstant() {
        // Constants are of the form propName[c], where c is the constant
        const left = this.path.indexOf('[');
        const right = this.path.indexOf(']');
        return left > -1 && right > -1 && right - left > 0;
    }
    get path() {
        return this._path;
    }
    get propertyName() {
        let w = null;
        if (this.hasConstant) {
            // Constants are of the form propName[c], where c is the constant
            // The binding still refrences a property name.
            w = this.path.substr(0, this.path.indexOf('['));
        }
        else if (this.hasAction) {
            // No property for an action
        }
        else {
            w = this.path;
        }
        return w;
    }
    get type() {
        return this._type;
    }
}
export class Button extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        PrintUtil.ifChild(this.nodeChildDict[XML_ASPECT_MODE], (n) => {
            this._aspectMode = PrintUtil.enumValue(n, AspectMode);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_CAP_INSETS], (n) => {
            this._capInsets = new Edges(node);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_RESIZE_MODE], (n) => {
            this._resizeMode = PrintUtil.enumValue(n, ResizeMode);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_URL], (n) => {
            this._urlString = PrintUtil.singleChildText(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_ENABLED_IN_READ_MODE], (n) => {
            this._enabledInReadMode = PrintUtil.singleChildBoolean(n);
        });
    }
    get aspectMode() {
        return this._aspectMode;
    }
    get capInsets() {
        return this._capInsets;
    }
    get resizeMode() {
        return this._resizeMode;
    }
    get urlString() {
        return this._urlString;
    }
    get enableInReadMode() {
        return this._enabledInReadMode;
    }
}
export class CaptureBounds extends PrintProperty {
    constructor(node) {
        super(node);
        PrintUtil.ifChild(this.nodeChildDict[XML_HEIGHT], (n) => {
            this._height = PrintUtil.singleChildRichNum(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_WIDTH], (n) => {
            this._width = PrintUtil.singleChildRichNum(n);
        });
    }
    get height() {
        return this._height;
    }
    get width() {
        return this._width;
    }
}
export class PrintCell extends Container {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        this._grid = parentContainer;
        PrintUtil.ifChild(this.nodeChildDict[XML_BORDER_COLOR], (n) => {
            this._borderColor = new Color(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_BORDER_WIDTHS], (n) => {
            this._borderWidths = new Edges(n);
        });
    }
    get borderColor() {
        return this._borderColor;
    }
    get borderWidths() {
        return this._borderWidths;
    }
    get cellHeight() {
        return this._grid.layout.heights[this.layout.row].value;
    }
    get cellWidth() {
        return this._grid.layout.widths[this.layout.column].resolveWithFill(this._grid.containerWidth);
    }
    get componentChildren() {
        const answer = [];
        this.children.map(c => {
            if (!(c instanceof Grid)) {
                answer.push(c);
            }
        });
        return answer;
    }
    get gridChildren() {
        const answer = [];
        this.children.map(c => {
            if (c instanceof Grid) {
                answer.push(c);
            }
        });
        return answer;
    }
    initCellWith(overrideLayout, overrideChildren) {
        let ol = overrideLayout;
        if (!this.layout && !overrideLayout) {
            ol = new Layout(null, NaN, NaN, NaN, NaN, 0, 0);
        }
        this.initContainerWith(ol, overrideChildren);
        if (!this.borderWidths) {
            this._borderWidths = new Edges();
            this._borderWidths.initEdgesWith(0, 0, 0, 0);
        }
    }
    calcAndAssignContainerWidth(parentContainer) {
        if (parentContainer.layout == null) {
            throw Error('bogus');
        }
        const cw = parentContainer.layout.widths[this.layout.column].resolveWithFill(parentContainer.containerWidth);
        this.assignContainerWidth(cw);
    }
}
export class Checkbox extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        PrintUtil.ifChild(this.nodeChildDict[XML_CHECKED_COLOR], (n) => {
            this._checkedColor = new Color(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_ENTRY_SEQ], (n) => {
            this._entrySeq = PrintUtil.singleChildInt(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_LINE_COLOR], (n) => {
            this._lineColor = new Color(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_LINE_WIDTH], (n) => {
            this._lineWidth = PrintUtil.singleChildFloat(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_RADIO_GROUP], (n) => {
            this._radioGroup = PrintUtil.singleChildText(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_UNCHECKED_COLOR], (n) => {
            this._uncheckedColor = new Color(n);
        });
    }
    get checkedColor() {
        return this._checkedColor;
    }
    get entrySeq() {
        return this._entrySeq;
    }
    get lineColor() {
        return this._lineColor;
    }
    get lineWidth() {
        return this._lineWidth;
    }
    get radioGroup() {
        return this._radioGroup;
    }
    get uncheckedColor() {
        return this._uncheckedColor;
    }
}
export class Color extends Spec {
    static WHITE() {
        const c = new Color(null);
        c._red = 255;
        c._green = 255;
        c._blue = 255;
        c._alpha = 255;
        return c;
    }
    static BLACK() {
        const c = new Color(null);
        c._red = 0;
        c._green = 0;
        c._blue = 0;
        c._alpha = 255;
        return c;
    }
    constructor(node, red, green, blue, alpha) {
        super(node);
        PrintUtil.ifChild(this.nodeChildDict[XML_RED], (n) => {
            this._red = PrintUtil.singleChildInt(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_BLUE], (n) => {
            this._blue = PrintUtil.singleChildInt(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_GREEN], (n) => {
            this._green = PrintUtil.singleChildInt(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_ALPHA], (n) => {
            this._alpha = PrintUtil.singleChildFloat(n);
        });
        if (red !== undefined) {
            this._red = red;
        }
        if (green !== undefined) {
            this._green = green;
        }
        if (blue !== undefined) {
            this._blue = blue;
        }
        if (alpha !== undefined) {
            this._alpha = alpha;
        }
    }
    get alpha() {
        return this._alpha;
    }
    get red() {
        return this._red;
    }
    get green() {
        return this._green;
    }
    get blue() {
        return this._blue;
    }
}
export class DatePicker extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        this._textAttributes = new TextAttributes();
        PrintUtil.ifChild(this.nodeChildDict[XML_ENTRY_SEQ], (n) => {
            this._entrySeq = PrintUtil.singleChildInt(n);
        });
        PrintUtil.importTextAttributes(this.nodeChildDict, this.textAttributes);
    }
    getTextAttributes() {
        return this.textAttributes;
    }
    get entrySeq() {
        return this._entrySeq;
    }
}
export class Edges extends Spec {
    constructor(node) {
        super(node);
        if (node) {
            PrintUtil.ifChild(this.nodeChildDict[XML_TOP], (n) => {
                this._top = PrintUtil.singleChildFloat(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_LEFT], (n) => {
                this._left = PrintUtil.singleChildFloat(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_BOTTOM], (n) => {
                this._bottom = PrintUtil.singleChildFloat(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_RIGHT], (n) => {
                this._right = PrintUtil.singleChildFloat(n);
            });
        }
    }
    initEdgesWith(top, left, bottom, right) {
        this._top = top;
        this._left = left;
        this._bottom = bottom;
        this._right = right;
    }
    get top() {
        return this._top;
    }
    get left() {
        return this._left;
    }
    get bottom() {
        return this._bottom;
    }
    get right() {
        return this._right;
    }
}
export class PrintForm extends Container {
    static fromXMLString(xmlString) {
        const xml = new DOMParser().parseFromString(xmlString, 'text/xml');
        return new PrintForm(xml.childNodes[0]);
    }
    constructor(node) {
        if (node) {
            // Because super fluffs children, we need to pre-get the Layout to know this Form's width
            PrintUtil.forEachChildNode(node, (n) => {
                if (n.nodeName == 'Layout') {
                    const tempLayout = new Layout(n);
                    this.assignContainerWidth(tempLayout.singleWidthNum());
                }
            });
        }
        // node.childNodes.
        super(null, node);
        // NOTE: Some forms have no pages, other forms have pages.  A page always contains exactly
        //       one 1x1 grid positioned at 0,0 and is the size of the form.
        //       Doctor up the children such that there are always Pages.  If no pages exist, then
        //       create a Page and a Grid and add these children as the content.
        if (this.children && this.children.length && this.children[0] instanceof Grid) {
            const p = new Page(this);
            const g = new Grid(p);
            const c = new PrintCell(g);
            c.initCellWith(new Layout(null, 0, 0, 0, 0, 0, 0), this.children);
            g.initGridWith(new Layout(null, 0, 0, this.layout.singleWidthNum(), this.layout.singleHeightNum()), [c]);
            p.initPageWith(g);
            this.assignChildren([p]);
        }
    }
    get hideControlFraming() {
        return this._hideControlFraming;
    }
    get hideSaveCancelButtons() {
        return this._hideSaveCancelButtons;
    }
    get pageChildren() {
        return this.children;
    }
    get settings() {
        return this._settings;
    }
    calcAndAssignContainerWidth(parentContainer) {
        this.assignContainerWidth(this.layout.singleWidthNum());
    }
}
export class Grid extends Container {
    constructor(parentContainer, node) {
        super(parentContainer, node);
    }
    get gridLines() {
        if (!this._gridLines) {
            this._gridLines = [];
            let hasHLines = false;
            let hasVLines = false;
            const cols = this.layout.widths.length;
            const rows = this.layout.heights.length;
            const vLines = new Array(rows * cols + rows); // Distinct vertical lines
            const hLines = new Array(rows * cols + cols); // Distinct horizointal lines
            let colStart = 0;
            for (let c = 0; c < cols; c++) {
                let rowStart = 0;
                const colWidth = this.layout.widths[c].resolveWithFill(this.parentContainer.containerWidth);
                for (let r = 0; r < rows; r++) {
                    const rowHeight = this.layout.heights[r].resolveWithFill(this.parentContainer.containerWidth);
                    // Vertical lines
                    let lineWidth = 0;
                    if (c == 0) {
                        // Left most vertical
                        lineWidth = this.cellChildren[r][c].borderWidths.left;
                    }
                    else {
                        // Middle verticals
                        lineWidth = Math.max(this.cellChildren[r][c - 1].borderWidths.right, this.cellChildren[r][c].borderWidths.left);
                    }
                    if (lineWidth) {
                        vLines[c * rows + r] = new GridLine(new Point(colStart, rowStart), new Point(colStart, rowStart + rowHeight), lineWidth);
                        hasVLines = true;
                    }
                    if (c == cols - 1) {
                        // Right most vertical
                        lineWidth = this.cellChildren[r][c].borderWidths.right;
                        if (lineWidth) {
                            // Place these appropriately so the combining logic can work.
                            vLines[cols * rows + r] = new GridLine(new Point(colStart + colWidth, rowStart), new Point(colStart + colWidth, rowStart + rowHeight), lineWidth);
                            hasVLines = true;
                        }
                    }
                    // Horizontal lines
                    lineWidth = 0;
                    if (r == 0) {
                        // Top most horizontal
                        lineWidth = this.cellChildren[r][c].borderWidths.top;
                    }
                    else {
                        // Middle horizontals
                        lineWidth = Math.max(this.cellChildren[r - 1][c].borderWidths.bottom, this.cellChildren[r][c].borderWidths.top);
                    }
                    if (lineWidth) {
                        hLines[r * cols + c] = new GridLine(new Point(colStart, rowStart), new Point(colStart + colWidth, rowStart), lineWidth);
                        hasHLines = true;
                    }
                    if (r == rows - 1) {
                        // Bottom most horizontal
                        lineWidth = this.cellChildren[r][c].borderWidths.bottom;
                        if (lineWidth) {
                            // Place these appropriately so the combining logic can work.
                            hLines[cols * rows + c] = new GridLine(new Point(colStart, rowStart + rowHeight), new Point(colStart + colWidth, rowStart + rowHeight), lineWidth);
                            hasHLines = true;
                        }
                    }
                    rowStart += rowHeight;
                }
                colStart += colWidth;
            }
            // Combine adjacent lines
            let lastX = NaN;
            let lastEndY = NaN;
            let lastLineWidth = NaN;
            let lastPush = this._gridLines.length - 1;
            if (hasVLines) {
                for (const gl of vLines) {
                    if (gl) {
                        if (isNaN(lastEndY)) {
                            lastEndY = gl.end.y;
                            lastX = gl.end.x;
                            lastLineWidth = gl.lineWidth;
                            this._gridLines.push(gl);
                            lastPush++;
                        }
                        else if (gl.start.y == lastEndY && gl.start.x == lastX && gl.lineWidth == lastLineWidth) {
                            // This line and the previous can be combined
                            lastEndY = gl.end.y;
                            this._gridLines[lastPush].end = gl.end;
                        }
                        else {
                            this._gridLines.push(gl);
                            lastPush++;
                            lastEndY = gl.end.y;
                            lastX = gl.end.x;
                            lastLineWidth = gl.lineWidth;
                        }
                    }
                }
            }
            if (hasHLines) {
                let lastEndX = NaN;
                let lastY = NaN;
                lastLineWidth = NaN;
                lastPush = this._gridLines.length - 1;
                for (const gl of hLines) {
                    if (gl) {
                        if (isNaN(lastEndX)) {
                            lastEndX = gl.end.x;
                            lastY = gl.end.y;
                            lastLineWidth = gl.lineWidth;
                            this._gridLines.push(gl);
                            lastPush++;
                        }
                        else if (gl.start.x == lastEndX && gl.start.y == lastY && gl.lineWidth == lastLineWidth) {
                            // This line and the previous can be combined
                            lastEndX = gl.end.x;
                            this._gridLines[lastPush].end = gl.end;
                        }
                        else {
                            this._gridLines.push(gl);
                            lastPush++;
                            lastEndX = gl.end.x;
                            lastY = gl.end.y;
                            lastLineWidth = gl.lineWidth;
                        }
                    }
                }
            }
        }
        return this._gridLines;
    }
    initCells() {
        // Structure the cells so that they can be retrieved
        this._cellChildren = new Array(this.layout.heights.length);
        for (let i = 0; i < this.layout.heights.length; i++) {
            this._cellChildren[i] = new Array(this.layout.widths.length);
        }
        this.children.map((c) => {
            this._cellChildren[c.layout.row][c.layout.column] = c;
        });
    }
    get cellChildren() {
        if (this._cellChildren == null) {
            this.initCells();
        }
        return this._cellChildren;
    }
    initGridWith(overrideLayout, overrideChildren) {
        super.initContainerWith(overrideLayout, overrideChildren);
        this.initCells();
    }
}
export class GridLine {
    constructor(start, end, lineWidth) {
        this.start = start;
        this.end = end;
        this.lineWidth = lineWidth;
    }
}
export class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}
export class Image extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        PrintUtil.ifChild(this.nodeChildDict[XML_ALLOW_ANNOTATIONS], (n) => {
            this._allowAnnotations = PrintUtil.singleChildBoolean(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_ALLOW_PICKER], (n) => {
            this._allowPicker = PrintUtil.singleChildBoolean(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_ALLOW_PICK_OPTIONS], (n) => {
            this._allowPickOptions = PrintUtil.singleChildBoolean(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_ASPECT_MODE], (n) => {
            this._aspectMode = PrintUtil.enumValue(n, AspectMode);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_CAP_INSETS], (n) => {
            this._capInsets = new Edges(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_RESIZE_MODE], (n) => {
            this._resizeMode = PrintUtil.enumValue(n, ResizeMode);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_URL], (n) => {
            this._urlString = PrintUtil.singleChildText(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_CAPTURE_BOUNDS], (n) => {
            this._captureBounds = new CaptureBounds(n);
        });
    }
    get allowAnnotations() {
        return this._allowAnnotations;
    }
    get allowPicker() {
        return this._allowPicker;
    }
    get allowPickOptions() {
        return this._allowPickOptions;
    }
    get aspectMode() {
        return this._aspectMode;
    }
    get capInsets() {
        return this._capInsets;
    }
    get resizeMode() {
        return this._resizeMode;
    }
    get urlString() {
        return this._urlString;
    }
    get capatureBounds() {
        return this._captureBounds;
    }
}
export class Label extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        this._textAttributes = new TextAttributes();
        PrintUtil.importTextAttributes(this.nodeChildDict, this._textAttributes);
        PrintUtil.ifChild(this.nodeChildDict[XML_TEXT], (n) => {
            this._text = PrintUtil.singleChildText(n);
        });
    }
    get text() {
        return this._text;
    }
}
export class Layout extends Spec {
    constructor(node, x, y, width, height, row, col) {
        super(node);
        if (node) {
            PrintUtil.ifChild(this.nodeChildDict[XML_UOM], (n) => {
                this._uom = PrintUtil.singleChildText(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_COLUMN], (n) => {
                this._column = PrintUtil.singleChildInt(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_ROW], (n) => {
                this._row = PrintUtil.singleChildInt(n);
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_SIZE], (n) => {
                this._heights = PrintUtil.arrayOfRichNums(n, 'Height');
                this._widths = PrintUtil.arrayOfRichNums(n, 'Width');
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_FILL_PARENT], (n) => {
                this._heights = [new RichNum(NaN, RichNumUsage.FillParent)];
                this._widths = [new RichNum(NaN, RichNumUsage.FillParent)];
            });
            PrintUtil.ifChild(this.nodeChildDict[XML_ORIGIN], (n) => {
                PrintUtil.forEachChildNode(n, (n2) => {
                    switch (n2.nodeName) {
                        case 'X':
                            this._x = PrintUtil.singleChildFloat(n2);
                            break;
                        case 'Y':
                            this._y = PrintUtil.singleChildFloat(n2);
                            break;
                    }
                });
            });
        }
        else {
            this._x = x;
            this._y = y;
            this._widths = [new RichNum(width)];
            this._heights = [new RichNum(height)];
            this._column = col;
            this._row = row;
        }
    }
    get uom() {
        return this._uom;
    }
    get heights() {
        return this._heights;
    }
    get widths() {
        return this._widths;
    }
    get x() {
        return this._x;
    }
    get y() {
        return this._y;
    }
    get column() {
        return this._column;
    }
    get row() {
        return this._row;
    }
    singleHeightNum() {
        const rn = this.singleHeightRichNum();
        if (!rn.isNumber) {
            throw Error('Expecting number on height layout');
        }
        return rn.value;
    }
    singleHeightRichNum() {
        if (!this.heights) {
            throw Error('No height values on Layout');
        }
        else if (this.heights.length != 1) {
            throw Error('Expecting exactly 1 height, but found: ' + this.heights.length);
        }
        else {
            return this.heights[0];
        }
    }
    singleWidthNum() {
        const rn = this.singleWidthRichNum();
        if (!rn.isNumber) {
            throw Error('Expecting number on width layout');
        }
        return rn.value;
    }
    singleWidthRichNum() {
        if (!this.widths) {
            throw Error('No width values on Layout');
        }
        else if (this.widths.length != 1) {
            throw Error('Expecting exactly 1 width, but found: ' + this.widths.length);
        }
        else {
            return this.widths[0];
        }
    }
    sumOfHeights() {
        let answer = 0.0;
        if (!this.heights) {
            throw Error('No height values on Layout');
        }
        this.heights.map((rn) => {
            if (!rn) {
                throw Error('Unsupported ricn num usage on layout.heights');
            }
            if (!rn.isNumber) {
                throw Error('Expecting number on layout.heights');
            }
            answer += rn.value;
        });
        return answer;
    }
    sumOfWidths(parentSize) {
        let answer = 0.0;
        if (!this.widths) {
            throw Error('No width values on Layout');
        }
        this.widths.map((rn) => {
            if (!rn) {
                throw Error('Unsupported ricn num usage on layout.widths');
            }
            if (rn.isFillParent) {
                if (this.widths.length != 1) {
                    throw Error('More than one value being summed and FillParentWidth used: layout.widths');
                }
                answer = parentSize;
            }
            else if (rn.isPercentOfParent) {
                answer += parentSize * rn.valueOfPercent;
            }
            else if (rn.isNumber) {
                answer += rn.value;
            }
            else {
                throw Error('Unknown RichNum usage on layout.widths');
            }
        });
        return answer;
    }
}
export class Page extends Container {
    constructor(parentContainer, node) {
        super(parentContainer, node);
    }
    get gridChildren() {
        return this.children;
    }
    initPageWith(grid) {
        this.initContainerWith(null, [grid]);
    }
    calcAndAssignContainerWidth(parentContainer) {
        this.assignContainerWidth(parentContainer.containerWidth);
    }
}
export class Settings extends Spec {
    constructor(node) {
        super(node);
        PrintUtil.ifChild(this.nodeChildDict[XML_REFRESH_TIMER], (n) => {
            this._refreshTimer = PrintUtil.singleChildInt(n);
        });
    }
    get refreshTimer() {
        return this._refreshTimer;
    }
}
export class RichNum {
    constructor(_value, _usage = RichNumUsage.Absolute) {
        this._value = _value;
        this._usage = _usage;
    }
    get value() {
        if (!this.isNumber) {
            throw Error('RichNum is not a raw number.');
        }
        return this._value;
    }
    get valueOfPercent() {
        if (!this.isPercentOfParent) {
            throw Error('PercentOfParent requested for wrong usage.');
        }
        return this._value * 0.01;
    }
    get isNumber() {
        return this._usage == RichNumUsage.Absolute;
    }
    get isFillParent() {
        return this._usage == RichNumUsage.FillParent;
    }
    get isPercentOfParent() {
        return this._usage == RichNumUsage.PercentOfParent;
    }
    resolveWithFill(n) {
        let answer;
        if (this.isNumber) {
            answer = this._value;
        }
        else if (this.isFillParent) {
            answer = n;
        }
        else if (this.isPercentOfParent) {
            answer = this.valueOfPercent * n;
        }
        else {
            throw Error('Unknown RichNum usage on resolveWithFill');
        }
        return answer;
    }
}
RichNum.ZERO = new RichNum(0);
export class SignatureCapture extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        PrintUtil.ifChild(this.nodeChildDict[XML_CAPTURE_BOUNDS], (n) => {
            this._captureBounds = new CaptureBounds(n);
        });
        PrintUtil.ifChild(this.nodeChildDict[XML_LINE_COLOR], (n) => {
            this._lineColor = new Color(n);
        });
    }
    get captureBounds() {
        return this._captureBounds;
    }
    get lineColor() {
        return this._lineColor;
    }
}
export class TextAlignment {
    constructor(_usage) {
        this._usage = _usage;
    }
    get isCenter() {
        return this._usage == TextAlignmentUsage.Center;
    }
    get isLeft() {
        return this._usage == TextAlignmentUsage.Left;
    }
    get isRight() {
        return this._usage == TextAlignmentUsage.Right;
    }
}
export class TextArea extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        this._textAttributes = new TextAttributes();
        PrintUtil.ifChild(this.nodeChildDict[XML_ENTRY_SEQ], (n) => {
            this._entrySeq = PrintUtil.singleChildInt(n);
        });
        PrintUtil.importTextAttributes(this.nodeChildDict, this._textAttributes);
    }
    get entrySeq() {
        return this._entrySeq;
    }
}
export class TextField extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        this._textAttributes = new TextAttributes();
        PrintUtil.ifChild(this.nodeChildDict[XML_ENTRY_SEQ], (n) => {
            this._entrySeq = PrintUtil.singleChildInt(n);
        });
        PrintUtil.importTextAttributes(this.nodeChildDict, this._textAttributes);
    }
    get entrySeq() {
        return this._entrySeq;
    }
}
export class TimePicker extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        this._textAttributes = new TextAttributes();
        PrintUtil.ifChild(this.nodeChildDict[XML_ENTRY_SEQ], (n) => {
            this._entrySeq = PrintUtil.singleChildInt(n);
        });
        PrintUtil.importTextAttributes(this.nodeChildDict, this._textAttributes);
    }
    get entrySeq() {
        return this._entrySeq;
    }
}
export class ValuePicker extends Component {
    constructor(parentContainer, node) {
        super(parentContainer, node);
        this._textAttributes = new TextAttributes();
        PrintUtil.ifChild(this.nodeChildDict[XML_ENTRY_SEQ], (n) => {
            this._entrySeq = PrintUtil.singleChildInt(n);
        });
        PrintUtil.importTextAttributes(this.nodeChildDict, this._textAttributes);
    }
    get entrySeq() {
        return this._entrySeq;
    }
}
// export class RichNum {
//     constructor(node:Node, public value?:number, public usage:RichNumUsage=RichNumUsage.Absolute) {
//         if (node) {
//
//         } else {
//             // Values held by constructor line
//         }
//     }
//
// }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class ComponentFactory {
    static fromNode(node, parentContainer) {
        let answer = null;
        switch (node.nodeName) {
            case XML_BUTTON:
                answer = new Button(parentContainer, node);
                break;
            case XML_CHECKBOX:
                answer = new Checkbox(parentContainer, node);
                break;
            case XML_DATE_PICKER:
                answer = new DatePicker(parentContainer, node);
                break;
            case XML_IMAGE:
                answer = new Image(parentContainer, node);
                break;
            case XML_LABEL:
                answer = new Label(parentContainer, node);
                break;
            case XML_SIGNATURE_CAPTURE:
                answer = new SignatureCapture(parentContainer, node);
                break;
            case XML_TEXT_AREA:
                answer = new TextArea(parentContainer, node);
                break;
            case XML_TEXT_FIELD:
                answer = new TextField(parentContainer, node);
                break;
            case XML_TIME_PICKER:
                answer = new TimePicker(parentContainer, node);
                break;
            case XML_VALUE_PICKER:
                answer = new ValuePicker(parentContainer, node);
                break;
            case XML_CELL:
                answer = new PrintCell(parentContainer, node);
                break;
            case XML_FORM:
                answer = new PrintForm(node);
                break;
            case XML_GRID:
                answer = new Grid(parentContainer, node);
                break;
            case XML_PAGE:
                answer = new Page(parentContainer, node);
                break;
        }
        return answer;
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class PrintUtil {
    static arrayOfRichNums(node, name) {
        const answer = [];
        PrintUtil.forEachChildNode(node, (n) => {
            if (n.nodeName == name) {
                answer.push(PrintUtil.singleChildRichNum(n));
            }
        });
        return answer;
    }
    static enumValue(node, e) {
        const answer = null;
        const sv = PrintUtil.singleChildText(node);
        let nv;
        if (sv) {
            nv = e[sv];
        }
        return nv;
    }
    static forEachChildNode(node, f) {
        for (let i = 0; i < node.childNodes.length; i++) {
            f(node.childNodes[i]);
        }
    }
    static ifChild(node, f) {
        if (node) {
            f(node);
        }
    }
    static importTextAttributes(nodeChildDict, textAttributes) {
        PrintUtil.ifChild(nodeChildDict[XML_BOLD], (n) => {
            textAttributes.bold = PrintUtil.singleChildBoolean(n);
        });
        PrintUtil.ifChild(nodeChildDict[XML_ITALIC], (n) => {
            textAttributes.italic = PrintUtil.singleChildBoolean(n);
        });
        PrintUtil.ifChild(nodeChildDict[XML_UNDERLINE], (n) => {
            textAttributes.underline = PrintUtil.singleChildBoolean(n);
        });
        PrintUtil.ifChild(nodeChildDict[XML_TEXT_ALIGNMENT], (n) => {
            textAttributes.textAlignment = new TextAlignment(PrintUtil.singleChildTextAlignmentUsage(n));
        });
        PrintUtil.ifChild(nodeChildDict[XML_TEXT_COLOR], (n) => {
            textAttributes.textColor = new Color(n);
        });
        PrintUtil.ifChild(nodeChildDict[XML_NUMBER_OF_LINES], (n) => {
            textAttributes.numberOfLines = PrintUtil.singleChildInt(n);
        });
    }
    static singleChildBoolean(node) {
        const text = PrintUtil.singleChildText(node);
        if (text) {
            return text.toLocaleLowerCase() == 'true';
        }
        else {
            return false;
        }
    }
    static singleChildInt(node) {
        let answer;
        if (node.childNodes.length != 1) {
            Log.error('XML error with ' + node.nodeName + '.  Expected exactly one child node.');
        }
        else if (node.childNodes[0].nodeName != '#text') {
            Log.error('XML error with ' + node.nodeName + '.  Expected numeric node.');
        }
        else {
            answer = parseInt(node.childNodes[0].textContent);
        }
        return answer;
    }
    static singleChildFloat(node) {
        let answer;
        if (node.childNodes.length != 1) {
            Log.error('XML error with ' + node.nodeName + '.  Expected exactly one child node.');
        }
        else if (node.childNodes[0].nodeName != '#text') {
            Log.error('XML error with ' + node.nodeName + '.  Expected numeric node.');
        }
        else {
            answer = parseFloat(node.childNodes[0].textContent);
        }
        return answer;
    }
    static singleChildRichNum(node) {
        // Either there is a FillParent entry with surrounding white space, or a single text entry
        let answer;
        for (let i = 0; i < node.childNodes.length; i++) {
            if (node.childNodes[i].nodeName == RichNumUsage[RichNumUsage.PercentOfParent].toString()) {
                const v = this.singleChildFloat(node.childNodes[i]);
                answer = new RichNum(v, RichNumUsage.PercentOfParent);
                break;
            }
            else if (node.childNodes[i].nodeName == RichNumUsage[RichNumUsage.FillParent].toString()) {
                answer = new RichNum(NaN, RichNumUsage.FillParent);
                break;
            }
            else if (node.childNodes[i].nodeName == '#text') {
                const v = parseFloat(node.childNodes[i].textContent.trim());
                if (!isNaN(v)) {
                    answer = new RichNum(v);
                }
                // Don't break, keep looking for FillParent
            }
        }
        return answer;
    }
    static singleChildText(node) {
        let text = null;
        if (node.childNodes.length != 1) {
            text = 'ExpectedExactlyOneNode';
            Log.error('XML error with ' + node.nodeName + '.  Expected exactly one child node.');
        }
        else if (node.childNodes[0].nodeName != '#text') {
            text = 'ExpectedNodeText';
            Log.error('XML error with ' + node.nodeName + '.  Expected text node.');
        }
        else {
            text = node.childNodes[0].textContent;
        }
        return text;
    }
    static singleChildTextAlignmentUsage(node) {
        // Either there is a FillParent entry with surrounding white space, or a single text entry
        let answer = TextAlignmentUsage.Left;
        if (node.childNodes.length != 1) {
            Log.error('XML error with ' + node.nodeName + '.  Expected exactly one child node.');
        }
        else if (node.childNodes[0].nodeName != '#text') {
            Log.error('XML error with ' + node.nodeName + '.  Expected text node.');
        }
        else {
            const a = node.childNodes[0].textContent;
            if (a == TextAlignmentUsage[TextAlignmentUsage.Center].toString()) {
                answer = TextAlignmentUsage.Center;
            }
            else if (a == TextAlignmentUsage[TextAlignmentUsage.Left].toString()) {
                answer = TextAlignmentUsage.Left;
            }
            else if (a == TextAlignmentUsage[TextAlignmentUsage.Right].toString()) {
                answer = TextAlignmentUsage.Right;
            }
            else {
                Log.error('XML error with ' + node.nodeName + '.  Unknown TextAlignment: ' + a);
            }
        }
        return answer;
    }
}
/**
 * *********************************
 */
/**
 * *********************************
 */
