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

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.broad.igv.sam;

import org.broad.igv.feature.Locus;
import org.broad.igv.feature.LocusScore;
import org.broad.igv.feature.genome.Genome;
import org.broad.igv.renderer.DataRange;
import org.broad.igv.renderer.SequenceRenderer;
import org.broad.igv.tdf.TDFDataSource;
import org.broad.igv.tdf.TDFReader;
import org.broad.igv.track.*;
import org.broad.igv.ui.panel.IGVPopupMenu;
import org.broad.igv.ui.panel.ReferenceFrame;
import org.broad.igv.util.ResourceLocator;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Developmental track to explore display of conservation scores.
 *
 * @author jrobinso
 */
public class EWigTrack extends AbstractTrack {

    //    double dataMax;
    char[] nucleotides = {'A', 'C', 'G', 'T'};
    public static Color grey1 = new Color(230, 230, 230);
    Map<Character, TDFDataSource> baseSources;
    TDFDataSource tdfSource;

    // TODO -- memory leak.  This needs to get cleared when the gene list changes
    private HashMap<String, LoadedInterval> loadedIntervalCache = new HashMap(200);

    public EWigTrack(ResourceLocator locator, Genome genome) {
        super(locator);

        TDFReader reader = TDFReader.getReader(locator.getPath());
        tdfSource = new TDFDataSource(reader, 4, "Pi", genome);

        setDataRange(new DataRange(0, 0, 10));
        baseSources = new HashMap();
        for (int i = 0; i < 4; i++) {
            TDFDataSource src = new TDFDataSource(reader, i, Character.toString(nucleotides[i]), genome);
            baseSources.put(nucleotides[i], src);
        }
    }

    @Override
    public boolean isReadyToPaint(ReferenceFrame frame) {

        final String chr = frame.getChrName();
        final int start = (int) frame.getOrigin();
        final int end = (int) frame.getEnd();
        final int zoom = frame.getZoom();

        LoadedInterval loadedInterval = loadedIntervalCache.get(frame.getName());

        return (loadedInterval != null && loadedInterval.contains(chr, start, end, zoom));
    }

    @Override
    public void load(ReferenceFrame context) {

        final String chr = context.getChrName();
        int start = (int) context.getOrigin();
        int end = (int) context.getEnd();
        final int zoom = context.getZoom();

        // Expand start and end a bit for panning
        int w = end - start;
        start -= w / 2;
        end += w / 2;

        List<LocusScore> scores = tdfSource.getSummaryScoresForRange(chr,
                (int) start,
                end,
                zoom);

        Map<Character, List<LocusScore>> nScores = new HashMap();
        for (Character c : nucleotides) {
            nScores.put(c, baseSources.get(c).getSummaryScoresForRange(chr,
                    (int) start,
                    end,
                    zoom));
        }

        loadedIntervalCache.put(context.getName(), new LoadedInterval(chr, start, end, zoom, scores, nScores));
    }

    public void render(RenderContext context, Rectangle rect) {
        paint(context, rect);
    }

