import NetcdfRasterSource from "../map/model/NetcdfRasterSource";
import { DASFConnection, DigitalEarthUrlBuilder, DASFModuleRequest, DASFModuleRequestReceipt, DASFModuleResponse } from "dasf-messaging-typescript";
import b64a from 'base64-arraybuffer';
import Geometry from 'ol/geom/Geometry';
import Feature from 'ol/Feature';
import GeoJSON from "ol/format/GeoJSON";

const TOPIC = "raster";

export class RasterBackendModule {

    private static readonly PROP_KEY_MODULE = "module"
    private static readonly PROP_KEY_FUNCTION = "function";
    private static readonly PROP_KEY_URL = "url";
    private static readonly PROP_KEY_RASTER = "raster";
    private static readonly PROP_KEY_MASK = "mask";
    private static readonly PROP_VALUE_TRANSFORMER_MODULE = "RasterTransformer";
    private static readonly PROP_VALUE_NETCDF_FUNCTION = "to_netcdf_v3";
    private connection: DASFConnection;

    public constructor() {
        this.connection = new DASFConnection(new DigitalEarthUrlBuilder(TOPIC));
    }

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

    private createNetcdfRequest(roiMask?: Geometry): DASFModuleRequest {
        let r = DASFModuleRequest.createRequestMessage();
        r.properties[RasterBackendModule.PROP_KEY_MODULE] = RasterBackendModule.PROP_VALUE_TRANSFORMER_MODULE;
        r.properties[RasterBackendModule.PROP_KEY_FUNCTION] = RasterBackendModule.PROP_VALUE_NETCDF_FUNCTION;


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

            r.properties[RasterBackendModule.PROP_KEY_MASK] = new GeoJSON().writeFeatures([roiFeature]);
        }

        return r;
    }

    public requestNetcdfFromUrl(onResponse: (status: string, netcdf: NetcdfRasterSource | string) => void, url: string, roiMask?: Geometry): void {
        let request = this.createNetcdfRequest(roiMask);
        request.properties[RasterBackendModule.PROP_KEY_URL] = url;

        this.connection.sendRequest(request, (response: DASFModuleResponse) => {
            // on response
            // console.log("received response for netcdf raster request");

            // forward payload
            let payloadString = atob(response.payload);
            if (response.properties['status'] == 'success') {
                // successful request - decode
                let arrayBuf = b64a.decode(payloadString);
                NetcdfRasterSource.create({ data: arrayBuf }).then(ncSource => {
                    onResponse('success', ncSource);
                }).catch(reason => {
                    onResponse('error', reason as string);
                });

            } else {
                // non successful request - forward payload message string
                onResponse(response.properties['status'] as string, payloadString);
            }
        }, undefined,
            (receipt: DASFModuleRequestReceipt) => {
                // on error
                console.warn("received error for netcdf raster request");
                console.warn(receipt);
            });
    }

    public requestNetcdfBytes(onResponse: (status: string, netcdf: ArrayBuffer | string) => void, ncSource: NetcdfRasterSource, roiMask?: Geometry): void {
        let request = this.createNetcdfRequest(roiMask);
        request.properties[RasterBackendModule.PROP_KEY_RASTER] = JSON.stringify(ncSource.toJson());

        this.connection.sendRequest(request, (response: DASFModuleResponse) => {
            // on response
            // console.log("received response for netcdf bytes request");

            // forward payload
            let payloadString = atob(response.payload);
            if (response.properties['status'] == 'success') {
                // successful request - decode
                try {
                    let arrayBuf: ArrayBuffer = b64a.decode(payloadString);
                    onResponse('success', arrayBuf);
                } catch (error) {
                    onResponse('error', 'client error: ' + error);
                }
            } else {
                // non successful request - forward payload message string
                onResponse(response.properties['status'] as string, payloadString);
            }
        }, undefined,
            (receipt: DASFModuleRequestReceipt) => {
                // on error
                console.warn("received error for netcdf bytes request");
                console.warn(receipt);
            });
    }
}

let BackendModule: RasterBackendModule = null;

function getBackendModule(): RasterBackendModule {
  if (BackendModule == null) {
    BackendModule = new RasterBackendModule();
  }

  return BackendModule;
}

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