package org.genemania.dw.tools;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeMap;
import org.genemania.dw.entity.EnsemblGene;
import org.genemania.dw.entity.EntrezGene;
import org.genemania.dw.entity.ExtResource;
import org.genemania.dw.entity.ExtResourceGene;
import org.genemania.dw.entity.TAIRGene;
import org.genemania.dw.util.DWUtil;
import org.genemania.dw.util.DefParams;
import org.genemania.dw.util.GenUtil;

/**
 * Encapsulates main functionality for identifier validation. Assumes uniform
 * handling for validation issues. Used as part of the ID Mapping process.
 * The class interface allows flexibility in setting output file names, if
 * interested.
 * - Note that the validation is working on sets of genes, which might
 * represent all of the genes in a resource, or just a subset of interest (e.g.
 * in the case of leftovers).
 * - No getter for the validationType, and a private setter. Setting through
 * the validate () methods provides an easier class interface. 
 * - The dump method should be called after validation.
 *
 *
 * @author rashadbadrawi
 */

public class IdentifierValidator  {

    //validation types
    public static final String VALID_TYPE_SHARED = "Shared Identifier";
    public static final String VALID_TYPE_LEFTOVER = "LeftOver Gene";
    public static final String VALID_TYPE_DEPRECATED = "Deprecated Identifier";

    private static PrintWriter log;
    private static ArrayList <String> validTypesList;

    private String validationType;
    private boolean fixFlag;
    private String summaryFileName;
    private String detailFileName;
    private TreeMap <String, ExtResource> leftOverMap;

    //for handling report dumps
    private BufferedWriter bwSum;
    private BufferedWriter bwDetShared, bwDetLO, bwDetDep;
    private ArrayList <IVReportSummary> IVSumList = new ArrayList <IVReportSummary> ();

    static {                                            //order is significant
        validTypesList = new ArrayList <String> ();
        validTypesList.add (IdentifierValidator.VALID_TYPE_LEFTOVER);
        validTypesList.add (IdentifierValidator.VALID_TYPE_DEPRECATED);
        validTypesList.add (IdentifierValidator.VALID_TYPE_SHARED);
    }

    public IdentifierValidator () {

        log = GenUtil.getDefaultLog ();
        setFixFlag (Boolean.parseBoolean(
                    DefParams.getDefaultProp(DefParams.IV_FIX_PROP)));
    }

    private void setValidationType (String validationType) {

        if (!validTypesList.contains (validationType)) {
            throw new IllegalArgumentException (
                                         "Invalid argument: " + validationType);
        }
        this.validationType = validationType;
    }

    public void setFixFlag (boolean fixFlag) {

        this.fixFlag = fixFlag;
    }

    public void setSummaryFileName (String fileName) {

       GenUtil.validateString(fileName);
       this.summaryFileName = fileName;
    }

    public void setDetailFileName (String fileName) {

       GenUtil.validateString(fileName);
       this.detailFileName = fileName;
    }

    public String getSummaryFileName () { return summaryFileName; }

    public String getDetailFileName () { return detailFileName; }

    public boolean isFixing () { return fixFlag; }

    public TreeMap <String, ExtResource> getLeftOvers () {

        if (leftOverMap == null) {
            throw new IllegalStateException
                    ("Must run validation for leftOvers before retrieving them.");
        }

        return leftOverMap;
    }

    //Helper method
    private void setOutputFiles () {

        if (getSummaryFileName() == null || getDetailFileName() == null) {
            String defPathName = DefParams.getDefaultProp(DefParams.TOOLS_PATH_PROP);
            log.println ("Using default output file names: ");
            if (getSummaryFileName () == null) {
                log.println ("Summary File: " +
                defPathName + DefParams.getDefaultProp(DefParams.IV_SUM_FILE_PROP));
                setSummaryFileName (defPathName +
                              DefParams.getDefaultProp(DefParams.IV_SUM_FILE_PROP));
            }
            if (getDetailFileName () == null) {
                log.println ("Detailed File: " +
                defPathName + DefParams.getDefaultProp(DefParams.IV_DET_FILE_PROP));
                setDetailFileName (defPathName +
                              DefParams.getDefaultProp(DefParams.IV_DET_FILE_PROP));
            }
        }
    }