    public boolean isLogNormalized() {
        return false;
    }

    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type, String frameName) {
        return 0;
    }

    private void paint(RenderContext context, Rectangle rect) {

        ReferenceFrame frame = context.getReferenceFrame();
        final String chr = frame.getChrName();
        final int start = (int) frame.getOrigin();
        final int end = (int) frame.getEnd();
        final int zoom = frame.getZoom();

        LoadedInterval loadedInterval = loadedIntervalCache.get(frame.getName());

        if (loadedInterval == null || !loadedInterval.contains(chr, start, end, zoom)) {
            return;
        }


        // The total score
        List<LocusScore> scores = loadedInterval.scores;
        Map<Character, List<LocusScore>> nScores = loadedInterval.baseScores;

        Map<Character, Color> nucleotideColors = SequenceRenderer.getNucleotideColors();

        for (int idx = 0; idx < scores.size(); idx++) {

            LocusScore score = scores.get(idx);
            int startPosition = score.getStart();
            int endPosition = score.getEnd();

            int pX = (int) (rect.getX() + (startPosition - context.getOrigin()) / context.getScale());
            int dX = Math.max(1,
                    (int) (rect.getX() + (endPosition - context.getOrigin()) / context.getScale()) - pX);
            if (dX > 4) {
                dX -= 2;
                pX++;
            }

            if (pX + dX < 0) {
                continue;
            } else if (pX > context.getVisibleRect().getMaxX()) {
                break;
            }


            float totalCount = score.getScore();
            int pY = (int) rect.getMaxY() - 1;

            float dataMax = getDataRange().getMaximum();

            double height = (totalCount * rect.getHeight() / dataMax);
            height = Math.min(height, rect.height - 1);

            for (char c : nucleotides) {
                try {
                    LocusScore ns = nScores.get(c).get(idx);
                    float count = ns.getScore() * totalCount;

                    //pY = drawBar(context, idx, rect, dataMax, pY, pX, dX, c, count);
                    Graphics2D tGraphics = context.getGraphic2DForColor(nucleotideColors.get(c));

                    int h = (int) Math.round(count * height / totalCount);
                    h = Math.min(pY - rect.y, h);
                    int baseY = (int) (pY - h);

                    if (h > 0) {
                        tGraphics.fillRect(pX, baseY, dX, h);
                    }
                    pY = baseY;
                } catch (Exception e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                }
            }

        }

        // Draw border
        context.getGraphic2DForColor(Color.gray).drawLine(
                rect.x, rect.y + rect.height,
                rect.x + rect.width, rect.y + rect.height);

        // Draw scale
        /*
        DataRange range = getDataRange();
        if (range != null) {
            Graphics2D g = context.getGraphic2DForColor(Color.black);
            Font font = g.getFont();
            Font smallFont = FontManager.getScalableFont(8);
            try {
                g.setFont(smallFont);
                String scale = "Scale: " + (int) range.getMinimum() + " - " +
                        (int) range.getMaximum();
                g.drawString(scale, rect.x + 10, rect.y + 10);

            } finally {
                g.setFont(font);
            }
        }*/

    }

    public JPopupMenu getPopupMenu(
            final MouseEvent evt) {

        JPopupMenu popupMenu = new IGVPopupMenu();

        JLabel popupTitle = new JLabel("  " + getName(), JLabel.CENTER);

        Font newFont = popupMenu.getFont().deriveFont(Font.BOLD, 12);
        popupTitle.setFont(newFont);
        if (popupTitle != null) {
            popupMenu.add(popupTitle);
        }

        popupMenu.addSeparator();

        // addSortMenuItem(popupMenu);
        // addPackMenuItem(popupMenu);
        // addShadeBaseMenuItem(popupMenu);
        // addCopyToClipboardItem(popupMenu, evt);
        // addGoToMate(popupMenu, evt);
        // popupMenu.addSeparator();


        //JLabel trackSettingsHeading = new JLabel("  Track Settings",
        //        JLabel.LEFT);
        //trackSettingsHeading.setFont(newFont);

        //popupMenu.add(trackSettingsHeading);

        ArrayList<Track> tmp = new ArrayList();
        tmp.add(this);
        popupMenu.add(TrackMenuUtils.getTrackRenameItem(tmp));
        popupMenu.add(TrackMenuUtils.getChangeTrackHeightItem(tmp));
        popupMenu.add(TrackMenuUtils.getDataRangeItem(tmp));


        return popupMenu;
    }

//    public String getValueStringAt(String chr, double position, int y, ReferenceFrame frame) {
//        return null;
//    }

    static public class LoadedInterval {


        Locus range;
        private List<LocusScore> scores;
        int zoom;
        Map<Character, List<LocusScore>> baseScores;

        public LoadedInterval(String chr, int start, int end, int zoom,
                              List<LocusScore> scores,
                              Map<Character, List<LocusScore>> baseScores) {

            range = new Locus(chr, start, end);
            this.zoom = zoom;
            this.scores = scores;
            this.baseScores = baseScores;
        }

        public boolean contains(String chr, int start, int end, int zoom) {
            return this.zoom == zoom && range.contains(chr, start, end);
        }

    }
}
