import axios, { AxiosResponse } from 'axios';
import * as xmljs from 'xml-js';
import getBackendModule from '../../../dataAcquisition/RasterBackendModule';
import NetcdfRasterSource from '../NetcdfRasterSource';
import Geometry from 'ol/geom/Geometry';


export class WcsCoverageSummary {
    public lowerCorner: string[] = [];
    public upperCorner: string[] = [];
    public axis: string[] = [];
    public type: string = 'unknown';
    public coverageId = '';
    public xIdx = 0;
    public yIdx = 1;
    public tIdx = Number.NaN;

    constructor(json: object) {
        this.parse(json);
    }

    private parse(json: object): void {
        this.lowerCorner = (WCS.getAttribute(json, ['ows:BoundingBox', 'ows:LowerCorner', '_text']) as unknown as string).split(' ');
        this.upperCorner = (WCS.getAttribute(json, ['ows:BoundingBox', 'ows:UpperCorner', '_text']) as unknown as string).split(' ');
        this.axis = WCS.getAxisList(json)
        this.type = (WCS.getAttribute(json, ['wcs:CoverageSubtype', '_text']) as unknown as string);
        this.coverageId = WCS.getAttribute(json, ['wcs:CoverageId', '_text']) as unknown as string;

        this.setAxisIdx();
    }

    private setAxisIdx(): void {
        for(let i=0; i<this.axis.length; ++i) {
            switch(this.axis[i].toLocaleLowerCase()) {
            case 'lon':
            case 'lng':
            case 'long':
            case 'longitude':
            case 'x':
                this.xIdx = i;
                break;
            case 'lat':
            case 'latitude':
            case 'y':
                this.yIdx = i;
                break;
            case 'ansi':
            case 'time':
            case 'date':
            case 'ansidate':
                this.tIdx = i;
                break;
            default:
                break;
            }
        }
    }

    public hasTime(): boolean {
        return !Number.isNaN(this.tIdx);
    }
}
export class WcsCapabilities {
    public coverages: WcsCoverageSummary[];
}

export default class WCS {
    constructor(private baseUrl: string) {
    }

    public async getCapabilities(): Promise<WcsCapabilities> {
        // https://data.awi.de/rasdaman/ows?&SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCapabilities
        let capabilitiesUrl = this.baseUrl + '?SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCapabilities'
        let result: AxiosResponse = await axios.get(capabilitiesUrl);
        if (result && result.status == 200 && result.data) {
            return this.parseCapabilities(result.data);
        } else {
            throw new Error('error requesting wcs capabilites from ' + capabilitiesUrl);
        }
    }

    public async getCoverage(coverage: string | WcsCoverageSummary, subset: string[], roiMask?: Geometry): Promise<NetcdfRasterSource> {
        let id = coverage instanceof WcsCoverageSummary ? coverage.coverageId : coverage;
        let coverageUrl = this.baseUrl + '?SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCoverage&COVERAGEID=' + id;

        // append subset
        for(let sub of subset) {
            coverageUrl += '&SUBSET='+sub;
        }

        // append netcdf format
        coverageUrl += '&FORMAT=application/netcdf'

        // since we don't know whether we get a compatible netcdf version 3 file
        // from the wcs server, we pipe the request through the raster backend module 
        // to ensure that we get a version 3 netcdf
        return new Promise<NetcdfRasterSource>((resolve, reject) => {
            getBackendModule().requestNetcdfFromUrl((status: string, netcdf: string | NetcdfRasterSource) => {
                if(status == 'success') {
                    resolve(netcdf as NetcdfRasterSource);
                } else {
                    reject(netcdf);
                }
            }, coverageUrl, roiMask);
        });
        

        // let result = await axios.get(coverageUrl, {proxy: false});
        // if (result && result.status == 200 && result.data) {
        //     return result.data
        // } else {
        //     throw new Error('error requesting wcs coverage from ' + coverageUrl);
        // }
    }

    public static getAxisList(json: object): string[] {
      const params = WCS.getAttribute(json, ['ows:AdditionalParameters'])['ows:AdditionalParameter']

      for(let param of params) {
        if ( ( WCS.getAttribute(param, ['ows:Name', '_text']) as unknown as string ) === 'axisList' ) {
          return (WCS.getAttribute(param, ['ows:Value', '_text']) as unknown as string).split(',')
        }
      }

      return []
    }

    public parseCapabilities(capabilitiesXML): WcsCapabilities {
        // convert to json
        let jC = xmljs.xml2js(capabilitiesXML, { compact: true });

        // parse converage summaries
        let coverages: object[] = WCS.getAttribute(jC, ['wcs:Capabilities', 'wcs:Contents', 'wcs:CoverageSummary']) as object[];
        let parsedCov: WcsCoverageSummary[] = [];
        for (let coverage of coverages) {
            parsedCov.push(new WcsCoverageSummary(coverage));
        }

        let c = new WcsCapabilities();
        c.coverages = parsedCov;

        return c;
    }

    public static getAttribute(json: object, path: string[]): object {
        while (path.length > 0) {
            json = json[path.shift()]

            if (json == undefined) {
                throw new Error('error extracting xml nodes or attributes');
            }
        }

        return json;
    }
}