    public void validate (TreeMap <String, ExtResource> extResMap1,
                          TreeMap <String, ExtResource> extResMap2) {

        setOutputFiles ();
        for (int i = 0; i < validTypesList.size (); i++) {
            validate (extResMap1, extResMap2, validTypesList.get (i));
        }
    }

    public void validate (TreeMap <String, ExtResource> extResMap1,
          TreeMap <String, ExtResource> extResMap2, String validationType) {

        setValidationType(validationType);
        setOutputFiles ();
        if (validationType.equals (VALID_TYPE_DEPRECATED)) {
            validateDeprecated (extResMap1, extResMap2);
        } else if (validationType.equals (VALID_TYPE_LEFTOVER)) {
            validateLeftOver (extResMap1, extResMap2);
        } else if (validationType.equals (VALID_TYPE_SHARED)) {
            validateShared (extResMap1, extResMap2);
        }
        dump (false);
    }

    //TreeMap: key: attVal, Symbol: GeneID(s) 
    //the maps for GeneID and GeneName cover IDs, names of any gene class
    //Assumes only one x-ref gene type for each.
    //For Entrez x-ref, only TAIR is tracked.
    private void validateShared (TreeMap <String, ExtResource> extResMap1,
                                TreeMap <String, ExtResource> extResMap2) {

        System.out.println ("Validating for: " + IdentifierValidator.VALID_TYPE_SHARED);
        TreeMap <String, ArrayList<ExtResource>> geneNameMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
           //new TreeMap <String, ArrayList<ExtResource>> (String.CASE_INSENSITIVE_ORDER);
        TreeMap <String, ArrayList<ExtResource>> ensTransMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
        TreeMap <String, ArrayList<ExtResource>> ensProteinMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
        TreeMap <String, ArrayList<ExtResource>> refSeqMRNAMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
        TreeMap <String, ArrayList<ExtResource>> refSeqProMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
        TreeMap <String, ArrayList<ExtResource>> uniprotMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
        TreeMap <String, ArrayList<ExtResource>> MGDMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
        TreeMap <String, ArrayList<ExtResource>> xrefIDMap =
           new TreeMap <String, ArrayList<ExtResource>> ();
        boolean trackStats = true;
        if (extResMap2 == null) {
            trackStats = false;
        }
        Iterator iterator = extResMap1.keySet().iterator();
        String geneNameSource = null, xrefSource = null, source = null, 
               speciesName = null;
        boolean firstTime = true;
        while (iterator.hasNext()) {
            String extResID = (String)iterator.next();
            ExtResource gene = extResMap1.get (extResID);
            //species specific validation - MGD
            boolean validateMGD = DWUtil.SP_MOUSE.equals (gene.getSpeciesName());
            if (gene instanceof EnsemblGene) {
                EnsemblGene ensGene = (EnsemblGene)gene;
                validateAttList (ensGene.getRefinedXrefList(ExtResource.LIST_REFSEQ_RNA),
                                 ensGene, refSeqMRNAMap);
                validateAttList (ensGene.getRefinedXrefList(ExtResource.LIST_REFSEQ_PRO),
                                 ensGene, refSeqProMap);
                validateAttList (ensGene.getUniprotList(), ensGene, uniprotMap);
                if (validateMGD) {
                    validateAttList (ensGene.getMGDList(), ensGene, MGDMap);
                }

                validateAttList (ensGene.getTranscripts(), ensGene, ensTransMap);
                validateAttList (ensGene.getProteins(), ensGene, ensProteinMap);
                if (firstTime) {
                    geneNameSource = FFColumns.ENS_GENE_NAME_HEADER;
                    source = ExtResource.RES_ENSEMBL;
                    speciesName = gene.getSpeciesName();
                    firstTime = false;
                }
            }
            if (gene instanceof EntrezGene) {
                EntrezGene entGene = (EntrezGene)gene;
                validateAttList (entGene.getUniprotList(), entGene, uniprotMap);
                if (validateMGD) {
                    validateAttList (entGene.getMGDList(), entGene, MGDMap);
                }
                validateAttList (entGene.getRefSeqRNAList(), entGene, refSeqMRNAMap);
                validateAttList (entGene.getRefSeqProList(), entGene, refSeqProMap);
                if (firstTime) {
                    geneNameSource = FFColumns.ENT_GENE_NAME_HEADER;
                    source = ExtResource.RES_ENTREZ;
                    speciesName = gene.getSpeciesName();
                    firstTime = false;
                }
            }
            if (gene instanceof TAIRGene) {
                TAIRGene tairGene = (TAIRGene)gene;
                validateAttList (tairGene.getRefinedXrefList(ExtResource.LIST_REFSEQ_RNA),
                             tairGene, refSeqMRNAMap);
                validateAttList (tairGene.getRefinedXrefList(ExtResource.LIST_REFSEQ_PRO),
                             tairGene, refSeqProMap);
                validateAttList (tairGene.getRefinedXrefList(
                           ExtResource.LIST_UNIPROT), tairGene, uniprotMap);
                if (firstTime) {
                    geneNameSource = FFColumns.TAIR_LOCUS_NAME_HEADER;
                    source = ExtResource.RES_TAIR;
                    speciesName = tairGene.getSpeciesName();
                    firstTime = false;
                }
            }
            if (gene.getName() != null) {
                validateAtt (geneNameMap, gene, gene.getName ());
            }
            if (extResMap2 != null) {
                if (ExtResource.RES_ENSEMBL.equals (extResMap2.get(extResMap2.firstKey()).getSource())) {
                    xrefSource = FFColumns.ENS_GENE_ID_HEADER;
                } else if (ExtResource.RES_ENTREZ.equals (extResMap2.get(extResMap2.firstKey()).getSource())) {
                    xrefSource = FFColumns.ENT_GENE_ID_HEADER;
                } else if (ExtResource.RES_TAIR.equals (extResMap2.get(extResMap2.firstKey()).getSource())) {
                    xrefSource = FFColumns.TAIR_LOCUS_ID_HEADER;
                }
            }
            validateAttList (((ExtResourceGene)gene).getRefinedXrefList(ExtResource.LIST_ID),
                             gene, xrefIDMap);
        }
        generateIVReportsShared (source, speciesName, geneNameSource, geneNameMap, trackStats);
        if (source.equals (ExtResource.RES_ENSEMBL)) {
            generateIVReportsShared (source, speciesName, FFColumns.ENS_TRANS_ID_HEADER, ensTransMap, trackStats);
            generateIVReportsShared (source, speciesName, FFColumns.ENS_PRO_ID_HEADER, ensProteinMap, trackStats);
        }
        generateIVReportsShared (source, speciesName, FFColumns.REFSEQ_MRNA_HEADER, refSeqMRNAMap, trackStats);
        generateIVReportsShared (source, speciesName, FFColumns.REFSEQ_PRO_HEADER, refSeqProMap, trackStats);
        generateIVReportsShared (source, speciesName, FFColumns.UNIPROT_ID_HEADER, uniprotMap, trackStats);
        generateIVReportsShared (source, speciesName, FFColumns.MGD_ID_HEADER, MGDMap, trackStats);
        if (extResMap2 != null) {
            generateIVReportsShared (source, speciesName, xrefSource, xrefIDMap, trackStats);
        }
        if (isFixing()) {
            removeShared (FFColumns.GENE_NAME_HEADER, geneNameMap);
            if (source.equals (ExtResource.RES_ENSEMBL)) {
                removeShared (FFColumns.ENS_TRANS_ID_HEADER, ensTransMap);
                removeShared (FFColumns.ENS_PRO_ID_HEADER, ensProteinMap);
            }
            //otherwise, removed indirectly by removing the Entrez Xref.
            if (source.equals (ExtResource.RES_ENTREZ)) {
                removeShared (FFColumns.REFSEQ_MRNA_HEADER, refSeqMRNAMap);
                removeShared (FFColumns.REFSEQ_PRO_HEADER, refSeqProMap);
            }
            removeShared (FFColumns.UNIPROT_ID_HEADER, uniprotMap);
            removeShared (FFColumns.MGD_ID_HEADER, MGDMap);
            if (extResMap2 != null) {
                removeShared (xrefSource, xrefIDMap);
            }
        }
    }

