/*
 * Copyright (c) 2007-2011 by The Broad Institute of MIT and Harvard.  All Rights Reserved.
 *
 * This software is licensed under the terms of the GNU Lesser General Public License (LGPL),
 * Version 2.1 which is available at http://www.opensource.org/licenses/lgpl-2.1.php.
 *
 * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR
 * WARRANTES OF ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING,
 * WITHOUT LIMITATION, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, WHETHER
 * OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR RESPECTIVE
 * TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES
 * OF ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
 * ECONOMIC DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER
 * THE BROAD OR MIT SHALL BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT
 * SHALL KNOW OF THE POSSIBILITY OF THE FOREGOING.
 */


/*
* GisticScoreList.java
*
* Created on June 21, 2007, 8:29 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.broad.igv.track;

//~--- non-JDK imports --------------------------------------------------------

import org.apache.log4j.Logger;
import org.broad.igv.feature.IGVFeature;
import org.broad.igv.feature.FeatureUtils;
import org.broad.igv.feature.GisticScore;
import org.broad.igv.feature.LocusScore;
import org.broad.igv.renderer.ColorScale;
import org.broad.igv.renderer.DataRange;
import org.broad.igv.renderer.GisticTrackRenderer;
import org.broad.igv.renderer.Renderer;
import org.broad.igv.ui.panel.ReferenceFrame;
import org.broad.igv.util.ResourceLocator;

import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author jrobinso
 */
public class GisticTrack extends AbstractTrack {

    private static Logger log = Logger.getLogger(GisticTrack.class);

    private static final int DEFAULT_HEIGHT = 50;
    
    private double maxQValue = 0;

    private double maxGScore = 0;

    /**
     * Map of chromosome -> sorted list of scores
     */
    Map<String, List<GisticScore>> ampScoreMap;

    Map<String, List<GisticScore>> delScoreMap;

    GisticTrackRenderer renderer;
    ColorScale colorScale;

    /**
     * Creates a new instance of GisticScoreList
     *
     * @param locator
     */
    public GisticTrack(ResourceLocator locator) {
        super(locator);
        ampScoreMap = new HashMap<String, List<GisticScore>>();
        delScoreMap = new HashMap<String, List<GisticScore>>();
        renderer = new GisticTrackRenderer();
        setHeight(DEFAULT_HEIGHT);
        renderer = new GisticTrackRenderer();
    }

    @Override
    public int getMinimumHeight() {
        return 25;
    }




    /**
     * Method description
     *
     * @param scores
     */
    public void setScores(List<GisticScore> scores) {

        for (GisticScore score : scores) {
            String chr = score.getChromosome();
            if (score.getType() == GisticScore.Type.AMP) {
                addScore(score, chr, ampScoreMap);
            } else {
                addScore(score, chr, delScoreMap);

            }
        }
        updateMaxValues(scores);
    }

    protected void addScore(GisticScore score, String chr, Map<String, List<GisticScore>> map) {
        List<GisticScore> scoreList = map.get(chr);
        if (scoreList == null) {
            scoreList = new ArrayList();
            map.put(chr, scoreList);
        }
        scoreList.add(score);
    }

    private void updateMaxValues(List<GisticScore> scores) {
        for (GisticScore score : scores) {
            if (!Double.isInfinite(maxGScore) && score.getGScore() > maxGScore) {
                maxGScore = score.getGScore();
            }
            if (!Double.isInfinite(maxQValue) && score.getQValue() > maxQValue) {
                maxQValue = score.getQValue();
            }
        }
        setDataRange(new DataRange(0, 0, (float) maxQValue));
    }

    /**
     * Method description
     *
     * @param chr
     * @return
     */
    public List<GisticScore> getAmpScores(String chr) {
        return ampScoreMap.get(chr);
    }

    /**
     * Method description
     *
     * @param chr
     * @return
     */
    public List<GisticScore> getDelScores(String chr) {
        return delScoreMap.get(chr);
    }

    /**
     * Return the score with the maximum "G Score" over the specified region.
     * Assumes the scores are sorted by location
     *
     * @param chr
     * @param start
     * @param end
     * @return
     */
    public GisticScore getMaxForRegion(String chr, long start, long end) {

        List<GisticScore> scores = ampScoreMap.get(chr);
        if ((scores == null) || scores.isEmpty()) {
            return null;
        }

        GisticScore maxScore = null;
        for (GisticScore score : scores) {
            if (maxScore == null) {
                if ((score.getStart() >= start)
                        || ((start >= score.getStart()) && (start <= score.getEnd()))) {
                    maxScore = score;
                }
            } else if (score.getStart() > end) {
                break;
            } else {
                if (score.getGScore() > maxScore.getGScore()) {
                    maxScore = score;
                }
            }
        }

        // If we haven't found bounding scores yet the region is to the right off the last
        // score.  Use the last score for the region.  This should be a rare case and should
        // probably be logged.
        return (maxScore == null) ? ((GisticScore) scores.get(scores.size() - 1)) : maxScore;

    }

