import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { ReactRenderer, ReactWrapper } from "igniteui-react-core/ES2015/react-renderer";
import { TypeRegistrar } from "igniteui-react-core/ES2015/type";
import { DataChartStylingDefaults } from './DataChartStylingDefaults';
import { XamDoughnutChart } from './XamDoughnutChart';
import { ensureBool, fromPoint, CollectionAdapter, initializePropertiesFromCss, isValidProp, getModifiedProps } from "igniteui-react-core/ES2015/componentUtil";
import { Style } from "igniteui-react-core/ES2015/Style";
import { SliceClickEventArgs } from "igniteui-react-charts/ES2015/igr-slice-click-event-args";
import { HoleDimensionsChangedEventArgs } from "igniteui-react-charts/ES2015/igr-hole-dimensions-changed-event-args";
import { RingSeriesBase } from "igniteui-react-charts/ES2015/RingSeriesBase";
import { NotifyCollectionChangedAction } from "igniteui-react-core/ES2015/NotifyCollectionChangedAction";
import { RingSeriesCollection } from "igniteui-react-charts/ES2015/igr-ring-series-collection";
import { DataContext } from "igniteui-react-core/ES2015/igr-data-context";
import { delegateCombine, delegateRemove } from "igniteui-react-core/ES2015/type";
import { ContentChildrenManager } from "igniteui-react-core/ES2015/ContentChildrenManager";
/**
 * Represents concentric circles divided on arcs  depending on data.
*/
export class IgrDoughnutChart extends React.Component {
    constructor(props) {
        super(props);
        this._contentChildren = null;
        this._contentChildrenActual = null;
        this._contentChildrenMap = null;
        this._contentChildrenUnresolved = 0;
        this._hasUserValues = new Set();
        /**
         * The series actually present in the chart. Do not directly modify this array.
         * This array's contents can be modified by causing Angular to reproject the child content.
         * Or adding and removing series from the manual series collection on the series property.
         */
        this.actualSeries = [];
        this._seriesAdapter = null;
        this._series = null;
        this._defaultTooltips = null;
        this._uniqueTooltipId = 0;
        this._initialized = false;
        this._sliceClick = null;
        this._sliceClick_wrapped = null;
        this._holeDimensionsChanged = null;
        this._holeDimensionsChanged_wrapped = null;
        this._getMainRef = this._getMainRef.bind(this);
        this._tooltipRef = this._tooltipRef.bind(this);
        this._activeTooltipElements = new Map();
        this._activeTooltips = new Map();
        this._currentTooltips = [];
        this._contentChildrenManager = new ContentChildrenManager((ch) => ch.key || ch.props.name, (ch) => ch.key || ch.props.name, () => this._updateContentChildren());
        let container = null;
        if (document) {
            container = document.createElement("div");
            container.style.display = "block";
            container.style.width = "100%";
            container.style.height = "100%";
        }
        var root;
        root = container;
        this._renderer = new ReactRenderer(root, document, true, DataChartStylingDefaults);
        this.container = this._renderer.getWrapper(container);
        this._implementation = this.createImplementation();
        this._implementation.externalObject = this;
        this.onImplementationCreated();
        this._wrapper = this._renderer;
        var chart = this.i;
        this._chart = chart;
        chart.provideContainer(this._renderer);
        this._renderer.addSizeWatcher(() => {
            this._chart.notifyResized();
        });
    }
    set height(value) {
        this._height = value;
        if (this._elRef) {
            this._elRef.style.height = value;
            this._chart.notifyResized();
        }
    }
    get height() {
        return this._height;
    }
    set width(value) {
        this._width = value;
        if (this._elRef) {
            this._elRef.style.width = value;
            this._chart.notifyResized();
        }
    }
    get width() {
        return this._width;
    }
    _updateContentChildren() {
        this.contentSeries = [];
        let contentChildrenActual = this._contentChildrenManager.contentChildrenActual;
        for (let i = 0; i < contentChildrenActual.length; i++) {
            if ((RingSeriesBase.$type).isAssignableFrom(contentChildrenActual[i].i.$type)) {
                this.contentSeries.push(contentChildrenActual[i]);
            }
        }
        if (this._seriesAdapter !== null) {
            this._seriesAdapter.notifyContentChanged();
        }
    }
    _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);
    }
    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 = this._contentChildrenManager.getChildren(this.props.children);
        if (this._currentTooltips && this._currentTooltips.length > 0) {
            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: this._getMainRef,
            children: children
        });
        return div;
        //}
    }
    get hasUserValues() {
        {
            return this._hasUserValues;
        }
    }
    componentDidMount() {
        for (const p of Object.keys(this.props)) {
            if (isValidProp(this, p)) {
                {
                    this._hasUserValues.add(p);
                    this[p] = this.props[p];
                }
            }
        }
        this._elRef.style.width = this._width ? this._width : "";
        this._elRef.style.height = this._height ? this._height : "";
        this._elRef.appendChild(this.container.getNativeElement());
        this._chart.notifyResized();
        this.initializeContent();
    }
    shouldComponentUpdate(nextProps, nextState) {
        const mod = getModifiedProps(this.props, nextProps);
        for (const p of Object.keys(mod)) {
            if (isValidProp(this, p)) {
                this._hasUserValues.add(p);
                this[p] = mod[p];
            }
        }
        return true;
    }
    _getMainRef(div) {
        this._elRef = div;
    }
    destroy() {
        this._chart.destroy();
        this._wrapper.destroy();
    }
    initializeContent() {
        initializePropertiesFromCss(this.container.getNativeElement(), this, "doughnut-chart-", this.hasUserValues);
        this._updateContentChildren();
        this._seriesAdapter = new CollectionAdapter(this.contentSeries, this.i.series, this.actualSeries, (c) => c.i, (i) => {
            i.owner = this;
            // (<any>i)._provideRenderer(this._dataSource);
            this._ensureDefaultTooltip(i);
            this._ensureTooltipCreated(i);
            //(<any>i)._provideRenderer(this._wrapper); 
        }, (i) => {
            //(<any>i)._provideRenderer(null);
        });
        this.i.notifyResized();
    }
    componentWillUnmount() {
        //this._grid.destroy();
        this.destroy();
    }
    onImplementationCreated() {
    }
    createImplementation() {
        return new XamDoughnutChart();
    }
    get i() {
        return this._implementation;
    }
    createSeriesComponent(type) {
        if (TypeRegistrar.isRegistered(type)) {
            let s = TypeRegistrar.create(type);
            s.owner = this;
            s._provideRenderer(this._wrapper);
            return s;
        }
        else {
            //we shouldn't get here, hopefully.
            throw Error("series type not loaded: " + type);
        }
    }
    /**
     * A collection or manually added series for the chart.
    */
    get series() {
        if (this._series === null) {
            let coll = new RingSeriesCollection();
            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;
    }
    _ensureTooltipCreated(series) {
        if (series.i.toolTip == null) {
            let tooltip = this.createTooltip();
            let ele = tooltip;
            if (tooltip == null) {
                return;
            }
            series._tooltipContent = tooltip;
            //(<any>tooltip.instance).template = this._tooltipTemplate;
            series.i.toolTip = this.createWrapper(tooltip);
            if (this._activeTooltips.has(ele)) {
                let tCont = this._activeTooltips.get(ele);
                tCont.template = ele.tooltipTemplate;
            }
        }
    }
    createWrapper(ele) {
        let wrapper = new ReactWrapper(ele, this._wrapper);
        wrapper.updateToolTip = ele.updateToolTip;
        wrapper.hideToolTip = ele.hideToolTip;
        if (this._activeTooltips.has(ele)) {
            let tCont = this._activeTooltips.get(ele);
            tCont.template = ele.tooltipTemplate;
        }
        return wrapper;
    }
    _ensureDefaultTooltip(series) {
        if (this._defaultTooltips == null) {
            return;
        }
        this._defaultTooltips.instance["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() {
        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, isSubContent) {
            if (c.externalObject) {
                c = c.externalObject;
            }
            else {
                let ext = new DataContext();
                ext._implementation = c;
                c = ext;
            }
            if (!isSubContent) {
                if (ele.parentElement != self.container.getNativeElement()) {
                    if (ele.parentElement != null) {
                        ele.parentElement.removeChild(ele);
                    }
                    self.container.getNativeElement().appendChild(ele);
                }
            }
            else {
                c.isSubContent = true;
            }
            if (self._activeTooltips.has(ele)) {
                let t = self._activeTooltips.get(ele);
                if (t.template === null &&
                    ele.tooltipTemplate !== null) {
                    t.template = ele.tooltipTemplate;
                }
                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 });
        }
    }
    /**
 * Gets or sets whether the slices can be selected.
*/
    get allowSliceSelection() {
        return this.i.aw;
    }
    set allowSliceSelection(v) {
        this.i.aw = ensureBool(v);
    }
    /**
     * Gets or sets whether all surface interactions with the plot area should be disabled.
    */
    get isSurfaceInteractionDisabled() {
        return this.i.ax;
    }
    set isSurfaceInteractionDisabled(v) {
        this.i.ax = ensureBool(v);
    }
    /**
     * Gets or sets whether the slices can be exploded.
    */
    get allowSliceExplosion() {
        return this.i.av;
    }
    set allowSliceExplosion(v) {
        this.i.av = ensureBool(v);
    }
    /**
     * Gets or sets the inner extent of the doughnut chart. It is percent from the outer ring's radius.
    */
    get innerExtent() {
        return this.i.a3;
    }
    set innerExtent(v) {
        this.i.a3 = +v;
    }
    /**
     * Gets or sets the fill brush.
    */
    get selectedSliceFill() {
        return this.i.ce ? this.i.ce.fill : null;
    }
    set selectedSliceFill(v) {
        this.ensureSelectedStyle();
        this.i.ce.fill = v;
    }
    /**
     * Gets or sets the stroke brush.
    */
    get selectedSliceStroke() {
        return this.i.ce ? this.i.ce.stroke : null;
    }
    set selectedSliceStroke(v) {
        this.ensureSelectedStyle();
        this.i.ce.stroke = v;
    }
    /**
     * Gets or sets the stroke thickness.
    */
    get selectedSliceStrokeThickness() {
        return this.i.ce ? this.i.ce.strokeThickness : NaN;
    }
    set selectedSliceStrokeThickness(v) {
        this.ensureSelectedStyle();
        this.i.ce.strokeThickness = +v;
    }
    /**
     * Gets or sets the opacity.
    */
    get selectedSliceOpacity() {
        return this.i.ce ? this.i.ce.opacity : NaN;
    }
    set selectedSliceOpacity(v) {
        this.ensureSelectedStyle();
        this.i.ce.opacity = +v;
    }
    ensureSelectedStyle() {
        if (this.i.ce) {
            return;
        }
        this.i.ce = new Style();
    }
    /**
     * Gets or sets the scaling value used to affect the pixel density of the control.
     * A higher scaling ratio will produce crisper visuals at the expense of memory.  Lower values will cause the control
     * to appear blurry.
    */
    get pixelScalingRatio() {
        return this.i.a4;
    }
    set pixelScalingRatio(v) {
        this.i.a4 = +v;
    }
    /**
     * Resolved pixel scaling ratio. Unless explicitly overridden by the IgxDoughnutChart.PixelScalingRatioComponent property,
     * this one returns the default ratio enforced by device. High resolution devices will initialize this property
     * to a higher value.
    */
    get actualPixelScalingRatio() {
        return this.i.a1;
    }
    set actualPixelScalingRatio(v) {
        this.i.a1 = +v;
    }
    /**
 * Called by the UI framework to provide a UI container for rendering this control.

* @param container  * The UI container element.
*/
    provideContainer(container) {
        this.i.provideContainer(container);
    }
    /**
     * Called when the control has been resized.
    
    */
    notifyResized() {
        this.i.notifyResized();
    }
    /**
     * Gets the ID of the UI container.
    
    */
    getContainerID() {
        let iv = this.i.bd();
        return (iv);
    }
    /**
     * Gets the center coordinates of the doughnut chart's center presenter.
    
    */
    getCenterCoordinates() {
        let iv = this.i.cd();
        return fromPoint(iv);
    }
    /**
     * Gets the hole radius of the doughnut chart's center presenter.
    
    */
    getHoleRadius() {
        let iv = this.i.a2();
        return (iv);
    }
    /**
     * Use to force the doughnut chart to finish any deferred work before printing or evaluating its visual.
     * This should only be called if the visual of the doughnut chart needs to be synchronously saved or evaluated.
     * Calling this method too often will hinder the performance of the doughnut chart.
    
    */
    flush() {
        this.i.bk();
    }
    /**
     * Returns the chart visuals expressed as a serialized string.
    
    */
    exportSerializedVisualData() {
        let iv = this.i.bc();
        return (iv);
    }
    notifyInsertItem(source_, index, newItem) {
        this.i.bm(source_, index, newItem);
    }
    notifySetItem(source_, index, oldItem, newItem) {
        this.i.bp(source_, index, oldItem, newItem);
    }
    /**
     * Used to manually notify the chart that the data source has reset or cleared its items.
    
    */
    notifyClearItems(source_) {
        this.i.bl(source_);
    }
    notifyRemoveItem(source_, index, oldItem) {
        this.i.bn(source_, index, oldItem);
    }
    /**
     * Raised when the slice is clicked.
    */
    get sliceClick() {
        return this._sliceClick;
    }
    set sliceClick(ev) {
        if (this._sliceClick_wrapped !== null) {
            this.i.sliceClick = delegateRemove(this.i.sliceClick, this._sliceClick_wrapped);
            this._sliceClick_wrapped = null;
            this._sliceClick = null;
        }
        this._sliceClick = ev;
        this._sliceClick_wrapped = (o, e) => {
            let outerArgs = new SliceClickEventArgs();
            outerArgs._provideImplementation(e);
            if (this.beforeSliceClick) {
                this.beforeSliceClick(this, outerArgs);
            }
            if (this._sliceClick) {
                this._sliceClick(this, outerArgs);
            }
        };
        this.i.sliceClick = delegateCombine(this.i.sliceClick, this._sliceClick_wrapped);
    }
    /**
     * Raised when the dimensions (center point or radius) of the doughnut hole change.
    */
    get holeDimensionsChanged() {
        return this._holeDimensionsChanged;
    }
    set holeDimensionsChanged(ev) {
        if (this._holeDimensionsChanged_wrapped !== null) {
            this.i.holeDimensionsChanged = delegateRemove(this.i.holeDimensionsChanged, this._holeDimensionsChanged_wrapped);
            this._holeDimensionsChanged_wrapped = null;
            this._holeDimensionsChanged = null;
        }
        this._holeDimensionsChanged = ev;
        this._holeDimensionsChanged_wrapped = (o, e) => {
            let outerArgs = new HoleDimensionsChangedEventArgs();
            outerArgs._provideImplementation(e);
            if (this.beforeHoleDimensionsChanged) {
                this.beforeHoleDimensionsChanged(this, outerArgs);
            }
            if (this._holeDimensionsChanged) {
                this._holeDimensionsChanged(this, outerArgs);
            }
        };
        this.i.holeDimensionsChanged = delegateCombine(this.i.holeDimensionsChanged, this._holeDimensionsChanged_wrapped);
    }
}