    private void generateIVReportsShared (String source, String speciesName, 
                 String IDType, TreeMap <String, ArrayList<ExtResource>> attMap, boolean
                 trackStats) {

        if (attMap.size() == 0) {
            return;
        }
        IVReportSummary IVSum = new IVReportSummary();
        IVSum.setSource(source);
        IVSum.setSpeciesName(speciesName);
        IVSum.setValidationType(this.validationType);
        IVSum.setIDType(IDType);
        IVSum.setNumTotal(attMap.size());
        //System.out.println ("Total num for ID Type: " + IDType + " " + attMap.size ());
        trimAttList (attMap);
        IVSum.setNumOfOccur(attMap.size());
        //System.out.println ("Problematic num for ID Type: " + IDType + " " + attMap.size());
        IVSum.setIsDisplayable(trackStats);
        ArrayList <IVReportDetail> IVDetList = new ArrayList <IVReportDetail> ();
        Iterator iterator = attMap.keySet().iterator();
        while (iterator.hasNext()) {
            String attVal = (String) iterator.next();
            ArrayList <ExtResource> sharedList = attMap.get (attVal);
            IVReportDetail IVDet = new IVReportDetail ();
            IVDet.setIDType(IDType);
            IVDet.setSharedDetails(attVal, sharedList);
            IVDetList.add (IVDet);
        }
        IVSum.setIVDetList(IVDetList, VALID_TYPE_SHARED);
        IVSumList.add (IVSum);
    }

