import definition = require("ui/repeater");
import proxy = require("ui/core/proxy");
import dependencyObservable = require("ui/core/dependency-observable");
import viewModule = require("ui/core/view");
import observable = require("data/observable");
import layoutBaseModule = require("ui/layouts/layout-base");
import utils = require("utils/utils");
import trace = require("trace");
import * as platform from "platform";
import * as stackLayout from "ui/layouts/stack-layout";
import * as types from "utils/types";
import * as builder from "ui/builder";
import * as labelModule from "ui/label";
import * as observableArray from "data/observable-array";
import * as weakEvents from "ui/core/weak-event-listener";

var label: typeof labelModule;
function ensureLabel() {
    if (!label) {
        label = require("ui/label");
    }
}

var ITEMS = "items";
var ITEMTEMPLATE = "itemTemplate";
var LAYOUT = "layout";
var REPEATER = "Repeater";

export module knownTemplates {
    export var itemTemplate = "itemTemplate";
}

function onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
    var repeater = <Repeater>data.object;
    repeater._onItemsPropertyChanged(data);
}

function onItemTemplatePropertyChanged(data: dependencyObservable.PropertyChangeData) {
    var repeater = <Repeater>data.object;
    repeater._onItemTemplatePropertyChanged(data);
}

function onItemsLayoutPropertyPropertyChanged(data: dependencyObservable.PropertyChangeData) {
    var repeater = <Repeater>data.object;
    repeater._onItemsLayoutPropertyPropertyChanged(data);

}

export class Repeater extends viewModule.CustomLayoutView implements definition.Repeater {
    private _ios: UIView;
    private _isDirty = false;

    constructor() {
        super();

        if (platform.device.os === platform.platformNames.ios) {
            this._ios = UIView.new();
        }

        this.itemsLayout = new stackLayout.StackLayout();
    }

    public static itemsProperty = new dependencyObservable.Property(
        ITEMS,
        REPEATER,
        new proxy.PropertyMetadata(
            undefined,
            dependencyObservable.PropertyMetadataSettings.AffectsLayout,
            onItemsPropertyChanged
            )
        );

    public static itemTemplateProperty = new dependencyObservable.Property(
        ITEMTEMPLATE,
        REPEATER,
        new proxy.PropertyMetadata(
            undefined,
            dependencyObservable.PropertyMetadataSettings.AffectsLayout,
            onItemTemplatePropertyChanged
            )
        );

    public static itemsLayoutProperty = new dependencyObservable.Property(
        LAYOUT,
        REPEATER,
        new proxy.PropertyMetadata(
            undefined,
            dependencyObservable.PropertyMetadataSettings.AffectsLayout,
            onItemsLayoutPropertyPropertyChanged
            )
        );

    get items(): any {
        return this._getValue(Repeater.itemsProperty);
    }
    set items(value: any) {
        this._setValue(Repeater.itemsProperty, value);
    }

    get itemTemplate(): string | viewModule.Template {
        return this._getValue(Repeater.itemTemplateProperty);
    }
    set itemTemplate(value: string | viewModule.Template) {
        this._setValue(Repeater.itemTemplateProperty, value);
    }

    get itemsLayout(): layoutBaseModule.LayoutBase {
        return this._getValue(Repeater.itemsLayoutProperty);
    }
    set itemsLayout(value: layoutBaseModule.LayoutBase) {
        this._setValue(Repeater.itemsLayoutProperty, value);
    }

    public onLoaded() {
        if (trace.enabled) {
            trace.write("Repeater.onLoaded()", "Repeater");
        }
        if (this._isDirty) {
            this.refresh();
        }

        super.onLoaded();
    }

    private _requestRefresh() {
        if (trace.enabled) {
            trace.write(`Repeater._requestRefresh()`, "Repeater");
        }
        this._isDirty = true;
        if (this.isLoaded) {
            this.refresh();
        }
    }

    public refresh() {
        if (trace.enabled) {
            trace.write("Repeater.refresh()", "Repeater");
        }
        if (this.itemsLayout) {
            this.itemsLayout.removeChildren();
        }

        if (types.isNullOrUndefined(this.items) || !types.isNumber(this.items.length)) {
            return;
        }

        var length = this.items.length;
        for (let i = 0; i < length; i++) {
            let viewToAdd = !types.isNullOrUndefined(this.itemTemplate) ? builder.parse(this.itemTemplate, this) : this._getDefaultItemContent(i);
            var dataItem = this._getDataItem(i);
            //trace.write(`viewToAdd.bindingContext = ${dataItem};`, "Repeater");
            viewToAdd.bindingContext = dataItem;
            //trace.write(`Repeater.itemsLayout.addChild(${viewToAdd})`, "Repeater");
            this.itemsLayout.addChild(viewToAdd);
        }

        this._isDirty = false;
    }

    public _onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
        if (trace.enabled) {
            trace.write(`Repeater._onItemsPropertyChanged(${data.oldValue} => ${data.newValue})`, "Repeater");
        }
        if (data.oldValue instanceof observableArray.ObservableArray) {
            weakEvents.removeWeakEventListener(data.oldValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this);
        }

        if (data.newValue instanceof observableArray.ObservableArray) {
            weakEvents.addWeakEventListener(data.newValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this);
        }

        this._requestRefresh();
    }

    public _onItemTemplatePropertyChanged(data: dependencyObservable.PropertyChangeData) {
        if (trace.enabled) {
            trace.write(`Repeater._onItemTemplatePropertyChanged(${data.oldValue} => ${data.newValue})`, "Repeater");
        }
        this._requestRefresh();
    }

    public _onItemsLayoutPropertyPropertyChanged(data: dependencyObservable.PropertyChangeData) {
        if (trace.enabled) {
            trace.write(`Repeater._onItemsLayoutPropertyPropertyChanged(${data.oldValue} => ${data.newValue})`, "Repeater");
        }
        if (data.oldValue instanceof layoutBaseModule.LayoutBase) {
            this._removeView((<layoutBaseModule.LayoutBase>data.oldValue));
        }

        if (data.newValue instanceof layoutBaseModule.LayoutBase) {
            this._addView((<layoutBaseModule.LayoutBase>data.newValue));
        }

        this._requestRefresh();
    }

    private _onItemsChanged(data: observable.EventData) {
        if (trace.enabled) {
            trace.write(`Repeater._onItemsChanged(${data})`, "Repeater");
        }
        this._requestRefresh();
    }

    public _getDefaultItemContent(index: number): viewModule.View {
        ensureLabel();

        var lbl = new label.Label();
        lbl.bind({
            targetProperty: "text",
            sourceProperty: "$value"
        });
        return lbl;
    }

    private _getDataItem(index: number): any {
        return this.items.getItem ? this.items.getItem(index) : this.items[index];
    }

    get ios(): UIView {
        return this._ios;
    }

    get _childrenCount(): number {
        var count = 0;

        if (this.itemsLayout) {
            count++;
        }

        return count;
    }

    public _eachChildView(callback: (child: viewModule.View) => boolean) {
        if (this.itemsLayout) {
            callback(this.itemsLayout);
        }
    }

    public onLayout(left: number, top: number, right: number, bottom: number): void {
        viewModule.View.layoutChild(this, this.itemsLayout, 0, 0, right - left, bottom - top);
    }

    public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
        var result = viewModule.View.measureChild(this, this.itemsLayout, widthMeasureSpec, heightMeasureSpec);

        var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
        var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);

        var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
        var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);

        var widthAndState = viewModule.View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0);
        var heightAndState = viewModule.View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);

        this.setMeasuredDimension(widthAndState, heightAndState);
    }

}