import * as React from "react";
import * as MultiArticle from "@intelligentgraphics/ig.gfx.multiarticle";
import { Arr, isNullOrUndefined } from "@intelligentgraphics/ig.utilities";
import { Context } from "./Context";
import { FilterContext } from "./FilterContext";
class Filter extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = {
            configuration: {},
            ready: false,
            availableProperties: [],
        };
        this.buildFilterContext = () => ({
            getValues: this.getValues,
            getCurrentValue: this.getCurrentValue,
            setValue: this.setValue,
            hasProperty: this.hasProperty,
            getArticles: this.getViableArticles,
            getAllValues: this.getAllValues,
            configuration: this.state.configuration,
            getAllProperties: this.getAllProperties,
        });
        this.getViableArticles = () => this.filter.getViableArticles();
        this.getValues = (key) => this.filter.getValues(key);
        this.getCurrentValue = (key) => this.filter.getCurrentValue(key);
        this.hasProperty = (key) => !!this.state.availableProperties.find(prop => prop === key);
        this.getAllProperties = () => this.getPackage().getProperties(this.props.root);
        this.getAllValues = (key, filterWith) => {
            const multiPackage = this.getPackage();
            if (!filterWith) {
                return Arr.unique(multiPackage.getPropertyValues(this.props.root, key));
            }
            const instances = multiPackage.Instances[this.props.root];
            const configurationSubset = filterWith.reduce((acc, prop) => ((acc[prop] = this.filter.getCurrentValue(prop)), acc), {});
            const values = new Set();
            for (const entry in instances) {
                if (multiPackage.matchesConfigExceptForOwnProperties(entry, configurationSubset)) {
                    values.add(instances[entry][key]);
                }
            }
            return Array.from(values);
        };
        this.reset = () => {
            this.filter["configuration"] = {};
            this.setState({ configuration: {} });
        };
        this.setValue = (key, value) => {
            const newValue = isNullOrUndefined(value) || this.state.configuration[key] === value
                ? undefined
                : value;
            this.filter["configuration"] = {
                ...this.filter["configuration"],
            };
            if (newValue === undefined) {
                delete this.filter["configuration"][key];
            }
            else {
                this.filter["configuration"][key] = newValue;
            }
            this.setState({
                configuration: {
                    ...this.filter["configuration"],
                },
            }, () => {
                if (this.props.updateConfiguration) {
                    this.props.updateConfiguration(key, value);
                }
            });
        };
    }
    static getDerivedStateFromProps(nextProps, nextState) {
        if (!nextProps.configuration) {
            return null;
        }
        return {
            configuration: nextProps.configuration,
        };
    }
    render() {
        if (!this.state.ready) {
            return null;
        }
        return (React.createElement(FilterContext.Provider, { value: this.buildFilterContext() }, this.props.children({
            properties: this.state.availableProperties,
        })));
    }
    componentDidMount() {
        this.setState(this.buildInitialState());
    }
    componentDidUpdate(prevProps, prevState) {
        if (prevProps.configuration !== this.props.configuration) {
            this.filter["configuration"] = this.props.configuration;
        }
        if (this.state.configuration !== prevState.configuration) {
            if (this.props.onViableArticlesChange) {
                this.props.onViableArticlesChange(this.filter.getViableArticles());
            }
        }
    }
    buildInitialState() {
        const multiPackage = this.getPackage();
        if (this.props.filter instanceof MultiArticle.MultiArticleFilter) {
            this.filter = this.props.filter;
        }
        else {
            this.filter = new MultiArticle.MultiArticleFilter(multiPackage);
        }
        if (typeof this.props.filterMode === "string") {
            const FilterConstructor = MultiArticle[`${this.props.filterMode}Mode`];
            this.filterMode = new FilterConstructor(multiPackage, this.props.root);
        }
        else {
            this.filterMode = new this.props.filterMode(multiPackage, this.props.root);
        }
        this.filter.startMode(this.filterMode);
        this.filter.setProperty = this.setValue;
        this.filter.reset = this.reset;
        if (this.props.initialConfiguration !== undefined) {
            this.filter["configuration"] = {
                ...this.props.initialConfiguration,
            };
        }
        else if (this.props.configuration !== undefined) {
            this.filter["configuration"] = {
                ...this.props.configuration,
            };
        }
        const availableProperties = multiPackage.getProperties(this.props.root);
        return {
            availableProperties,
            ready: true,
            configuration: this.filter["configuration"],
        };
    }
    getPackage() {
        if (this.props.package !== undefined) {
            return this.props.package;
        }
        if (this.context === undefined) {
            throw new Error(`The Filter component requires a MultiArticle Package to be supplied either through the package prop or through context`);
        }
        return this.context;
    }
}
Filter.contextType = Context;
export default Filter;
