/*******************************************************************************
 * 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.telescope;



import crush.CRUSH;
import crush.Integration;
import crush.motion.Chopper;
import crush.motion.Chopping;
import jnum.Util;
import jnum.math.Vector2D;


public abstract class GroundBasedIntegration<FrameType extends HorizontalFrame> extends Integration<FrameType> {


    /**
     * 
     */
    private static final long serialVersionUID = 5302265555895118823L;
    public double zenithTau = 0.0;
    
    
    protected GroundBasedIntegration(GroundBasedScan<? extends GroundBasedIntegration<? extends FrameType>> parent) {
        super(parent);
    }
  
    
    @SuppressWarnings("unchecked")
    @Override
    public GroundBasedScan<? extends GroundBasedIntegration<? extends FrameType>> getScan() { 
        return (GroundBasedScan<? extends GroundBasedIntegration<? extends FrameType>>) super.getScan(); 
    }
   
    @Override
    public TelescopeInstrument<?> getInstrument() { return (TelescopeInstrument<?>) super.getInstrument(); }
    
    
    @Override
    public void validate() {
        super.validate();
        
        if(hasOption("tau")) {
            try { setTau(); }
            catch(Exception e) { 
                warning("Problem setting tau: " + e.getMessage()); 
                if(CRUSH.debug) CRUSH.trace(e);
            }
        }     
    }

     
 
    // Try in this order:
    //   1. in-band value, e.g. "0.304"
    //   2. scaling relation, e.g. "225GHz", provided "tau.225GHz" is defined.
    //   
    public void setTau() throws Exception { 
        String spec = option("tau").getValue();

        try { setTau(Double.parseDouble(spec)); }
        catch(Exception notanumber) {
            String id = spec.toLowerCase();
            if(hasOption("tau." + id)) setTau(id, option("tau." + id).getDouble());
            else throw new IllegalArgumentException("Supplied tau is neither a number nor a known subtype.");
        }
    }

    public void setTau(String id, double value) {
        Vector2D t = getTauCoefficients(id);
        Vector2D inband = getTauCoefficients(getInstrument().getName());
        try { setZenithTau(inband.x() / t.x() * (value - t.y()) + inband.y()); }
        catch(Exception e) { 
            warning("Could not set zenith tau: " + e.getMessage()); 
            if(CRUSH.debug) CRUSH.trace(e);
        }
    }

    public double getTau(String id, double value) {
        Vector2D t = getTauCoefficients(id);
        Vector2D inband = getTauCoefficients(getInstrument().getName());
        return t.x() / inband.x() * (value - inband.y()) + t.y();
    }

    public double getTau(String id) {
        return getTau(id, zenithTau);
    }

    public Vector2D getTauCoefficients(String id) {
        String key = "tau." + id.toLowerCase();

        if(!hasOption(key + ".a")) throw new IllegalStateException(key + " has no scaling relation.");

        Vector2D coeff = new Vector2D();
        coeff.setX(option(key + ".a").getDouble());
        if(hasOption(key + ".b")) coeff.setY(option(key + ".b").getDouble());

        return coeff;
    }

    public void setTau(final double value) throws Exception {   
        try { setZenithTau(value); }
        catch(NumberFormatException e) {}
    }

    public void setZenithTau(final double value) {
        info("Setting zenith tau to " + Util.f3.format(value));
        zenithTau = value;
        validParallelStream().forEach(f ->f.setZenithTau(value));
    }
    
    
    @Override
    public Object getTableEntry(String name) {
        if(name.equals("zenithtau")) return zenithTau;
        if(name.equals("tau")) return zenithTau / Math.cos(getScan().horizontal.EL());
        if(name.startsWith("tau.")) return getTau(name.substring(4).toLowerCase());
        if(name.startsWith("chop") && this instanceof Chopping) {
            Chopper chopper = ((Chopping) this).getChopper();
            if(chopper == null) return null;
            return chopper.getTableEntry(name);
        }
        
        return super.getTableEntry(name);
    }

    
}
