package calhoun.gebo.internal.db;

import java.awt.Color;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import calhoun.bean.LinkageGroup;
import calhoun.gebo.db.AbstractRangeTrack;
import calhoun.gebo.model.AltChromoRange;
import calhoun.gebo.model.AltContigRange;
import calhoun.gebo.model.Criterion;
import calhoun.gebo.model.Range;
import calhoun.gebo.model.SequenceGroup;
import calhoun.gebo.model.SimpleRange;
import calhoun.gebo.ui.TrackStyle;
import calhoun.gebo.util.C;
import calhoun.gebo.util.Informer;
import calhoun.gebo.util.M;
import calhoun.gebo.util.Q;

public class CalhounRangeTrack extends AbstractRangeTrack {

    private static Informer I = new Informer(CalhounRangeTrack.class);

    public CalhounRangeTrack(CalhounTrack calhounTrack) {
        super(calhounTrack.getId(), calhounTrack.getLabel(), calhounTrack
                .getOrder(), calhounTrack.getColor());
        m_calhounTrack = calhounTrack;
        loadPrefs();
    }

    public void loadPrefs() {
        I.warn("loading  prefs...");
        gaga();
    }

    public String getPrefPath() {
        return m_calhounTrack.getPrefPath() + "_rt";
    }

    private boolean needsChromoData(String sequenceId) {
        if (getStyle().equals(TrackStyle.CHROMO_MAP)) {
            if (m_rangesBySequenceId != null) {
                if (m_rangesBySequenceId.containsKey(sequenceId)) {
                    if (m_rangesBySequenceId.get(sequenceId).size() > 0
                            && m_rangesBySequenceId.get(sequenceId).get(0) instanceof AltChromoRange) {
                        return false;
                    }
                }
            }
            return true;
        }
        return false;
    }

    private boolean needsContigData(String sequenceId) {
        if (getStyle().equals(TrackStyle.CONTIG_MAP)) {
            if (m_rangesBySequenceId != null) {
                if (m_rangesBySequenceId.containsKey(sequenceId)) {
                    if (m_rangesBySequenceId.get(sequenceId).size() > 0
                            && m_rangesBySequenceId.get(sequenceId).get(0) instanceof AltContigRange) {
                        return false;
                    }
                }
            }
            return true;
        }
        return false;
    }

    public List<Range> getRanges(SequenceGroup sequenceGroup, String sequenceId) {
        // not an attractive method.
        boolean needsChromoData = needsChromoData(sequenceId);
        boolean needsContigData = needsContigData(sequenceId);
        boolean needsEither = needsChromoData || needsContigData;
        if (m_rangeCountBySequenceGroup == null || needsChromoData
                || needsContigData) {
            m_rangeCountBySequenceGroup = new HashMap();
        }
        if (m_rangesBySequenceId == null || needsChromoData || needsContigData) {
            m_rangesBySequenceId = new HashMap();
        }
        if (!(m_rangesBySequenceId.containsKey(sequenceId))) {
            setDataChanged(false);
            List<Range> ranges = new ArrayList<Range>();
            String chromoDataSql = needsEither ? ",ap_alt_sequence_id" : "";
            String sql = m_calhounTrack.getTrackType()
                    .getDensityFeatureSelect()
                    + " where ap_analysis_event_id = ? and ap_subclass = ? "
                    + " and ap_ontology_term_id = ? and ap_sequence_id = ? ";
            sql = sql.replaceAll("FROM", chromoDataSql + " FROM");
            sql += m_calhounTrack.getCalhounTrackManager()
                    .generateTrackSpecificCriteriaSql(m_calhounTrack);
            I.warn("REINOS: " + sql);
            try {
                Q.startStopWatch(sql);
                PreparedStatement statement = CalhounTrackManager.getInstance()
                        .getConnection().prepareStatement(sql);
                statement.setString(1, m_calhounTrack.getAnalysisEventId());
                statement.setString(2, m_calhounTrack.getSubclass());
                statement
                        .setString(3, m_calhounTrack.getOntologyTerm().getId());
                statement.setString(4, sequenceId);
                ResultSet resultSet = statement.executeQuery();
                while (resultSet.next()) {
                    int start = resultSet.getInt(1);
                    int stop = resultSet.getInt(2);
                    Range r = null;
                    if (needsChromoData) {
                        r = createAltChromoRange(start, stop, resultSet
                                .getString(3));
                    } else if (needsContigData) {
                        r = createAltContigRange(start, stop, resultSet
                                .getString(3));
                    } else {
                        r = new SimpleRange(start, stop);
                    }
                    ranges.add(r);
                }
                statement.close();
                resultSet.close();
                Collections.sort(ranges);
                m_rangesBySequenceId.put(sequenceId, ranges);
                M.mapPlus(m_rangeCountBySequenceGroup, sequenceGroup, ranges
                        .size());
            } catch (SQLException e) {
                I.error("Could not get Track Types. \nsql: " + sql, e);
                return new ArrayList<Range>();
            } finally {
                Q.stopStopWatch(sql);
            }
        } 
        return m_rangesBySequenceId.get(sequenceId);
    }

    private AltContigRange createAltContigRange(int start, int stop,
            String contigId) {
        return new AltContigRange(start, stop, CalhounSequenceManager
                .getInstance().getSequence(contigId));
    }

    private AltChromoRange createAltChromoRange(int start, int stop,
            String altSequenceId) {
        LinkageGroup lg = CalhounSequenceManager.getInstance()
                .internLinkageGroup(altSequenceId);
        if (lg != null) {
            // I.warn("Adding linkage group " + lg.getName() );
            m_linkageGroupSet.add(lg);
        }
        return new AltChromoRange(start, stop, lg);
    }

    public String getSourceString() {
        return m_calhounTrack.getSourceString();
    }

    // defer to m_calhounTrack for these properties.

    public Color getColor() {
        return m_calhounTrack.getColor();
    }

    public void setColor(Color color) {
        m_calhounTrack.setColor(color);
    }

    public int getOrder() {
        return m_calhounTrack.getOrder();
    }

    public void setOrder(int order) {
        m_calhounTrack.setOrder(order);
    }

    public Criterion[] getCriteria() {
        return m_calhounTrack.getCriteria();
    }

    public boolean hasCriterion(String key) {
        return m_calhounTrack.hasCriterion(key);
    }

    public void setCriteria(Criterion[] criteria) {
        m_calhounTrack.setCriteria(criteria);
    }

    public Set<LinkageGroup> getLinkageGroupSet() {
        return m_linkageGroupSet;
    }

    public List<LinkageGroup> getOrderedLinkageGroups() {
        List<LinkageGroup> linkageGroupList = C.list();
        I.warn("linkageGroupSet.size: " + m_linkageGroupSet.size());
        linkageGroupList.addAll(m_linkageGroupSet);
        I.warn("linkagegroupList.size: " + linkageGroupList.size());
        Collections.sort(linkageGroupList, new LinkageGroupComparator());
        return linkageGroupList;
    }

    private Set<LinkageGroup> m_linkageGroupSet = C.set();
    private CalhounTrack m_calhounTrack;

    class LinkageGroupComparator implements Comparator {
        public int compare(Object o1, Object o2) {
            LinkageGroup l1 = (LinkageGroup) o1;
            LinkageGroup l2 = (LinkageGroup) o2;
            return l1.getOrder().compareTo(l2.getOrder());
        }
    }

}
