/**
 * determine some statistical threshold values of given layer
 */
export default class FilterThresholdCalculator {

    private _min: number = NaN;
    private _max: number = NaN;
    private _median: number = NaN;
    private _quartileQ1: number = NaN;
    private _quartileQ3: number = NaN;

    public determineStatisticalThresholds(rasterList: number[][], fillValue: number) {
        let pixelList: number[] = [];
        rasterList.forEach((raster) => {
            raster.forEach(pixel => {
                if (!isNaN(pixel) && pixel != fillValue) {
                    pixelList.push(pixel);
                }
            })
        });
        this.calculateStatistics(pixelList)
    }

    protected calculateStatistics(pixelList: number[]) {

        pixelList = pixelList.sort((a, b) => a - b);

        this.setMin(pixelList);
        this.setMax(pixelList);
        this.setMedian(pixelList);
        this.setPercentile(pixelList, 0.25);
    }

    protected setMin(pixelList: number[]): void {
        this._min = pixelList[0];
    }

    protected setMax(pixelList: number[]): void {
        this._max = pixelList[pixelList.length - 1];
    }

    protected setMedian(pixelList: number[]): void {
        if (pixelList.length % 2 == 0) {
            let half: number = pixelList.length / 2 - 1;
            this._median = 0.5 * (pixelList[half] + pixelList[half + 1]);
        } else {
            let half: number = (pixelList.length + 1) / 2 - 1;
            this._median = pixelList[half];
        }
    }



    protected setPercentile(pixelList: number[], p: number): void {
        let lower: number = Math.floor(pixelList.length * p) - 1;
        let upper: number = Math.floor(pixelList.length * (1 - p)) - 1;

        if ((pixelList.length * p) % 1 === 0) { //is int?
            if (p == 0.25) {
                this._quartileQ1 = 0.5 * (pixelList[lower] + pixelList[lower + 1]);
                this._quartileQ3 = 0.5 * (pixelList[upper] + pixelList[upper + 1]);
            }
        } else {
            if (p == 0.25) {
                this._quartileQ1 = pixelList[lower + 1];
                this._quartileQ3 = pixelList[upper + 1];
            }
        }

    }

    public getMin(): number {
        return this._min;
    }
    public getMax(): number {
        return this._max
    }
    public getMedian(): number {
        return this._median;
    }
    public getQuartileQ1(): number {
        return this._quartileQ1;
    }
    public getQuartileQ3(): number {
        return this._quartileQ3;
    }

    public toString(): string {
        let value: string = "Filter ";
        value += "\n min: " + this._min;
        value += "\n max: " + this._max;
        value += "\n median :" + this._median;
        value += "\n q1: " + this._quartileQ1;
        value += "\n q3: " + this._quartileQ3;
        return value;
    }
}