/*
THIS INFRAGISTICS ULTIMATE SOFTWARE LICENSE  AGREEMENT ("AGREEMENT") LOCATED HERE:
https://www.infragistics.com/legal/license/igultimate-la
https://www.infragistics.com/legal/license/igultimate-eula
GOVERNS THE LICENSING, INSTALLATION AND USE OF INFRAGISTICS SOFTWARE. BY DOWNLOADING AND/OR INSTALLING AND USING INFRAGISTICS SOFTWARE:  you are indicating that you have read and understand this Agreement, and agree to be legally bound by it on behalf of the yourself and your company.
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GridMode_$type } from './GridMode';
import { IgrSeriesViewer } from './igr-series-viewer';
import { ReactRenderer, ReactWrapper } from "igniteui-react-core/ES2015/react-renderer";
import { TypeRegistrar } from "igniteui-react-core/ES2015/type";
import { XamDataChart } from './XamDataChart';
import { CollectionAdapter, brushToString, stringToBrush, fromBrushCollection, toBrushCollection, ensureBool, ensureEnum } from "igniteui-react-core/ES2015/componentUtil";
import { Axis } from './Axis';
import { DataChartStylingDefaults } from './DataChartStylingDefaults';
import { DataContext } from "igniteui-react-core/ES2015/igr-data-context";
import { SeriesCollection } from './igr-series-collection';
import { NotifyCollectionChangedAction } from "igniteui-react-core/ES2015/NotifyCollectionChangedAction";
import { AxisCollection } from './igr-axis-collection';
/**
 * Represents a chart area containing axes, series, an optional legend and other hosted content.
*/
export class IgrDataChart extends IgrSeriesViewer {
    constructor(props) {
        super(props);
        this.contentAxes = [];
        this.contentSeries = [];
        this._contentChildren = null;
        this._contentChildrenActual = null;
        this._contentChildrenMap = null;
        this._contentChildrenUnresolved = 0;
        /**
         * The series actually present in the chart. Do not directly modify this array.
         * This array's contents can be modified by causing React to reproject the child content.
         * Or adding and removing series from the manual series collection on the series property.
         */
        this.actualSeries = [];
        /**
         * The axes actually present in the chart. Do not directly modify this array.
         * This array's contents can be modified by causing React to reproject the child content.
         * Or adding and removing axes from the manual axes collection on the axes property.
         */
        this.actualAxes = [];
        this._series = null;
        this._axes = null;
        this._initialized = false;
        this._axesAdapter = null;
        this._seriesAdapter = null;
        this._defaultTooltips = null;
        this._uniqueTooltipId = 0;
        this._tooltipRef = this._tooltipRef.bind(this);
        this.getSeriesRef = this.getSeriesRef.bind(this);
        this._activeTooltipElements = new Map();
        this._activeTooltips = new Map();
        this._currentTooltips = [];
        if (document) {
            this._container = document.createElement("div");
            this._container.style.display = "block";
            this._container.style.width = "100%";
            this._container.style.height = "100%";
        }
        var root;
        root = this._container;
        var ren = new ReactRenderer(root, document, true, DataChartStylingDefaults);
        this._wrapper = ren;
        var chart = this.i;
        this._chart = chart;
        chart.provideContainer(ren);
        ren.addSizeWatcher(() => {
            this._chart.notifyContainerResized();
        });
        this._initialized = true;
    }
    set height(value) {
        this._height = value;
        if (this._elRef) {
            this._elRef.style.height = value;
            this._chart.notifyContainerResized();
        }
    }
    get height() {
        return this._height;
    }
    _updateContentChildren() {
        this.contentAxes = [];
        this.contentSeries = [];
        for (let i = 0; i < this._contentChildrenActual.length; i++) {
            if ((Axis.$type).isAssignableFrom(this._contentChildrenActual[i].i.$type)) {
                this.contentAxes.push(this._contentChildrenActual[i]);
            }
            else {
                this.contentSeries.push(this._contentChildrenActual[i]);
            }
        }
        if (this._axesAdapter !== null) {
            this._axesAdapter.notifyContentChanged();
        }
        if (this._seriesAdapter !== null) {
            this._seriesAdapter.notifyContentChanged();
        }
    }
    getSeriesRef(child) {
        console.log(child);
        if (child === null) {
            return;
        }
        let index = this._contentChildrenMap.get(child.name);
        if (this._contentChildrenActual[index] === null) {
            this._contentChildrenActual[index] = child;
            this._contentChildrenUnresolved--;
            if (this._contentChildrenUnresolved <= 0) {
                this._updateContentChildren();
            }
        }
    }
    _childrenDiffer(children) {
        if (this._contentChildren === null) {
            return true;
        }
        if (React.Children.count(children) != this._contentChildren.length) {
            return true;
        }
        let arr = React.Children.toArray(children);
        for (let i = 0; i < arr.length; i++) {
            if (arr[i].props.name !== this._contentChildren[i].props.name) {
                return true;
            }
        }
        return false;
    }
    render() {
        // if (!this._childrenDiffer(this.props.children)) {
        // 	let div = React.createElement("div", {
        // 		ref: (ref) => {
        // 			this._elRef = ref;
        // 		},
        // 		children: this.props.children
        // 	});
        // 	return div;
        // } else {
        let children = null;
        let c = React.Children.map(this.props.children, (ch) => {
            let key = ch.props.key || ch.props.name;
            if (key === undefined) {
                throw new Error("each child of chart must have a unique key or name property");
            }
            return React.cloneElement(ch, {
                key: key,
                ref: this.getSeriesRef
            });
        });
        children = c;
        this._contentChildren = c;
        this._contentChildrenActual = [];
        this._contentChildrenMap = new Map();
        this._contentChildren.map((ch, i) => {
            this._contentChildrenMap.set(ch.props.name, i);
            this._contentChildrenActual[i] = null;
        });
        this._contentChildrenUnresolved = this._contentChildrenMap.size;
        if (this._currentTooltips && this._currentTooltips.length > 0) {
            children = React.Children.toArray(this.props.children);
            for (let i = 0; i < this._currentTooltips.length; i++) {
                let t = this._currentTooltips[i];
                if (this._activeTooltipElements.has(t)) {
                    children.push(this._activeTooltipElements.get(t));
                }
                else {
                    if (!TypeRegistrar.isRegistered("IgrTooltipContainer")) {
                        continue;
                    }
                    let Tooltip = TypeRegistrar.get("IgrTooltipContainer");
                    let tEle = React.createElement(Tooltip, {
                        ref: this._tooltipRef,
                        key: this._currentTooltips[i].key,
                        owner: this._currentTooltips[i]
                    });
                    let portal = ReactDOM.createPortal(tEle, t, this._currentTooltips[i].key);
                    this._activeTooltipElements.set(t, portal);
                    children.push(portal);
                }
            }
        }
        let div = React.createElement("div", {
            ref: (ref) => {
                this._elRef = ref;
            },
            children: children
        });
        return div;
        //}
    }
    _tooltipRef(t) {
        console.log(t);
        if (t === null) {
            return;
        }
        if (t.currentOwner &&
            t.currentOwner.tooltipTemplate) {
            t.template = t.currentOwner.tooltipTemplate;
        }
        this._activeTooltips.set(t.currentOwner, t);
    }
    componentDidMount() {
        super.componentDidMount();
        this._elRef.style.width = this._width ? this._width : "";
        this._elRef.style.height = this._height ? this._height : "";
        this._elRef.appendChild(this._container);
        this._chart.notifyContainerResized();
        this.initializeContent();
    }
    set width(value) {
        this._width = value;
        if (this._elRef) {
            this._elRef.style.height = value;
            this._chart.notifyContainerResized();
        }
    }
    get width() {
        return this._width;
    }
    /**
     * A collection or manually added series for the chart.
    */
    get series() {
        if (this._series === null) {
            let coll = new SeriesCollection();
            let inner = coll._innerColl;
            inner.addListener((sender, e) => {
                switch (e.action) {
                    case NotifyCollectionChangedAction.Add:
                        this._seriesAdapter.addManualItem(e.newItems.item(0));
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        this._seriesAdapter.removeManualItemAt(e.oldStartingIndex);
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        this._seriesAdapter.removeManualItemAt(e.oldStartingIndex);
                        this._seriesAdapter.insertManualItem(e.newStartingIndex, e.newItems.item(0));
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        this._seriesAdapter.clearManualItems();
                        break;
                }
            });
            this._series = coll;
        }
        return this._series;
    }
    /**
     * A collection or manually added axes for the chart.
    */
    get axes() {
        if (this._axes === null) {
            let coll = new AxisCollection();
            let inner = coll._innerColl;
            inner.addListener((sender, e) => {
                switch (e.action) {
                    case NotifyCollectionChangedAction.Add:
                        this._axesAdapter.addManualItem(e.newItems.item(0));
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        this._axesAdapter.removeManualItemAt(e.oldStartingIndex);
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        this._axesAdapter.removeManualItemAt(e.oldStartingIndex);
                        this._axesAdapter.insertManualItem(e.newStartingIndex, e.newItems.item(0));
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        this._axesAdapter.clearManualItems();
                        break;
                }
            });
            this._axes = coll;
        }
        return this._axes;
    }
    componentWillUnmount() {
        this._chart.destroy();
        this._wrapper.destroy();
    }
    createImplementation() {
        return new XamDataChart();
    }
    set dataSource(value) {
        this._dataSource = value;
        this.bindData();
    }
    get dataSource() {
        return this._dataSource;
    }
    bindData() {
        //console.log("binding data: " + this._dataSource);
        if (this.actualAxes && this.actualAxes.length > 0) {
            var currAxes = this.actualAxes;
            for (var i = 0; i < currAxes.length; i++) {
                currAxes[i].provideData(this._dataSource);
            }
        }
        if (this.actualSeries && this.actualSeries.length > 0) {
            var currSeries = this.actualSeries;
            for (var i = 0; i < currSeries.length; i++) {
                currSeries[i].provideData(this._dataSource);
            }
        }
    }
    initializeContent() {
        if (TypeRegistrar.isRegistered("IgrDataChartDefaultTooltips")) {
            let d = TypeRegistrar.create("IgrDataChartDefaultTooltips");
            this._defaultTooltips = d;
            this._onDefaultTooltipsReady(d);
            //this._container.appendChild(cr.location.nativeElement);
        }
        this._axesAdapter = new CollectionAdapter(this.contentAxes, this.i.axes, this.actualAxes, (c) => c.i, (i) => { i._provideRenderer(this._wrapper); i.provideData(this._dataSource); }, (i) => { i._provideRenderer(null); i.provideData(null); });
        this._seriesAdapter = new CollectionAdapter(this.contentSeries, this.i.series, this.actualSeries, (c) => c.i, (i) => {
            i.owner = this;
            i._provideRenderer(this._dataSource);
            i.provideData(this._dataSource);
            i.bindAxes(this.actualAxes);
            this._ensureDefaultTooltip(i);
            this._ensureTooltipCreated(i);
        }, (i) => { i._provideRenderer(null); i.provideData(null); });
        this.i.notifyContainerResized();
    }
    _ensureTooltipCreated(series) {
        series._ensureTooltipCreated(() => this.createTooltip(), (ele) => {
            let wrapper = new ReactWrapper(ele, this._wrapper);
            wrapper.updateToolTip = ele.updateToolTip;
            wrapper.hideToolTip = ele.hideToolTip;
            return wrapper;
        });
    }
    _ensureDefaultTooltip(series) {
        if (this._defaultTooltips == null) {
            return;
        }
        this._defaultTooltips["ensureDefaultTooltip"](series);
    }
    _onDefaultTooltipsReady(cr) {
        if (this.actualSeries && this.actualSeries.length > 0) {
            var currSeries = this.actualSeries;
            for (var i = 0; i < currSeries.length; i++) {
                if (currSeries[i].showDefaultTooltip) {
                    this._ensureDefaultTooltip(currSeries[i]);
                }
            }
        }
    }
    createTooltip() {
        // if (!TypeRegistrar.isRegistered("IgrTooltipContainer")) {
        // 	return null;
        // }
        // let Tooltip = TypeRegistrar.get("IgrTooltipContainer");
        let wrapper = this._wrapper.createElement("div");
        let ele = wrapper.getNativeElement();
        ele.key = "__tooltip_" + this._uniqueTooltipId;
        this._uniqueTooltipId++;
        this._currentTooltips = this._currentTooltips.slice(0);
        this._currentTooltips.push(ele);
        //let element = React.createElement(Tooltip,  );
        //let portal = ReactDOM.createPortal(element, ele);
        let self = this;
        ele.updateToolTip = function (c) {
            if (c.externalObject) {
                c = c.externalObject;
            }
            else {
                let ext = new DataContext();
                ext._implementation = c;
                c = ext;
            }
            if (ele.parentElement != self._container) {
                if (ele.parentElement != null) {
                    ele.parentElement.removeChild(ele);
                }
                self._container.appendChild(ele);
            }
            if (self._activeTooltips.has(ele)) {
                let t = self._activeTooltips.get(ele);
                t.dataContext = c;
            }
            ele.style.display = "block";
            return true;
        };
        ele.hideToolTip = function () {
            ele.style.display = "none";
        };
        ele.style.display = "none";
        this._updateTooltipState();
        return ele;
    }
    _updateTooltipState() {
        if (this._initialized) {
            this.setState({ tooltips: this._currentTooltips });
        }
    }
    get i() {
        return this._implementation;
    }
    /**
 * Gets or sets whether to use a square aspect ratio for the chart. This is locked to true for polar and radial charts.
*/
    get isSquare() {
        return this.i.nm;
    }
    set isSquare(v) {
        this.i.nm = ensureBool(v);
    }
    /**
     * A number between 0 and 1 determining the scale of the horizontal zoom.
     * This property is effectively a shortcut to the Width of the WindowRect property.
    */
    get windowScaleHorizontal() {
        return this.i.n5;
    }
    set windowScaleHorizontal(v) {
        this.i.n5 = +v;
    }
    /**
     * A number between 0 and 1 determining the scale of the vertical zoom.
     * This property is effectively a shortcut to the Height of the WindowRect property.
    */
    get windowScaleVertical() {
        return this.i.n6;
    }
    set windowScaleVertical(v) {
        this.i.n6 = +v;
    }
    /**
     * A number between 0 and 1 determining the scale of the horizontal zoom.
     * This property is effectively a shortcut to the Width of the ActualWindowRect property.
    */
    get actualWindowScaleHorizontal() {
        return this.i.nx;
    }
    /**
     * A number between 0 and 1 determining the scale of the vertical zoom.
     * This property is effectively a shortcut to the Height of the ActualWindowRect property.
    */
    get actualWindowScaleVertical() {
        return this.i.ny;
    }
    /**
     * Gets or sets horizontal zoomability of the current control
    */
    get isHorizontalZoomEnabled() {
        return this.i.nl;
    }
    set isHorizontalZoomEnabled(v) {
        this.i.nl = ensureBool(v);
    }
    /**
     * Gets or sets vertical zoomability of the current control
    */
    get isVerticalZoomEnabled() {
        return this.i.nn;
    }
    set isVerticalZoomEnabled(v) {
        this.i.nn = ensureBool(v);
    }
    /**
     * Gets or sets how grid lines are rendered in relation to series.
    */
    get gridMode() {
        return this.i.na;
    }
    set gridMode(v) {
        this.i.na = ensureEnum(GridMode_$type, v);
    }
    /**
     * Gets or sets a value indicating whether grid and tick lines are aligned to device pixels.
    */
    get alignsGridLinesToPixels() {
        return this.i.nh;
    }
    set alignsGridLinesToPixels(v) {
        this.i.nh = ensureBool(v);
    }
    /**
     * Gets or sets the palette of brushes to use for coloring the chart series.
    */
    get brushes() {
        return fromBrushCollection(this.i.m3);
    }
    set brushes(v) {
        this.i.m3 = toBrushCollection(v);
    }
    /**
     * Gets or sets the MarkerBrushes property.
     * The brushes property defines the palette from which automatically assigned series brushes are selected.
    */
    get markerBrushes() {
        return fromBrushCollection(this.i.m4);
    }
    set markerBrushes(v) {
        this.i.m4 = toBrushCollection(v);
    }
    /**
     * Gets or sets the palette of brushes to use for outlines on the chart series.
    */
    get outlines() {
        return fromBrushCollection(this.i.m6);
    }
    set outlines(v) {
        this.i.m6 = toBrushCollection(v);
    }
    /**
     * Gets or sets the MarkerOutlines property.
     * The brushes property defines the palette from which automatically assigned series brushes are selected.
    */
    get markerOutlines() {
        return fromBrushCollection(this.i.m5);
    }
    set markerOutlines(v) {
        this.i.m5 = toBrushCollection(v);
    }
    /**
     * Gets or sets the DefaultAxisStroke property.
     * The DefaultAxisStroke property defines the brush which is used by the axes when no Axis.Stroke is set.
    */
    get defaultAxisStroke() {
        return brushToString(this.i.pi);
    }
    set defaultAxisStroke(v) {
        this.i.pi = stringToBrush(v);
    }
    /**
     * Gets or sets the DefaultAxisMajorStroke property.
     * The DefaultAxisMajorStroke property defines the brush which is used by the axes when no Axis.MajorStroke is set.
    */
    get defaultAxisMajorStroke() {
        return brushToString(this.i.pg);
    }
    set defaultAxisMajorStroke(v) {
        this.i.pg = stringToBrush(v);
    }
    /**
     * Gets or sets the DefaultAxisMinorStroke property.
     * The DefaultAxisMinorStroke property defines the brush which is used by the axes when no Axis.MinorStroke is set.
    */
    get defaultAxisMinorStroke() {
        return brushToString(this.i.ph);
    }
    set defaultAxisMinorStroke(v) {
        this.i.ph = stringToBrush(v);
    }
    /**
     * Margin applied to the left of the plot area.
    */
    get plotAreaMarginLeft() {
        return this.i.n2;
    }
    set plotAreaMarginLeft(v) {
        this.i.n2 = +v;
    }
    /**
     * Margin applied above the plot area.
    */
    get plotAreaMarginTop() {
        return this.i.n4;
    }
    set plotAreaMarginTop(v) {
        this.i.n4 = +v;
    }
    /**
     * Margin applied to the right of the plot area.
    */
    get plotAreaMarginRight() {
        return this.i.n3;
    }
    set plotAreaMarginRight(v) {
        this.i.n3 = +v;
    }
    /**
     * Margin applied below the plot area.
    */
    get plotAreaMarginBottom() {
        return this.i.n1;
    }
    set plotAreaMarginBottom(v) {
        this.i.n1 = +v;
    }
    /**
 * Notifies the chart that the CSS styles in effect have been updated.

*/
    styleUpdated() {
        this.i.oy();
    }
    /**
     * Export serialized visual data.
    
    */
    exportSerializedVisualData() {
        let iv = this.i.og();
        return (iv);
    }
}
