/*
 * Copyright 2010 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.protgroup;

import compid.data.tuples.ProteinHit.GroupMemberStatus;
import compid.report.DatasetCollection;
import compid.report.DatasetCollectionNotEmptyValidator;
import compid.report.ReporterParams;
import net.aavalla.utils.jobpool.parameters.FieldAnnotations;
import net.aavalla.utils.jobpool.parameters.SyncEnumSet;
import net.aavalla.utils.jobpool.parameters.validators.IntegerMinMaxValidator;
import net.aavalla.utils.jobpool.parameters.validators.StringValidator;

/**
 *
 * @author Lari Natri
 */
public class ProtGroupParams extends ReporterParams {

    public enum LinkageMethod { // *within* the thresholds
        Single, Complete
    }

    public enum ClusteringMethod {
        PeptideSubset, CommonPeptides, Sequence, AccessionOnly;

        @Override public String toString() { // must be done here to keep type
            if (this.equals(ClusteringMethod.PeptideSubset))
                return "Peptide Subset";
            if (this.equals(ClusteringMethod.CommonPeptides))
                return "Common Peptides";
            if (this.equals(ClusteringMethod.Sequence))
                return "Sequence Similarity";
            if (this.equals(ClusteringMethod.AccessionOnly))
                return "Accession only";
            return this.name();
        }
    }

    public enum ParagonScoreType { Total, Unused }


    @FieldAnnotations(name="Report title",
        description="Title for this Protein Groups report",
        validator=StringValidator.class,
        validatorSettings={"1","64",FieldAnnotations.NULL})
    String reportTitle = "Protein Groups";
    @FieldAnnotations(name="Left Datasets (X)",
        description="Datasets which will be combined (and compared to right datasets if any chosen)",
        validator=DatasetCollectionNotEmptyValidator.class)
    DatasetCollection left;
    @FieldAnnotations(name="Right Datasets (Y)",
        description="Datasets which will be combined and compared to left datasets")
    DatasetCollection right;

    @FieldAnnotations(name="Linkage Method",
        description="<p>Linkage method makes it's decision by taking into account only \n" +
                    "those proteins which pass the <i><b>Cutoff</b> thresholds</i>. Similarity is \n" +
                    "determined by the <i><b>Protein Pair Scoring</b> method</i>.</p>" +
                    "<p><table>"+
                    "<tr><td><b>Single:</b></td><td>Two groups are combined, if they have any two proteins (one from each group) \n" +
                    "                               among the considered proteins that are similar enough.</td></tr> \n" +
                    "<tr><td><b>Complete:</b></td><td>Even the least similar considered proteins from each group must be similar enough \n" +
                    "                                 for the groups to be combined. </td></tr> \n" +
                    "</table></p>")
    LinkageMethod groupThLinkage = LinkageMethod.Complete; // within prots going over threshold
    @FieldAnnotations(name="Ignore Original Grouping",
        description="If selected, the original grouping given by the source \n" +
                    "software is ignored. Every protein in the datasets is initially \n" +
                    "considered to be a group of one. Sub proteins will also get \n" +
                    "their own groups of size one.")
    boolean ignoreOriginalGrouping = false;

    @FieldAnnotations(name="Protein Pair Scoring Method",
        description="<p>This is the method used in scoring <i>protein pairs</i> by their similarity and determining if \n" +
                    "they go over a <i>scoring threshold</i> to be given a score. <b><i>Linkage Method</i></b> considers \n" +
                    "the amount of scored protein pairs between groups and decides whether the groups \n" +
                    "can be combined or not. Proteins not passing the <i><b>Cutoff</b> thresholds</i> are not \n" +
                    "scored nor taken into account by the Linkage Method.</p>" +
                    "<p><table>" +
                    "<tr><td><b>Peptide Subset:</b></td><td>All the peptides of one protein must be found in the other protein</td></tr>" +
                    "<tr><td><b>Common Peptides:</b></td><td>Proteins must have a specified amount of common peptides</td></tr>" +
                    "<tr><td><b>Sequence:</b></td><td>Proteins' sequences must be similar (allowing for a specified \n" +
                                                    "amount of amino acid alterations). <b><i>Note:</b></i> Protein sequences must \n" +
                                                    "be imported after importing the datasets for this to be effective.</td></tr>" +
                    "<tr><td><b>Accession only:</b></td><td>Proteins must have the same accession number.</td></tr>" +
                    "</table></p>")
    ClusteringMethod method = ClusteringMethod.PeptideSubset;
    @FieldAnnotations(name="Group by Accession",
        description="If selected, proteins having the same accession are considered to be \n" +
                    "similar enough for grouping regardless of the grouping method. \n" +
                    "<b>Recommended</b>.")
    boolean useAccession = true; // true if accession equality *always* goes over treshold
    @FieldAnnotations(name="Peptide-based: Strict Equality",
        description="If selected, peptides must have exactly the same sequences to be considered \n" +
                    "the same, otherwise ambiguous amino acid symbols are taken into account: \n" +
                    "for example, amino acid symbol X is thought of being equal with any \n" +
                    "other amino acid. <i>This setting only has effect when using peptide-based \n" +
                    "grouping methods.</i>")
    boolean strictPepEquality = true; // used only if method=PeptideSubset || method==CommonPeptides
    @FieldAnnotations(name="Peptide-based: Minimum Peptides",
        description="Minimum number of peptides a protein must have to be grouped when \n" +
                    "using peptide-based grouping methods. Additionally, with Common \n" +
                    "Peptides method, this is used as a minimum number of peptides \n" +
                    "proteins must share in order for them to be grouped. <i>This setting \n" +
                    "only has effect when using peptide-based grouping methods.</i>",
        validator=IntegerMinMaxValidator.class,
        validatorSettings={"0","200"})
    int minPepsForPeptideBasedMethods = 1;
    @FieldAnnotations(name="Sequence: Max AA substitutions",
        description="When using <b>Sequence</b> as the grouping method, this is the \n" +
                    "<i>maximum number of single amino acid differences</i> allowed in two \n" +
                    "sequences for them to be grouped together, including insertions \n" +
                    "and deletions. <i>This setting only has effect if using sequence- \n" +
                    "based grouping method.</i>",
        validator=IntegerMinMaxValidator.class,
        validatorSettings={"0","100"})
    int seqMaxSubsts = 3; // used only if method==Sequence