    /**
     * Method description
     *
     * @return
     */
    public double getMaxQValue() {
        return maxQValue;
    }

    /**
     * Method description
     *
     * @return
     */
    public double getMaxGScore() {
        return maxGScore;
    }

    /**
     * Method description
     *
     * @param context
     * @param rect
     */
    public void render(RenderContext context, Rectangle rect) {
        if (renderer == null) {
            log.error("Null renderer !!");

        } else {
            renderer.render(this, context, rect);
        }
    }

    double dataMax = -1;

    /**
     * Method description
     *
     * @return
     */
    public double getDataMax() {
        if (dataMax < 0) {
            dataMax = getMaxGScore();
        }
        return dataMax;
    }

    /**
     * Method description
     *
     * @param type
     */
    public void setWindowFunction(WindowFunction type) {

        // ignored
    }

    /**
     * Method description
     *
     * @return
     */
    public WindowFunction getWindowFunction() {
        return WindowFunction.median;
    }

    /**
     * Method description
     *
     * @param chr
     * @return
     */
    public double getMedian(String chr) {
        return 1.0;
    }

    /**
     * Method description
     *
     * @return
     */
    public boolean isLogNormalized() {
        return true;
    }

    /**
     * This method is required for the interface, but will not be called.
     *
     * @param chr
     * @param startLocation
     * @param endLocation
     * @return
     */
    public List<IGVFeature> getFeatures(String chr, int startLocation, int endLocation) {
        return null;
    }

    /**
     * This method is required for the interface, but will not be called.
     *
     * @param chr
     * @param startLocation
     * @param endLocation
     * @param zoom
     * @return
     */
    public List<LocusScore> getSummaryScores(String chr, int startLocation, int endLocation,
                                             int zoom) {

        List<LocusScore> ss = new ArrayList();
        List<GisticScore> ampScores = ampScoreMap.get(chr);
        if (ampScores != null) {
            ss.addAll(ampScores);
        }
        List<GisticScore> delScores = delScoreMap.get(chr);
        if (delScores != null) {
            ss.addAll(delScores);
        }
        return ss;

        // return getFeatures(chr, startLocation, endLocation);
    }

    /**
     * This method is required for the interface, but will not be called.
     *
     * @param chr
     * @param start
     * @param end
     * @param zoom
     * @param type
     * @param frame
     * @return
     */
    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type, ReferenceFrame frame) {
        return 0;
    }


    /**
     * Method description
     *
     * @param chr
     * @param position
     * @param ignore
     * @return
     */
    public String getValueStringAt(String chr, double position, int ignore, ReferenceFrame frame) {

        // give a 2 pixel window, otherwise very narrow features will be missed.
        double bpPerPixel = frame.getScale();
        double minWidth = 2 * bpPerPixel;    /*
                                              */

        LocusScore amp = null;
        List<GisticScore> ampScores = ampScoreMap.get(chr);
        if (ampScores != null) {
            amp = (LocusScore) FeatureUtils.getFeatureAt(position, 0, ampScores);
        }

        LocusScore del = null;
        List<GisticScore> delScores = delScoreMap.get(chr);
        if (delScores != null) {
            del = (LocusScore) FeatureUtils.getFeatureAt(position, 0, delScores);
        }

        if ((amp == null) && (del == null)) {
            return "";
        } else {
            StringBuffer buf = new StringBuffer();
            if (amp != null) {
                buf.append("Amplification score: " + amp.getScore());
            }
            if (del != null) {
                buf.append("<br>Deletion score: " + del.getScore());
            }
            return buf.toString();

        }
    }

    /**
     * Method description
     *
     * @param chr
     * @param startLocation
     * @param endLocation
     * @return
     */
    public List<List<IGVFeature>> getFeaturesByLevels(String chr, int startLocation, int endLocation) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Method description
     *
     * @return
     */
    public int getNumberOfFeatureLevels() {
        return 1;
    }

    /**
     * Method description
     *
     * @param isMultiLevel
     */
    public void setMultiLevelFeatures(boolean isMultiLevel) {

        // ignore
    }

    /**
     * Method description
     *
     * @return
     */
    public boolean isMuliLevelFeatures() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Method description
     *
     * @param x
     * @param y
     */
    public boolean handleClick(int x, int y) {

        // Ignore
        return false;
    }


    public Color getColor() {
        return null;
    }

    public void setColor(Color color) {
        // Not used
    }

    public Color getAltColor() {
        return null;
    }

    public void setAltColor(Color color) {
        // Not used
    }

    // GisticTrack does not expose its renderer
    public Renderer getRenderer() {
        return null;
    }


}
