package com.interactive.ivaap.data.crs.apachesis;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.apache.sis.referencing.CRS;
import org.opengis.metadata.extent.Extent;
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.opengis.metadata.extent.GeographicExtent;
import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.ConcatenatedOperation;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.SingleOperation;
import org.opengis.referencing.operation.Transformation;

/**
 *
 * @author Michael Arneson
 */
public class Wgs84TransformUtil {

    public static CoordinateOperation getCoordinateTransform(String fromEpsg, String wgs84TransformCode, double x, double y) throws Exception {
        CoordinateReferenceSystem fromCrs = CRS.forCode(fromEpsg);
        CoordinateReferenceSystem toCrs = CRS.forCode("EPSG:4326");
        List<CoordinateOperation> operations = CRS.findOperations(fromCrs, toCrs, ExtentUtil.getExtentFromAreaOfUse(fromCrs, x, y, x, y));
        for (CoordinateOperation currentOperation : operations) {
            Transformation transformation = getTransformation(currentOperation);
            String transformCode = getEpsgTransformCode(transformation);
            if (transformCode.equals(wgs84TransformCode)) {
                return currentOperation;
            }
        }
        return null;
    }

    public static List<ApacheSisWgs84Transform> getWgs84TransformsFromCrs(String epsgCode) throws Exception {
        CoordinateReferenceSystem fromCrs = CRS.forCode(epsgCode);
        CoordinateReferenceSystem toCrs = CRS.forCode("EPSG:4326");
        List<CoordinateOperation> operations = CRS.findOperations(fromCrs, toCrs, null);
        List<ApacheSisWgs84Transform> wgs84Transforms = new ArrayList<>();
        for (CoordinateOperation currentOperation : operations) {
            Transformation transformation = getTransformation(currentOperation);
            String transformCode = getEpsgTransformCode(transformation);
            List<GeographicBoundingBox> bounds = getBoundingBoxFromTransformation(transformation);
            wgs84Transforms.add(new ApacheSisWgs84Transform(transformCode, bounds));
        }
        return wgs84Transforms;
    }

    private static List<GeographicBoundingBox> getBoundingBoxFromTransformation(Transformation transformation) {
        Extent domainOfValidity = transformation.getDomainOfValidity();
        if (domainOfValidity == null) {
            return null;
        }
        Collection<? extends GeographicExtent> geographicElements = domainOfValidity.getGeographicElements();
        if (geographicElements == null) {
            return null;
        }
        List<GeographicBoundingBox> boundingBoxes = new ArrayList<>();
        for (GeographicExtent currentExtent : geographicElements) {
            if (currentExtent instanceof GeographicBoundingBox) {
                boundingBoxes.add((GeographicBoundingBox) currentExtent);
            }
        }
        return boundingBoxes;
    }

    private static String getEpsgTransformCode(Transformation transformation) {
        Set<ReferenceIdentifier> identifiers = transformation.getIdentifiers();
        for (ReferenceIdentifier currentReferenceIdentifier : identifiers) {
            if (currentReferenceIdentifier.getCodeSpace().equals("EPSG")) {
                return currentReferenceIdentifier.getCode();
            }
        }
        return null;
    }

    private static Transformation getTransformation(CoordinateOperation operation) {
        if (operation instanceof ConcatenatedOperation) {
            ConcatenatedOperation concatOperation = (ConcatenatedOperation) operation;
            for (SingleOperation currentSingleOperation : concatOperation.getOperations()) {
                if (currentSingleOperation instanceof Transformation) {
                    return (Transformation) currentSingleOperation;
                }
            }
        }
        return null;
    }
}