    @FieldAnnotations(name="Cutoff: Original Types A",
        description="When combining two groups to form a new group, only \n" +
                    "these group member types (as given by the source \n" +
                    "software) from one of the groups are taken into \n" +
                    "account. Other types are ignored as if they don't \n" +
                    "exist. Note that this applies always even if the \n" +
                    "two groups originate in the same dataset. Also, only \n" +
                    "proteins having their original types in the union \n" +
                    "of Original types A and B are displayed in the final \n" +
                    "report.")
    SyncEnumSet<GroupMemberStatus> cutoffStatuses1 = SyncEnumSet.of(GroupMemberStatus.WINNER); // statuses to go over threshold for ONE of the sets
    @FieldAnnotations(name="Cutoff: Original Types B",
        description="When combining two groups to form a new group, only \n" +
                    "these group member types (as given by the source \n" +
                    "software) from one of the groups are taken into \n" +
                    "account. Other types are ignored as if they don't \n" +
                    "exist. Note that this applies always even if the \n" +
                    "two groups originate in the same dataset. Also, only \n" +
                    "proteins having their original types in the union \n" +
                    "of Original types A and B are displayed in the final \n" +
                    "report.")
    SyncEnumSet<GroupMemberStatus> cutoffStatuses2 = SyncEnumSet.of(GroupMemberStatus.WINNER, GroupMemberStatus.SUBISOFORMWINNER, GroupMemberStatus.SUBPEPCOMPLETE); // statuses to go over threshold for OTHER of the sets
    @FieldAnnotations(name="Cutoff: Minimum Peptides",
        description="Only proteins having at least this amount of peptide matches \n" +
                    "are taken into consideration when combining groups. The other \n" +
                    "proteins are treated as if they don't exist (in a group).",
        validator=IntegerMinMaxValidator.class,
        validatorSettings={"1","200"})
    int cutoffMinPeps = 1; // minimum peptides to go over treshold, used in all
    
//    @FieldAnnotations(
//        name="Create list pages",
//        description="Generate lists of unique and common proteins. \n" +
//        "If not selected, only the <b>amounts</b> of common/unique proteins are displayed.\n" +
//        "<b>Recommended.</b>")
//    boolean includeLists = true;
    @FieldAnnotations(
        name="Paragon Score Type",
        description="The type of protein score used for proteins originating in \n" +
                    "ProteinPilot. Used for scoring protin pairs and is the score \n" +
                    "displayed in the final report. <i>See ProteinPilot documentation \n" +
                    "for details.</i>")
    ParagonScoreType paragonScoreType = ParagonScoreType.Total;
    @FieldAnnotations(
        name="Display protein names",
        description="Displays protein names in resulting lists")
    boolean displayProtNames = true;

    public ProtGroupParams() {
        this.setDescription("Creates a Protein Groups Report for a dataset or for a union <br/>" +
                "of several sets. Comparing (unions of) sets is possible.");
        this.setHelpID("protgroups");
    }

    @Override
    public synchronized String validateFieldRelations() {
        if (this.cutoffStatuses1.isEmpty() || this.cutoffStatuses2.isEmpty()) {
            return "Neither of the Original Types sets (A, B) can be empty.";
        }
        if (!this.cutoffStatuses1.contains(GroupMemberStatus.WINNER)
                && !this.cutoffStatuses2.contains(GroupMemberStatus.WINNER))
        {
            return "At least one of the Original Types sets must contain "
                    + GroupMemberStatus.WINNER.toString() + " for the results " +
                    "to be meaningful";
        }
        return null;
    }

}
