/*******************************************************************************
 * Copyright (c) 2019 Attila Kovacs <attila[AT]sigmyne.com>.
 * All rights reserved. 
 * 
 * This file is part of crush.
 * 
 *     crush 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, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     crush 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.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with crush.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Attila Kovacs <attila[AT]sigmyne.com> - initial API and implementation
 ******************************************************************************/

package crush.instrument.hawcplus;


import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

import crush.Instrument;
import crush.instrument.GridIndexed;
import crush.instrument.SingleEndedLayout;
import jnum.Unit;
import jnum.math.Vector2D;

class HawcLayout extends SingleEndedLayout implements GridIndexed { 
    /**
     * 
     */
    private static final long serialVersionUID = 7518391378648096316L;

    private Vector2D pixelSize;
    
    private Vector2D[] subarrayOffset;
    private double[] subarrayOrientation;
    
    private double[] polZoom;
    
    HawcLayout(Hawc instrument) {
        super(instrument);
    }
    
    @Override
    public HawcLayout copyFor(Instrument<?> instrument) {
        HawcLayout copy = (HawcLayout) super.copyFor(instrument);
        
        if(pixelSize != null) copy.pixelSize = pixelSize.copy();
        if(subarrayOffset != null) copy.subarrayOffset = Vector2D.copyOf(subarrayOffset);
        if(subarrayOrientation != null) copy.subarrayOrientation = Arrays.copyOf(subarrayOrientation, subarrayOrientation.length);
        if(polZoom != null) copy.polZoom = Arrays.copyOf(polZoom, polZoom.length);
        
        return copy;
    }
    
    @Override
    public Vector2D getPixelSize() { return pixelSize; }
    
    @Override
    public Hawc getInstrument() { return (Hawc) super.getInstrument(); }
    
    @Override
    public void validate() {
        Hawc hawc = getInstrument();
        
        // The subarrays orientations
        subarrayOrientation = new double[Hawc.subarrays];
        subarrayOrientation[Hawc.R0] = hasOption("rotation.r0") ? option("rotation.r0").getDouble() * Unit.deg : 0.0;
        subarrayOrientation[Hawc.R1] = hasOption("rotation.r1") ? option("rotation.r1").getDouble() * Unit.deg : Math.PI;
        subarrayOrientation[Hawc.T0] = hasOption("rotation.t0") ? option("rotation.t0").getDouble() * Unit.deg : 0.0;
        subarrayOrientation[Hawc.T1] = hasOption("rotation.t1") ? option("rotation.t1").getDouble() * Unit.deg : Math.PI;

        // The subarray offsets (after rotation, in pixels)
        subarrayOffset = new Vector2D[Hawc.subarrays];
        subarrayOffset[Hawc.R0] = hasOption("offset.r0") ? option("offset.r0").getVector2D() : new Vector2D();
        subarrayOffset[Hawc.R1] = hasOption("offset.r1") ? option("offset.r1").getVector2D() : new Vector2D(67.03, -39.0);
        subarrayOffset[Hawc.T0] = hasOption("offset.t0") ? option("offset.t0").getVector2D() : new Vector2D();
        subarrayOffset[Hawc.T1] = hasOption("offset.t1") ? option("offset.t1").getVector2D() : new Vector2D(67.03, -39.0);

        // The relative zoom of the polarization planes...
        polZoom = new double[Hawc.polArrays];
        polZoom[Hawc.R_ARRAY] = hasOption("zoom.r") ? option("zoom.r").getDouble() : 1.0;
        polZoom[Hawc.T_ARRAY] = hasOption("zoom.t") ? option("zoom.t").getDouble() : 1.0;
        
        // The default pixelSizes...
        pixelSize = new Vector2D(hawc.array.pixelSize, hawc.array.pixelSize);

        // Set the pixel size...
        if(hasOption("pixelsize")) {
            pixelSize = new Vector2D();
            StringTokenizer tokens = new StringTokenizer(option("pixelsize").getValue(), " \t,:xX");
            pixelSize.setX(Double.parseDouble(tokens.nextToken()) * Unit.arcsec);
            pixelSize.setY(tokens.hasMoreTokens() ? Double.parseDouble(tokens.nextToken()) * Unit.arcsec : pixelSize.x());
        }

        hawc.array.pixelSize = Math.sqrt(pixelSize.x() * pixelSize.y());

        super.validate();
        
        // TODO load bias gains? ...
    }
    

    @Override
    public void setDefaultPixelPositions() {  
        Hawc hawc = getInstrument();


        hawc.info("Boresight pixel from FITS is " + hawc.array.boresightIndex);

        if(hasOption("pcenter")) {
            hawc.array.boresightIndex = option("pcenter").getVector2D(); 
            hawc.info("Boresight override --> " + hawc.array.boresightIndex);
        } 
        else if(Double.isNaN(hawc.array.boresightIndex.x())) {
            hawc.array.boresightIndex = Hawc.defaultBoresightIndex;
            hawc.warning("Missing FITS boresight --> " + hawc.array.boresightIndex);
        }
        Vector2D center = getSIBSPosition(0, 39.0 - hawc.array.boresightIndex.y(), hawc.array.boresightIndex.x());

        for(HawcPixel pixel : hawc) pixel.calcSIBSPosition();

        // Set the pointing center...
        setReferencePosition(center);
    }


    @Override
    public void readRCP(String fileName)  throws IOException {
        super.readRCP(fileName);
        getInstrument().registerConfigFile(fileName);
    }

    Vector2D getSIBSPosition(int sub, double row, double col) {
        Vector2D v = new Vector2D(col, 39.0 - row); // X, Y
        v.rotate(subarrayOrientation[sub]);
        v.add(subarrayOffset[sub]);
        // X is oriented like AZ (tXEL), whereas Y is oriented like -tEL.
        v.scaleX(pixelSize.x());       
        v.scaleY(-pixelSize.y());
        v.scale(polZoom[sub>>1]);
        // v is now in proper tXEL,tEL coordinates...
        return v;
    }



    // TODO... currently treating all subarrays as non-overlapping -- which is valid for point sources...
    @Override
    public void addLocalFixedIndices(int fixedIndex, double radius, List<Integer> toIndex) {
        addLocalFixedIndices(this, fixedIndex, radius, toIndex);
        for(int sub=1; sub < Hawc.subarrays; sub++) {
            final int subOffset = sub * Hawc.subarrayPixels;
            for(int i = toIndex.size(); --i >= 0; ) toIndex.add(toIndex.get(i) + subOffset);
        }
    }


    @Override
    public final int rows() { return Hawc.rows; }

    @Override
    public final int cols() { return Hawc.subarrayCols; }

    
}
