import { DatasetGroup } from "dasf-web/lib/dataDiscovery/DataArchive";
import PulsarConnection from "dasf-web/lib/messaging/PulsarConnection";
import { PulsarModuleResponse, PulsarModuleRequest, PulsarModuleRequestReceipt, PulsarProgressReport } from "dasf-web/lib/messaging/PulsarMessages";
import DateRange from "dasf-web/lib/dateSelection/daterangepicker/DateRange";
import GeoJSON from 'ol/format/GeoJSON';
import Geometry from "ol/geom/Geometry";
import Feature from "ol/Feature";
import { DigitalEarthUrlBuilder } from "dasf-web/lib/messaging/PulsarUrlBuilder";

const PULSAR_HOST = "rz-vm154.gfz-potsdam.de";
const PULSAR_PORT = "8082";
const PULSAR_TOPIC = "change";

export class ChangeModule {

    private static readonly PROP_KEY_REF_GROUP = "referenceGroup";
    private static readonly PROP_KEY_COMP_GROUP = "compareGroup";
    private static readonly PROP_KEY_ROI = "roi";

    private pulsarConnection: PulsarConnection;

    public constructor() {
        this.pulsarConnection = new PulsarConnection(new DigitalEarthUrlBuilder(PULSAR_TOPIC));
    }

    public isConnected(): boolean {
        return this.pulsarConnection.isBackendConnected();
    }

    public sendCordexMaskRequest(roiGeometry: Geometry, domainName: string, clipToRoi: boolean, onResponse: (status: string, data: string) => void): void {
        let request = this.createCordexMaskRequestMessage(roiGeometry, domainName, clipToRoi);

        console.log("sending cordex mask request to change backend module");
        // console.log(request);

        this.pulsarConnection.sendRequest(request, (response: PulsarModuleResponse) => {
            // on response
            console.log("received response for cordex mask request");

            // forward payload
            onResponse(response.properties['status'], atob(response.payload));
        }, undefined,
            (receipt: PulsarModuleRequestReceipt) => {
                // on error
                console.warn("received error for request");
                console.warn(receipt);
            });
    }

    private readonly analysisSteps: string[] = ['download', 'merge', 'statistics', 'plot'];

    public sendDownloadRequest(requestReference: string, step: number, onResponse: (status: string, data: string, filename?: string) => void): void {
        let request = this.createDownloadRequestMessage(requestReference, step);

        console.log("sending download request to change backend module");
        // console.log(request);

        this.pulsarConnection.sendRequest(request, (response: PulsarModuleResponse) => {
            // on response
            console.log("received response for download request");

            let filename: string = undefined;
            if(response.properties.hasOwnProperty('filename')) {
                filename = response.properties['filename'];
            }

            // forward payload
            onResponse(response.properties['status'], response.payload, filename);
        }, undefined,
            (receipt: PulsarModuleRequestReceipt) => {
                // on error
                console.warn("received error for request");
                console.warn(receipt);
            });
    }

    private createDownloadRequestMessage(requestReference: string, step: number): PulsarModuleRequest {
        if (step < 0 || step >= this.analysisSteps.length) {
            throw new Error('invalid analysis step index: ' + step);
        }

        let r = PulsarModuleRequest.createRequestMessage();

        
        let functionArgs = {
            'request_reference': requestReference,
            'step': this.analysisSteps[step]
        };

        r.properties['function'] = 'download';
        r.properties['function_args'] = JSON.stringify(functionArgs);

        return r;
    }

    private createCordexMaskRequestMessage(roiGeometry: Geometry, domainName: string, clipToRoi: boolean): PulsarModuleRequest {
        let r = PulsarModuleRequest.createRequestMessage();

        let roiFeature: Feature = new Feature({
            geometry: roiGeometry,
            name: 'roi'
        });

        let functionArgs = {
            'roi': new GeoJSON().writeFeatures([roiFeature]),
            'domain': domainName,
            'clip_to_roi': clipToRoi
        };

        r.properties['function'] = 'cordex_mask';
        r.properties['function_args'] = JSON.stringify(functionArgs);

        return r;
    }

    public sendAnalysisRequest(referenceGroup: DatasetGroup, compareGroup: DatasetGroup, roiWkt: string, onResponse: (base64SVG: string, request_reference: string) => void, onProgress?: (responseMsg: PulsarProgressReport, msgProperties: object) => void): void {
        let request = this.createAnalysisRequestMessage(referenceGroup, compareGroup, roiWkt);

        console.log("sending request to change backend module");
        // console.log(request);

        this.pulsarConnection.sendRequest(request,
        (response: PulsarModuleResponse) => {
            console.log("received response for request");
            // extract request_reference from properties
            let requestReference = '';
            if('request_reference' in response.properties) {
                requestReference = response.properties['request_reference'];
            }
            onResponse(response.payload, requestReference);
        }, onProgress
        , (receipt: PulsarModuleRequestReceipt) => {
            console.warn("received error for request");
            console.warn(receipt);
        });
    }

    private createAnalysisRequestMessage(referenceGroup: DatasetGroup, compareGroup: DatasetGroup, roiWkt: string): PulsarModuleRequest {
        let refGroup: string = JSON.stringify(this.simplifyDatasetGroup(referenceGroup));
        let compGroup: string = JSON.stringify(this.simplifyDatasetGroup(compareGroup));


        let r = PulsarModuleRequest.createRequestMessage();

        let functionArgs = {};
        functionArgs[ChangeModule.PROP_KEY_REF_GROUP] = refGroup;
        functionArgs[ChangeModule.PROP_KEY_COMP_GROUP] = compGroup;
        functionArgs[ChangeModule.PROP_KEY_ROI] = roiWkt;

        r.properties['function'] = 'change_analysis';
        r.properties['function_args'] = JSON.stringify(functionArgs);

        return r;
    }

    private simplifyDatasetGroup(group: DatasetGroup): any {        
        let name = group.name;
        let datasets: any[] = [];
        let intervals: any[] = [];

        // dataset name and url
        for (let ds of group.datasets) {
            let dataset = {
                name: ds.getName(),
                url: ds.getUrl()
            }
            datasets.push(dataset);
        }

        // time intervals
        for (let interval of group["averagingIntervals"]) {
            let dr = interval as DateRange;
            let i = {
                start: dr.selectedMinDate.toISOString(),
                end: dr.selectedMaxDate.toISOString()
            }
            intervals.push(i);
        }

        // date range
        let dateRange = {
            start: group.dateRange.selectedMinDate.toISOString(),
            end: group.dateRange.selectedMaxDate.toISOString()
        }

        let g = {
            name: name,
            dateRange: dateRange,
            datasets: datasets,
            averagingIntervals: intervals
        };

        return g;
    }

}

const BackendModule: ChangeModule = new ChangeModule();

/**
 * Singleton module handle export
 */
export default BackendModule;