    private void validateAttList (ArrayList <String> attList, ExtResource
                 gene, TreeMap <String, ArrayList<ExtResource>> attMap) {

        for (int i = 0; i < attList.size (); i++) {
            validateAtt (attMap, gene, attList.get (i));
        }
    }

    private void validateAtt (TreeMap <String, ArrayList<ExtResource>> attMap,
            ExtResource gene, String attVal) {

        ArrayList <ExtResource> sharedList = attMap.get (attVal);
        if (sharedList == null) {
            sharedList = new ArrayList <ExtResource> ();
            sharedList.add (gene);
            attMap.put (attVal, sharedList);
        } else {
            for (int i = 0; i < sharedList.size(); i++) {
                if (gene.getID().equals (sharedList.get (i).getID())) {
                    System.err.println ("ValidateShared: Duplicates: " + 
                                         sharedList.get (i).getID());
                    return;                    
                }
            }
            sharedList.add (gene);
            attMap.put (attVal, sharedList);
        }
    }

    private void trimAttList (TreeMap <String, ArrayList<ExtResource>> attMap) {

        Iterator iterator = attMap.keySet ().iterator ();
        while (iterator.hasNext ()) {
            String attName = (String)iterator.next ();
            ArrayList<ExtResource> sharedList = attMap.get (attName);
            if (sharedList.size () == 1) {
                iterator.remove();
            }
        }
    }

