import ImageLayer from 'ol/layer/Image';
import TemporalData, { isTemporalData } from './TemporalData';
import DateRange from "../../dateSelection/daterangepicker/DateRange";
import LayerColorScale, { HasColorScale } from './LayerColorScale';
import NumericalParameter from './Parameter';
import Filter from './Filter';
import IHasFilter, { isFilterable } from './HasFilter';
import HasParameterGradient from './HasParameterGradient';
import { Extent, createEmpty } from 'ol/extent';
import NetcdfRasterSource from './NetcdfRasterSource';
import Histogram from '../../model/Histogram';

export default class TemporalImageLayer extends ImageLayer implements TemporalData, HasColorScale, IHasFilter {

    public constructor(options: object) {
        super(options);

        if (!TemporalImageLayer.checkTemporalSource(options)) {
            throw new TypeError('invalid layer options');
        }
    }

    private static checkTemporalSource(options: object): boolean {
        if (!options) {
            console.warn('missing options for temporal layer');
            return false;
        }

        if (!options['source']) {
            console.warn('missing source in options for temporal layer');
            return false;
        }

        if (!isTemporalData(options['source'])) {
            console.warn('given source option is not a temporal data source');
            return false;
        }

        return true;
    }

    public updateSourceColorScale(): void {
        if (this.hasLayerColorScale()) {
            let colorScale = this.getLayerColorScale();
            let src: HasParameterGradient = this.getParameterGradientSource();
            src.setParameterAndGradient(colorScale.getSelectedParameter().getName(), colorScale.getGradient());
            // if ('setParameterAndGradient' in src) {
            //     src.setParameterAndGradient(colorScale.getSelectedParameter().getName(), colorScale.getGradient());
            // } else {
            //     if ('setGradient' in src) {
            //         src.setGradient(colorScale.getGradient());
            //     }
            //     if ('setParameter' in src) {
            //         src.setParameter(colorScale.getSelectedParameter().getName());
            //     }
            // }
        }
    }

    // override HasColorScale
    public updateLayerColorScale(parameters: NumericalParameter[]): void {
        if (this.hasLayerColorScale()) {
            this.getLayerColorScale().updateParameters(parameters);
            // TODO: should we trigger an update event here?
            // this.getSuper().dispatchEvent(LayerColorScale.KEY);
        }
    }

    // override: HasColorScale
    public getAvailableParameters(): NumericalParameter[] {
        let src = this.getSource();
        if ('getAvailableParameters' in src) {
            return this.getParameterGradientSource().getAvailableParameters();
        }

        return [];
    }

    // override: HasColorScale
    public getLayerColorScale(): LayerColorScale {
        return this.get(LayerColorScale.KEY);
    }

    // override: HasColorScale
    public hasLayerColorScale(): boolean {
        return (this.getLayerColorScale() !== undefined);
    }

    // override: HasColorScale
    public setLayerColorScale(colorScale: LayerColorScale): void {
        colorScale.registerImageLayer(this);
        this.updateSourceColorScale();
    }

    // override: HasColorScale
    public isAutoLayerColorScale(): boolean {
        return (this.get(LayerColorScale.AUTO_COLOR_SCALE_KEY) !== LayerColorScale.AUTO_COLOR_SCALE_DISABLE_VALUE);
    }

    // override: HasColorScale
    public setAutoLayerColorScale(auto: boolean): void {
        if (auto) {
            super.unset(LayerColorScale.AUTO_COLOR_SCALE_KEY);
        } else {
            super.set(LayerColorScale.AUTO_COLOR_SCALE_KEY, LayerColorScale.AUTO_COLOR_SCALE_DISABLE_VALUE);
        }
    }

    public getExtent(): Extent {
        if (this.getSource() instanceof NetcdfRasterSource) {
            return (this.getSource() as NetcdfRasterSource).getExtent();
        } else {
            createEmpty();
        }
    }

    private getTemporalDataSource(): TemporalData {
        return (this.getSource() as unknown as TemporalData);
    }

    private getFilterableSource(): IHasFilter {
        return (this.getSource() as unknown as IHasFilter);
    }

    private getParameterGradientSource(): HasParameterGradient {
        return (this.getSource() as unknown as HasParameterGradient);
    }

    // forward the temporal data calls to the actual source
    public getDate(): Date {
        return this.getTemporalDataSource().getDate();
    }

    public setDate(date: Date): void {
        this.getTemporalDataSource().setDate(date);
        this.changed();
    }

    public getDateRange(): DateRange {
        return this.getTemporalDataSource().getDateRange();
    }

    public getAllowedDates(): Date[] {
        return this.getTemporalDataSource().getAllowedDates();
    }

    // override: HasFilter
    public setFilter(filter: Filter): void {
        if (isFilterable(super.getSource())) {
            this.getFilterableSource().setFilter(filter);
            this.changed();
        } else {
            console.warn('unable to set filter due to unfilterable source', super.getSource());
        }
    }

    public getFilter(): Filter | undefined {
        if (isFilterable(super.getSource())) {
            return this.getFilterableSource().getFilter();
        }
    }

    public getHistogram(numBuckets: number = 100, sampleBounds: [number, number] = [NaN, NaN]): Histogram {
        if (this.getSource() instanceof NetcdfRasterSource) {
            return (this.getSource() as NetcdfRasterSource).getHistogram(numBuckets, sampleBounds);
        }

        return undefined;
    }

    /**
     *  Returns histograms for all allowed dates of the dataset
     * 
     */
    public getHistograms(numBuckets: number = 100, sampleBounds: [number, number] = [NaN, NaN]): Histogram[] {
        if (this.getSource() instanceof NetcdfRasterSource) {
            return (this.getSource() as NetcdfRasterSource).getHistograms(numBuckets, sampleBounds);
        }

        return undefined;
    }

}
