/* BEGIN_HEADER                                              Java TreeView
 *
 * $Author: rqluk $
 * $RCSfile: IntegerMap.java,v $
 * $Revision: 1.1 $
 * $Date: 2006-08-16 19:13:45 $
 * $Name:  $
 *
 * This file is part of Java TreeView
 * Copyright (C) 2001-2014 Alok Saldanha, All Rights Reserved. Modifications Copyright (C) Lawrence Berkeley Lab.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * END_HEADER 
 */
package edu.stanford.genetics.treeview.plugin.dendroview;

import edu.stanford.genetics.treeview.*;

/**
 * This class is a contract for maps between indexes and pixels. It
 * would be an interface, except there are some common routines which
 * are worth implementing in the superclass.
 */
public abstract class IntegerMap implements ConfigNodePersistent
{
    protected int availablepixels;
    protected int maxindex;
    protected int minindex;
    protected ConfigNode root;
    
    public IntegerMap()
    {
        availablepixels = 0;
        maxindex = -1;
        minindex = -1;
        root = new DummyConfigNode(type());
    }

    public void bindConfig(ConfigNode configNode)
    {
        root = configNode;
        root.setAttribute("type", type(), null);
    }

    IntegerMap createMap(String string)
    {
        if (string.equals("Fixed"))
            return new FixedMap();
        if (string.equals("Fill"))
            return new FillMap();
        System.out.println(string + " not found");
        return null;
    }

    /**
     * @return number of pixels available for display
     */     
    public int getAvailablePixels()
    {
        return availablepixels;
    }

    /**
     * @param i pixel for which to find index
     * @return index into array for that pixel
     */
    public abstract int getIndex(int i);
    /**
     * @return maximum index mapped
     */     
    public int getMaxIndex()
    {
        return maxindex;
    }

    // simple accessors
    /**
     * @return minimum index mapped
     */     
    public int getMinIndex()
    {
        return minindex;
    }


    public boolean contains(int i)
    {
        if (i < getMinIndex())
            return false;
        if (i > getMaxIndex())
            return false;
        else
            return true;
    }

    // subclasses implement actual mapping functions
    /**
     * note: if i == maxindex + 1, return the first pixel beyond end of max
     * 
     * @param i the index for which we want the first pixel of
     *
     * @return first pixel corresponding to index
     */
    public abstract int getPixel(int i);

    /** 
     * @param indval the (fractional) index for which we want the pixel.
     * 
     * This is determined by assuming that the actual index corresponds to the middle of the 
     * block of pixels assigned to that index, and then linearly interpolating the unit interval onto the block.
     * 
     * This means that 6.0 would map to the middle of the block, and
     * 6.5 would map to the boundary of the 6 and 7 blocks. Values
     * between 6.0 and 6.5 would be linearly interpolated between
     * those points.
	 *
	 * The relation getPixel(i) == getPixel (i -0.5) should hold.
     */ 
    public int getPixel (double indval) {
	double base = Math.rint(indval);
	double residual = indval - base + .5; // indicates how far into the block to go, from 0.0 - 1.0
	int ibase = (int) base;
	int map = (int) (getPixel(ibase)*(1.0 - residual) + residual * getPixel(ibase + 1));
	//	System.out.println("scale " + getScale() + "got base " + base + " residual " + residual + " maps to " + map);

	return map;
    }

    public int getRequiredPixels()
    {
        return (int)((double)(maxindex - minindex + 1) * getScale());
    }

    // how many pixels per integer, on average?
    /**
     * @return average number of pixels per index. Could be
     * meaningless if non-constant spacing.
     */
    public abstract double getScale();
    /**
     * @return how many of the avaiable pixels are actually used...
     */
    public abstract int getUsedPixels();
    /**
     * @return number of indexes viewable at once
     */
    public abstract int getViewableIndexes();
    /**
     * @param i number of pixels which we can map to. The map will map
     * the index range to pixels 1 to n-1.
     */
    public void setAvailablePixels(int i)
    {
        availablepixels = i;
    }

    /**
     * Set the range of pixels to map to
     *
     * @param i lower bound
     * @param j upper bound
     */
    public void setIndexRange(int i, int j)
    {
        minindex = i;
        maxindex = j;
    }

    public abstract String type();
}