    private void removeShared (String IDType,
                        TreeMap <String, ArrayList <ExtResource>> attMap) {

        Iterator iterator = attMap.keySet().iterator();
        while (iterator.hasNext()) {
            String attVal = (String)iterator.next();
            ArrayList <ExtResource> attList = attMap.get (attVal);
            for (int i = 0; i < attList.size (); i++) {
                ExtResource sharingGene = attList.get (i);
                if (IDType.equals (FFColumns.GENE_NAME_HEADER)) {
                    sharingGene.resetName();
                } else if (IDType.equals (FFColumns.ENS_TRANS_ID_HEADER)) {
                    ((EnsemblGene)sharingGene).removeTranscriptID(attVal);
                } else if (IDType.equals (FFColumns.ENS_PRO_ID_HEADER)) {
                    ((EnsemblGene)sharingGene).removeProteinID(attVal);
                } else if (IDType.equals (FFColumns.REFSEQ_MRNA_HEADER)) {
                    ((EntrezGene)sharingGene).removeRefSeqRNAID(attVal);
                } else if (IDType.equals (FFColumns.REFSEQ_PRO_HEADER)) {
                    ((EntrezGene)sharingGene).removeRefSeqProID(attVal);
                } else if (IDType.equals (FFColumns.UNIPROT_ID_HEADER)) {
                    ((ExtResourceGene)sharingGene).removeUniprotID(attVal);
                } else if (IDType.equals (FFColumns.MGD_ID_HEADER)) {
                    ((ExtResourceGene)sharingGene).removeMGDID(attVal);
                } else if (IDType.equals (FFColumns.ENT_GENE_ID_HEADER)) {
                     sharingGene.removeXRef(ExtResource.RES_ENTREZ, attVal);
                } else if (IDType.equals (FFColumns.TAIR_LOCUS_ID_HEADER)) {
                     sharingGene.removeXRef(ExtResource.RES_TAIR, attVal);
                }
                //System.out.println ("Remove: " + attVal +  " from " + sharingGene.toString());
                attList.set(i, sharingGene);
            }
        }
    }

    private TreeMap <String, ExtResource>
                validateLeftOver (TreeMap <String, ExtResource> extResMap1,
                                  TreeMap <String, ExtResource> extResMap2) {

       System.out.println ("Validating for: " + IdentifierValidator.VALID_TYPE_LEFTOVER);
       TreeMap <String, ExtResource> xRefMap = new
                                          TreeMap <String, ExtResource> ();
       Iterator iterator = extResMap1.keySet().iterator();
       String currentXRefType = extResMap2.get (extResMap2.firstKey()).getSource();
       while (iterator.hasNext()) {
           ExtResource gene1 = extResMap1.get ((String)iterator.next());
           TreeMap <String, ExtResource> tempMap =
           gene1.getXRef(currentXRefType);
           if (tempMap == null) {           //i.e. no XRefs from that resource
               continue;
           } else {
               xRefMap.putAll(tempMap);    //add to global refined xref set
           }
       }
       //filter and save all leftovers
       ArrayList <IVReportDetail> IVDetList = new ArrayList <IVReportDetail> ();
       this.leftOverMap = new TreeMap <String, ExtResource> ();
       iterator = extResMap2.keySet().iterator();
       while (iterator.hasNext()) {
           String geneID2 = (String)iterator.next();
           if (xRefMap.containsKey(geneID2)) {           //not a leftover
               continue;
           }
           this.leftOverMap.put (geneID2, extResMap2.get (geneID2));
           IVReportDetail IVDet = new IVReportDetail ();
           IVDet.setLeftOverDetails(extResMap2.get (geneID2));
           IVDetList.add (IVDet);
       }
       IVReportSummary IVSum = new IVReportSummary();
       IVSum.setSource(extResMap1.get (extResMap1.firstKey()).getSource());
       IVSum.setSpeciesName (extResMap1.get (extResMap1.firstKey()).getSpeciesName());
       IVSum.setValidationType(this.validationType);
       IVSum.setNumOfOccur(leftOverMap.size());
       IVSum.setNumTotal(extResMap1.size());
       IVSum.setIVDetList(IVDetList, VALID_TYPE_LEFTOVER);
       IVSumList.add (IVSum);

       return this.leftOverMap;
    }

