/*
 * Copyright 2009 Lari Natri <lari.natri@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


package compid.report;

import compid.data.DAOException;
import compid.data.DAOFactory;
import compid.data.QueryHandler;
import compid.data.tuples.PeptideHit;
import compid.data.PeptideSequenceWithHits;
import compid.report.CommonPeptidesReporter.CommonPeptidesReporterParams;
import net.aavalla.utils.jobpool.parameters.FieldAnnotations;
import net.aavalla.utils.jobpool.parameters.validators.StringValidator;
import net.aavalla.utils.general.BasicStatNumbers;
import net.aavalla.utils.general.RetrievalHashSet;
import net.aavalla.utils.gui.twovenn.VennParameters;
import net.aavalla.utils.jobpool.JobCategory;
import java.util.ArrayList;

/**
 *
 * @author Lari Natri
 */
public final class CommonPeptidesReporter extends Reporter<CommonPeptidesReporterParams> {

    public final class CommonPeptidesReporterParams extends ReporterParams {
        @FieldAnnotations(
            name="Report title",
            validator=StringValidator.class,
            validatorSettings={"1","64",FieldAnnotations.NULL})
        private String reportTitle = "Common Peptides";

        @FieldAnnotations(
            name="Left datasets (X)",
            description="Datasets which will be combined (and compared to right datasets if any chosen)",
            validator=DatasetCollectionNotEmptyValidator.class)
        private DatasetCollection left;

        @FieldAnnotations(
            name="Right datasets (Y)",
            description="Datasets which will be combined and compared to left datasets")
        private DatasetCollection right;

        @FieldAnnotations(name="Create list pages",
            description="Create lists of unique and common peptides (recommended)")
        private boolean includeLists = true;

        @FieldAnnotations(
            name="Only assigned peptides",
            description="If selected, only peptides assigned to proteins will be included.")
        private boolean onlyPepsInProts = true;

        public CommonPeptidesReporterParams() {
            this.setDescription("Creates a peptide report.");
            this.setHelpID("peptides");
        }
    }


    public CommonPeptidesReporter() {
        super();
        this.addCategory(JobCategory.MEMORY);
        this.addCategory(JobCategory.DB_READ);
        this.addConflictingCategory(JobCategory.DB_WRITE);
        this.setName("Peptides reporter");
    }

