// Copyright 2021 - Unistra/CNRS
// The MOC API project is distributed under the terms
// of the GNU General Public License version 3.
//
//This file is part of MOC API java project.
//
//    MOC API java project is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, version 3 of the License.
//
//    MOC API java project is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    The GNU General Public License is available in COPYING file
//    along with MOC API java project.
//

package cds.moc;

import static cds.healpix.Healpix.nHash;

import cds.healpix.FlatHashIterator;
import cds.healpix.HealpixNestedBMOC;
import cds.healpix.HealpixNestedFixedRadiusConeComputer;

/** HEALPix CDS wrapper
 * Encapsulate the usage of the HEALpix lib
 *
 * The HEALPix ordering is always NESTED
 *
 * @author Pierre Fernique [CDS]
 * @version 1.3 May 2014 - NPIX => UNIQ fits keyword
 * @version 1.2 Jan 2012 - Thread safe implementation
 * @version 1.1 Oct 2011 - direct HealpixBase use
 * @version 1.0 May 2011
 */
public final class Healpix implements HealpixImpl {

   /** Provide the HEALPix number associated to a coord, for a given order
    * @param order HEALPix order [0..MAXORDER]
    * @param lon longitude (expressed in the Healpix frame)
    * @param lat latitude (expressed in the Healpix frame)
    * @return HEALPix number
    * @throws Exception
    */
   public long ang2pix(int order,double lon, double lat) throws Exception {
      return cds.healpix.Healpix.getNestedFast(order).hash(Math.toRadians(lon), Math.toRadians(lat));
   }

   /** Provide the spherical coord associated to an HEALPix number, for a given order
    * @param order HEALPix order [0..MAXORDER]
    * @param npix HEALPix number
    * @return coord (lon,lat) (expressed in the Healpix frame)
    * @throws Exception
    */
   public double [] pix2ang(int order,long npix) throws Exception {
	  final double[] lonlat = cds.healpix.Healpix.getNestedFast(order).center(npix);
	  lonlat[0] = Math.toDegrees(lonlat[0]);
	  lonlat[1] = Math.toDegrees(lonlat[1]);
      return lonlat;
   }

   /** Provide the list of HEALPix numbers fully covering a circle (for a specified order)
    * @param order Healpix order
    * @param lon    center longitude (expressed in the Healpix frame)
    * @param lat    center latitude (expressed in the Healpix frame)
    * @param radius circle radius (in degrees)
    * @return
    * @throws Exception
    */
   public long [] queryDisc(int order, double lon, double lat, double radius) throws Exception {
      lon = Math.toRadians(lon);
      lat = Math.toRadians(lat);
      radius = Math.toRadians(radius);
      // Exact
      // HealpixNestedFixedRadiusConeComputer cc = cds.healpix.Healpix.getNested(order).newConeComputer(coneRadiusRad)
      // Approx
      HealpixNestedFixedRadiusConeComputer cc = cds.healpix.Healpix.getNested(order).newConeComputerApprox(radius);
      HealpixNestedBMOC bmoc = cc.overlappingCells(lon, lat);
      long[] cells = new long[(int) bmoc.computeDeepSize()];
      FlatHashIterator it = bmoc.flatHashIterator();
      for(int i = 0; it.hasNext(); i++) {
    	  cells[i] = it.next();
      }
      return cells;
   }

   /*********************** Healpix Utilities ***************************************************/

   static public final double SKYAREA = 4.*Math.PI*Math.toDegrees(1.0)*Math.toDegrees(1.0);

   static public final long pow2(long order){ return 1L<<order;}
   
   /** Pixel area (in square degrees) for a given HEALPix order */
   static public double getPixelArea(int order) {
      if( order < 0 ) return SKYAREA;
      return SKYAREA/nHash(order);
   }
   
}