    private void validateDeprecated (TreeMap <String, ExtResource> extResMap1,
                                    TreeMap <String, ExtResource> extResMap2)
                                    throws RuntimeException {
        
        System.out.println ("Validating for: " + IdentifierValidator.VALID_TYPE_DEPRECATED);
        TreeMap <String, String> deprecatedMap = new TreeMap <String, String> ();
        TreeMap <String, ExtResource> xRefMap = null;
        Iterator iterator1 = extResMap1.keySet().iterator();
        String currentXRefType = null;
        IVReportSummary IVSum = new IVReportSummary();
        IVSum.setSource(extResMap1.get (extResMap1.firstKey()).getSource());
        IVSum.setSpeciesName(extResMap1.get (extResMap1.firstKey()).getSpeciesName());
        IVSum.setValidationType(this.validationType);
        ArrayList <IVReportDetail> IVDetList = new ArrayList <IVReportDetail> ();

        //start by grabbing the xrefs of interest
        while (iterator1.hasNext()) {
            ExtResource gene1 = extResMap1.get ((String)iterator1.next());
            if (gene1 instanceof EnsemblGene) {
                xRefMap = gene1.getXRef(ExtResource.RES_ENTREZ);
                currentXRefType = ExtResource.RES_ENTREZ;
            } else if (gene1 instanceof EntrezGene) {
                xRefMap = gene1.getXRef(ExtResource.RES_TAIR);
                currentXRefType = ExtResource.RES_TAIR;
            } else if (gene1 instanceof TAIRGene) {
                xRefMap = gene1.getXRef(ExtResource.RES_ENTREZ);
                currentXRefType = ExtResource.RES_ENTREZ;
            }
            //iterate through the xrefs, see if they are present in the
            //second resource genes. Gene2 and xRefGene should carry the same ID.
            if (xRefMap == null) {
                continue;
            }
            TreeMap <String, ExtResource> newXRefMap = new TreeMap
                    <String, ExtResource> ();
            Iterator iterator2 = xRefMap.keySet().iterator();
            while (iterator2.hasNext()) {
                IVSum.incrementTotal();
                String geneID2 = (String)iterator2.next();
                ExtResource gene2 = xRefMap.get (geneID2);
                ExtResource xRefGene = extResMap2.get (gene2.getID());
                if (xRefGene == null) {
                    //System.out.println ("ValidateDeprecated: " +
                    //       gene1.getSource() + " to " + gene2.getSource() +
                    //       " " + gene2.getGeneID());
                    if (!isFixing()) {                //keep as is
                        newXRefMap.put (geneID2, gene2);
                    }
                    //i.e, if Xref is from Entrez, look for replacement
                    if (gene2 instanceof EntrezGene) {
                        ArrayList <String> geneIDList =
                                                EntrezGene.replaceGeneID ((ExtResourceGene)gene2);
                        ArrayList <ExtResource> tempList =
                                  getReplacements (extResMap2, geneIDList);
                        String replaceStr = "";
                        for (int i = 0; i < tempList.size(); i++) {
                            if (i > 0) {
                                replaceStr += GenUtil.SEMICOLON;
                            }
                            replaceStr += tempList.get (i).getID();
                            if (isFixing()) {
                                newXRefMap.put (tempList.get (i).getID (), 
                                                tempList.get (i));
                            }
                        }
                        if (replaceStr.length () == 0) {
                            replaceStr += GenUtil.NA;
                        }
                        //System.out.println (gene2.getID() + ": " + replaceStr);
                        if (deprecatedMap.containsKey(gene2.getID ())) {
                            System.out.println ("Multiple deprecation: " +
                                gene1.getID() + " " + gene2.getID ());
                        } else {
                            IVReportDetail IVDet = new IVReportDetail();
                            IVDet.setDepDetails(gene1, gene2, tempList);
                            deprecatedMap.put (gene2.getID (), replaceStr);
                            IVDetList.add (IVDet);
                        }
                    } 
                } else {
                    //complemented version, with full atts, will overwrite
                    //the existing one. Entrez dropped if ID missing.
                    newXRefMap.put (xRefGene.getID(), xRefGene);
                }
            }
            if (newXRefMap.size () > 0) {
                gene1.setXRef(currentXRefType, newXRefMap);
            }
        }
        IVSum.setNumOfOccur(IVDetList.size());
        IVSum.setIVDetList(IVDetList, VALID_TYPE_DEPRECATED);
        IVSumList.add (IVSum);
    }

