/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2015 Broad Institute
 *
 * 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.
 */


/*
* 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.broad.igv.logging.*;
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.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 = LogManager.getLogger(GisticTrack.class);

    private static final int DEFAULT_HEIGHT = 50;

    private double maxQValue = 0;

    private double maxGScore = 0;

    private boolean hasScores = false;

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

    Map<String, List<GisticScore>> delScoreMap;

    GisticTrackRenderer renderer;

    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();
        setSortable(false);

    }

    public GisticTrack() {
    }

    @Override
    public boolean isReadyToPaint(ReferenceFrame frame) {
        return hasScores;
    }

    @Override
    public void load(ReferenceFrame referenceFrame) {
        //
    }

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


    public void setScores(List<GisticScore> scores) {

        this.hasScores = true;

        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 frameName
     * @return
     */
    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type, String frameName) {
        return 0;
    }


    /**
     * Method description
     *
     * @param chr
     * @param position
     * @param mouseX
     *@param ignore  @return
     */
    public String getValueStringAt(String chr, double position, int mouseX, 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.");
    }


    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;
    }


}
