import utils = require("utils/utils");
import common = require("./wrap-layout-common");
import {View} from "ui/core/view";
import {Orientation} from "ui/enums";
import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style";

global.moduleMerge(common, exports);

export class WrapLayout extends common.WrapLayout {
    private _lengths: Array<number> = new Array<number>();

    private static getChildMeasureSpec(parentMode: number, parentLength: number, itemLength): number {
        if (itemLength > 0) {
            return utils.layout.makeMeasureSpec(itemLength, utils.layout.EXACTLY);
        }
        else if (parentMode === utils.layout.UNSPECIFIED) {
            return utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED);
        }
        else {
            return utils.layout.makeMeasureSpec(parentLength, utils.layout.AT_MOST);
        }
    }

    public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
        WrapLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        var measureWidth = 0;
        var measureHeight = 0;

        var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
        var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);

        var density = utils.layout.getDisplayDensity();

        var horizontalPadding = (this.paddingLeft + this.paddingRight) * density;
        var verticalPadding = (this.paddingTop + this.paddingBottom) * density;

        var availableWidth = widthMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : utils.layout.getMeasureSpecSize(widthMeasureSpec) - horizontalPadding;
        var availableHeight = heightMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : utils.layout.getMeasureSpecSize(heightMeasureSpec) - verticalPadding;

        var childWidthMeasureSpec: number = WrapLayout.getChildMeasureSpec(widthMode, availableWidth, this.itemWidth * density);
        var childHeightMeasureSpec: number = WrapLayout.getChildMeasureSpec(heightMode, availableHeight, this.itemHeight * density);

        var remainingWidth = availableWidth;
        var remainingHeight = availableHeight;

        this._lengths.length = 0;
        var rowOrColumn = 0;
        var maxLength = 0;

        var isVertical = this.orientation === Orientation.vertical;

        let useItemWidth: boolean = this.itemWidth > 0;
        let useItemHeight: boolean = this.itemHeight > 0;
        let itemWidth = this.itemWidth;
        let itemHeight = this.itemHeight;

        this.eachLayoutChild((child, last) => {
            var desiredSize = View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec);
            let childMeasuredWidth = useItemWidth ? itemWidth : desiredSize.measuredWidth;
            let childMeasuredHeight = useItemHeight ? itemHeight : desiredSize.measuredHeight;
            let isFirst = this._lengths.length <= rowOrColumn;

            if (isVertical) {
                if (childMeasuredHeight > remainingHeight) {
                    rowOrColumn++;
                    maxLength = Math.max(maxLength, measureHeight);
                    measureHeight = childMeasuredHeight;
                    remainingHeight = availableHeight - childMeasuredHeight;
                    this._lengths[isFirst ? rowOrColumn - 1 : rowOrColumn] = childMeasuredWidth;
                }
                else {
                    remainingHeight -= childMeasuredHeight;
                    measureHeight += childMeasuredHeight;
                }
            }
            else {
                if (childMeasuredWidth > remainingWidth) {
                    rowOrColumn++;
                    maxLength = Math.max(maxLength, measureWidth);
                    measureWidth = childMeasuredWidth;
                    remainingWidth = availableWidth - childMeasuredWidth;
                    this._lengths[isFirst ? rowOrColumn - 1 : rowOrColumn] = childMeasuredHeight;
                }
                else {
                    remainingWidth -= childMeasuredWidth;
                    measureWidth += childMeasuredWidth;
                }
            }

            if (isFirst) {
                this._lengths[rowOrColumn] = isVertical ? childMeasuredWidth : childMeasuredHeight;
            }
            else {
                this._lengths[rowOrColumn] = Math.max(this._lengths[rowOrColumn], isVertical ? childMeasuredWidth : childMeasuredHeight);
            }
        });

        if (isVertical) {
            measureHeight = Math.max(maxLength, measureHeight);
            this._lengths.forEach((value, index, array) => {
                measureWidth += value;
            });
        }
        else {
            measureWidth = Math.max(maxLength, measureWidth);
            this._lengths.forEach((value, index, array) => {
                measureHeight += value;
            });
        }

        measureWidth += horizontalPadding;
        measureHeight += verticalPadding;

        measureWidth = Math.max(measureWidth, this.minWidth * density);
        measureHeight = Math.max(measureHeight, this.minHeight * density);

        var widthAndState = View.resolveSizeAndState(measureWidth, utils.layout.getMeasureSpecSize(widthMeasureSpec), widthMode, 0);
        var heightAndState = View.resolveSizeAndState(measureHeight, utils.layout.getMeasureSpecSize(heightMeasureSpec), heightMode, 0);

        this.setMeasuredDimension(widthAndState, heightAndState);
    }

    public onLayout(left: number, top: number, right: number, bottom: number): void {
        super.onLayout(left, top, right, bottom);

        var isVertical = this.orientation === Orientation.vertical;

        var density = utils.layout.getDisplayDensity();

        var childLeft = this.paddingLeft * density;
        var childTop = this.paddingTop * density;
        var childrenLength: number;
        if (isVertical) {
            childrenLength = bottom - top - (this.paddingBottom * density);
        }
        else {
            childrenLength = right - left - (this.paddingRight * density);
        }

        var rowOrColumn = 0;
        this.eachLayoutChild((child, last) => {
            // Add margins because layoutChild will sustract them.
            // * density converts them to device pixels.
            let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty);

            let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density;
            let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density;

            let length = this._lengths[rowOrColumn];
            if (isVertical) {
                childWidth = length;
                childHeight = this.itemHeight > 0 ? this.itemHeight * density : childHeight;
                let isFirst = childTop === this.paddingTop * density;
                if (childTop + childHeight > childrenLength) {
                    // Move to top.
                    childTop = this.paddingTop * density;

                    if (!isFirst) {
                    // Move to right with current column width.
                        childLeft += length;
                    }

                    // Move to next column.
                    rowOrColumn++;

                    // Take respective column width.
                    childWidth = this._lengths[isFirst ? rowOrColumn - 1 : rowOrColumn];
                }
            }
            else {
                childWidth = this.itemWidth > 0 ? this.itemWidth * density : childWidth;
                childHeight = length;
                let isFirst = childLeft === this.paddingLeft * density;
                if (childLeft + childWidth > childrenLength) {
                    // Move to left.
                    childLeft = this.paddingLeft * density;

                    if (!isFirst) {
                        // Move to bottom with current row height.
                        childTop += length;
                    }

                    // Move to next row.
                    rowOrColumn++;

                    // Take respective row height.
                    childHeight = this._lengths[isFirst ? rowOrColumn - 1 : rowOrColumn];
                }
            }

            View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childTop + childHeight);

            if (isVertical) {
                // Move next child Top position to bottom.
                childTop += childHeight;
            }
            else {
                // Move next child Left position to right.
                childLeft += childWidth;
            }
        });

        WrapLayout.restoreOriginalParams(this);
    }
}