    private ArrayList <ExtResource>  getReplacements (
                                  TreeMap <String, ExtResource> extResMap2,
                                  ArrayList <String> geneIDList) {

        ArrayList <ExtResource> tempList = new ArrayList <ExtResource> ();
        for (int i = 0; i < geneIDList.size(); i++) {
            ExtResource extResGene = extResMap2.get (geneIDList.get (i));
            if (extResGene != null) {
                tempList.add (extResGene);
            } else {
                System.err.println ("Deprecated Replacement: " + geneIDList.get (i));
            }
        }

        return tempList;
    }

    public void dump (boolean closeStreams) {

        try {
            if (bwSum == null) {
                bwSum = new BufferedWriter (new FileWriter (summaryFileName));
                bwSum.write(FFColumns.SOURCE_HEADER + GenUtil.TAB +
                            FFColumns.SPECIES_HEADER + GenUtil.TAB +
                            FFColumns.VALID_TYPE_HEADER + GenUtil.TAB +
                            FFColumns.ID_TYPE_HEADER + GenUtil.TAB +
                            FFColumns.NUM_HEADER + GenUtil.TAB +
                            FFColumns.TOT_HEADER
                           );
                bwSum.newLine();
            }
            if (bwDetShared == null) {
                bwDetShared = new BufferedWriter (new FileWriter (detailFileName
                                       + GenUtil.UNDERSCORE + VALID_TYPE_SHARED));
                bwDetShared.write(FFColumns.SOURCE_HEADER + GenUtil.TAB +
                                  FFColumns.SPECIES_HEADER + GenUtil.TAB +
                                  FFColumns.ID_TYPE_HEADER + GenUtil.TAB +
                                  FFColumns.ID_HEADER + GenUtil.TAB +
                                  FFColumns.GENE_ID_HEADER
                                 );
                bwDetShared.newLine();
            }
            if (bwDetLO == null) {
                bwDetLO = new BufferedWriter (new FileWriter (detailFileName
                                      + GenUtil.UNDERSCORE + VALID_TYPE_LEFTOVER));
                bwDetLO.write(FFColumns.SOURCE_HEADER + GenUtil.TAB +
                              FFColumns.SPECIES_HEADER + GenUtil.TAB +
                              FFColumns.GENE_ID_HEADER
                              );
                bwDetLO.newLine();
            }
            if (bwDetDep == null) {
                bwDetDep = new BufferedWriter (new FileWriter (detailFileName
                                    + GenUtil.UNDERSCORE + VALID_TYPE_DEPRECATED));
                bwDetDep.write(FFColumns.SOURCE_HEADER + GenUtil.TAB +
                               FFColumns.SPECIES_HEADER + GenUtil.TAB +
                               FFColumns.GENE_ID_HEADER + GenUtil.TAB +
                               FFColumns.OLD_GENE_ID_HEADER + GenUtil.TAB +
                               FFColumns.NEW_GENE_ID_HEADER
                               );
                bwDetDep.newLine();
            }
            System.out.println ("Dumping: Summary: " + IVSumList.size());
            for (int i = 0; i < IVSumList.size (); i++) {
                System.out.println (IVSumList.get (i).toString());
                if (IVSumList.get (i).isDisplayable()) {
                    bwSum.write (IVSumList.get (i).toString());
                    bwSum.newLine();
                    bwSum.flush();
                }

                IVSumList.get (i).toStringDetailShared(bwDetShared);
                IVSumList.get (i).toStringDetailLO(bwDetLO);
                IVSumList.get (i).toStringDetailDep(bwDetDep);
            }
            bwSum.flush();
            bwDetShared.flush();
            bwDetLO.flush();
            bwDetDep.flush();
            IVSumList.clear();
            if (closeStreams) {
                bwSum.close ();
                bwDetShared.close();
                bwDetLO.close ();
                bwDetDep.close ();
            }
        } catch (IOException ioe) {
            throw new RuntimeException (ioe);
        }
    }
}