    @Override
    protected CommonPeptidesReporterParams getNonfinalEditableParameters() {
        return new CommonPeptidesReporterParams();
    }

   
    @Override
    public ReportModel createReport(CommonPeptidesReporterParams p) throws DAOException
    {
        boolean onlyLeft;

        if (p.right==null || p.right.getCollection()==null || p.right.getCollection().size()<1) {
            onlyLeft = true;
        } else {
            onlyLeft = false;
        }
        
        String dsLegend;
        String subtitle;
        String summaryRowTitle;
        String uniqueLeftPageTitle;

        if (onlyLeft) {
            dsLegend = p.left.getName();
            subtitle = (p.onlyPepsInProts ? "Peptides (assigned)" : "Peptides (all)");
            summaryRowTitle = p.left.getName();
            uniqueLeftPageTitle = "Peptide List";
        } else {
            dsLegend = "X = {"+p.left.getName()+"}, Y= {"+p.right.getName()+"}";
            subtitle = (p.onlyPepsInProts ? "Common Peptides (assigned)" : "Common Peptides (all)");
            summaryRowTitle = "X="+p.left.getName()+", Y="+p.right.getName();
            uniqueLeftPageTitle = "Unique in X";
        }


        ReportModel report = new ReportModel(p.reportTitle,subtitle+": "+dsLegend,p);

        ReportPageModel summaryPage =  new ReportPageModel("Summary",subtitle);

        summaryPage.addColumn("");
        
        if (onlyLeft) {
            summaryPage.addColumn("Peptides");
        } else {
            summaryPage.addColumn("X: Unique");
            summaryPage.addColumn("(X, Y): Common");
            summaryPage.addColumn("Y: Unique");
            summaryPage.addColumn("Sum");
        }
        
        QueryHandler qh = DAOFactory.getInstance().getQueryHandler();
        
        RetrievalHashSet<PeptideSequenceWithHits> leftPeps = null;
        RetrievalHashSet<PeptideSequenceWithHits> rightPeps = null;

       


        leftPeps = qh.getDistinctPepsInDataSets(p.left.getCollection(),
                                                p.onlyPepsInProts);

        if (!onlyLeft) {
            rightPeps = qh.getDistinctPepsInDataSets(p.right.getCollection(),
                                                     p.onlyPepsInProts);
        }

        ReportPageModel commonPeps = new ReportPageModel("Common", dsLegend);
        ReportPageModel uniqueLPeps = new ReportPageModel(uniqueLeftPageTitle, dsLegend);
        ReportPageModel uniqueRPeps = new ReportPageModel("Unique in Y", dsLegend);

        if (p.includeLists) {
            commonPeps.addColumn("Peptide");
            commonPeps.addColumn("X, Hit count");
            commonPeps.addColumn("X, Best score");
            commonPeps.addColumn("X, Best query");
            commonPeps.addColumn("X, Avg score");
            commonPeps.addColumn("X, all scores");
            commonPeps.addColumn("X, all queries");
            commonPeps.addColumn("Y, Hit count");
            commonPeps.addColumn("Y, Best score");
            commonPeps.addColumn("Y, Best query");
            commonPeps.addColumn("Y, Avg score");
            commonPeps.addColumn("Y, all scores");
            commonPeps.addColumn("Y, all queries");
            commonPeps.addColumn("Compat score (X,Y)");

            uniqueLPeps.addColumn("Peptide"); uniqueRPeps.addColumn("Peptide");
            uniqueLPeps.addColumn("Hit count"); uniqueRPeps.addColumn("Hit count");
            uniqueLPeps.addColumn("Best score"); uniqueRPeps.addColumn("Best score");
            uniqueLPeps.addColumn("Best query"); uniqueRPeps.addColumn("Best query");
            uniqueLPeps.addColumn("Avg score"); uniqueRPeps.addColumn("Avg score");
            uniqueLPeps.addColumn("all scores"); uniqueRPeps.addColumn("all scores");
            uniqueLPeps.addColumn("all queries"); uniqueRPeps.addColumn("all queries");
            uniqueLPeps.addColumn("Compat score"); uniqueRPeps.addColumn("Compat score");
        }

        // find common peptides
        int commonCount = 0;
        for (PeptideSequenceWithHits pepL : leftPeps) {
            if (!onlyLeft && rightPeps.contains(pepL)) {
                ++commonCount;
                if (p.includeLists) {
                    PeptideSequenceWithHits pepR = rightPeps.getKey(pepL);
                    ArrayList<Object> comPepsRow = new ArrayList<Object>(14);

                    comPepsRow.add(pepL.getSequence().toString());
                    comPepsRow.add(pepL.getHitCount());
                    comPepsRow.add(pepL.getGoodRepresentingHit().getCorrectScore());
                    comPepsRow.add(pepL.getGoodRepresentingHit().getQuery().getScanTitle());
                    ArrayList<Double> scores = new ArrayList<Double>(pepL.getHitCount());
                    String scoreList = "";
                    String queryList = "";
                    for (PeptideHit ph : pepL.getHits()) {
                        scores.add(ph.getCorrectScore());
                        scoreList+=ph.getCorrectScore()+"; ";
                        queryList+=ph.getQuery().getScanTitle()+"; ";
                    }
                    if (scoreList.endsWith("; ")) scoreList = scoreList.substring(0,scoreList.length()-2);
                    if (queryList.endsWith("; ")) queryList = queryList.substring(0,queryList.length()-2);

                    comPepsRow.add((new BasicStatNumbers<Double>(scores)).mean);
                    comPepsRow.add(scoreList);
                    comPepsRow.add(queryList);
                    comPepsRow.add(pepR.getHitCount());
                    comPepsRow.add(pepR.getGoodRepresentingHit().getCorrectScore());
                    comPepsRow.add(pepR.getGoodRepresentingHit().getQuery().getScanTitle());
                    scores = new ArrayList<Double>(pepR.getHitCount());
                    scoreList = "";
                    queryList = "";
                    for (PeptideHit ph : pepR.getHits()) {
                        scores.add(ph.getCorrectScore());
                        scoreList+=ph.getCorrectScore()+"; ";
                        queryList+=ph.getQuery().getScanTitle()+"; ";
                    }
                    if (scoreList.endsWith("; ")) scoreList = scoreList.substring(0,scoreList.length()-2);
                    if (queryList.endsWith("; ")) queryList = queryList.substring(0,queryList.length()-2);
                    comPepsRow.add((new BasicStatNumbers<Double>(scores)).mean);
                    comPepsRow.add(scoreList);
                    comPepsRow.add(queryList);
                    comPepsRow.add((pepL.getGoodRepresentingHit().getComparableConfidence()
                            + pepR.getGoodRepresentingHit().getComparableConfidence()) / 2d);
                    commonPeps.addRow(comPepsRow);
                }

            } else if (p.includeLists) { // rightPeps.contains(pepL)==false

                ArrayList<Object> uniqueLPepsRow = new ArrayList<Object>(8);

                uniqueLPepsRow.add(pepL.getSequence().toString());
                uniqueLPepsRow.add(pepL.getHitCount());
                uniqueLPepsRow.add(pepL.getGoodRepresentingHit().getCorrectScore());
                uniqueLPepsRow.add(pepL.getGoodRepresentingHit().getQuery().getScanTitle());
                ArrayList<Double> scores = new ArrayList<Double>(pepL.getHitCount());
                String scoreList = "";
                String queryList = "";
                for (PeptideHit ph : pepL.getHits()) {
                    scores.add(ph.getCorrectScore());
                    scoreList+=ph.getCorrectScore()+"; ";
                    queryList+=ph.getQuery().getScanTitle()+"; ";
                }
                if (scoreList.endsWith("; ")) scoreList = scoreList.substring(0,scoreList.length()-2);
                if (queryList.endsWith("; ")) queryList = queryList.substring(0,queryList.length()-2);

                uniqueLPepsRow.add((new BasicStatNumbers<Double>(scores)).mean);
                uniqueLPepsRow.add(scoreList);
                uniqueLPepsRow.add(queryList);
                uniqueLPepsRow.add(pepL.getGoodRepresentingHit().getComparableConfidence());
                uniqueLPeps.addRow(uniqueLPepsRow);
            }
        }

        if (!onlyLeft && p.includeLists) { // create unique list for right
            for (PeptideSequenceWithHits pepR : rightPeps) {
                if (!leftPeps.contains(pepR)) {
                    ArrayList<Object> uniqueRPepsRow = new ArrayList<Object>(8);

                    uniqueRPepsRow.add(pepR.getSequence().toString());
                    uniqueRPepsRow.add(pepR.getHitCount());
                    uniqueRPepsRow.add(pepR.getGoodRepresentingHit().getCorrectScore());
                    uniqueRPepsRow.add(pepR.getGoodRepresentingHit().getQuery().getScanTitle());
                    ArrayList<Double> scores = new ArrayList<Double>(pepR.getHitCount());
                    String scoreList = "";
                    String queryList = "";
                    for (PeptideHit ph : pepR.getHits()) {
                        scores.add(ph.getCorrectScore());
                        scoreList+=ph.getCorrectScore()+"; ";
                        queryList+=ph.getQuery().getScanTitle()+"; ";
                    }
                    if (scoreList.endsWith("; ")) scoreList = scoreList.substring(0,scoreList.length()-2);
                    if (queryList.endsWith("; ")) queryList = queryList.substring(0,queryList.length()-2);

                    uniqueRPepsRow.add((new BasicStatNumbers<Double>(scores)).mean);
                    uniqueRPepsRow.add(scoreList);
                    uniqueRPepsRow.add(queryList);
                    uniqueRPepsRow.add(pepR.getGoodRepresentingHit().getComparableConfidence());
                    uniqueRPeps.addRow(uniqueRPepsRow);
                }
            }
        }

        if (!onlyLeft) {
            VennParameters vParam = new VennParameters();
            vParam.setaTotal(leftPeps.size());
            vParam.setbTotal(rightPeps.size());
            vParam.setAbIntersect(commonCount);
            vParam.setaLabel("X-(X\u2229Y)");
            vParam.setAbLabel("X\u2229Y");
            vParam.setbLabel("Y-(X\u2229Y)");
            vParam.setMinFontSize(12);
            vParam.setMaxFontSize(72);
            vParam.setEmbeddedLabelType(VennParameters.LabelType.PREFIXANDNUMBER);
            vParam.setExternalLabelType(VennParameters.LabelType.NONE);
            vParam.setAntialiase(true);
            vParam.setLineThickness(3);
            VennReportPageModel vPage = new VennReportPageModel("Diagram",dsLegend,vParam);
            vPage.setPageIsFinal();
            report.addPage(vPage);
        }

        report.addPage(summaryPage);

        if (p.includeLists) {

            uniqueLPeps.setPageIsFinal();
            uniqueLPeps.sort(7,false);

            if (onlyLeft) {
                report.addPage(uniqueLPeps);

            } else {
                commonPeps.setPageIsFinal();
                commonPeps.sort(13,false);

                uniqueRPeps.setPageIsFinal();
                uniqueRPeps.sort(7,false);

                report.addPage(commonPeps);
                report.addPage(uniqueLPeps);
                report.addPage(uniqueRPeps);
            }

        }

        ArrayList<Object> mainReportRow = new ArrayList<Object>(5);
        mainReportRow.add(summaryRowTitle);

        if (onlyLeft) {
            mainReportRow.add(leftPeps.size());
        } else {
            mainReportRow.add(leftPeps.size()-commonCount);
            mainReportRow.add(commonCount);
            mainReportRow.add(rightPeps.size()-commonCount);
            mainReportRow.add(leftPeps.size()+rightPeps.size()-commonCount);
        }


        summaryPage.addRow(mainReportRow);

        summaryPage.setPageIsFinal();

        return report;
        
    }

}
