package org.intermine.bio.dataconversion;

/*
 * Copyright (C) 2002-2011 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.intermine.util.FormattedTextParser;


/**
 * Parse NCBI Entrez gene_info file and return data structure for each row for later processing.
 * @author Richard Smith
 *
 */
public class NcbiGeneInfoParser
{
    private Map<String, Set<GeneInfoRecord>> recordMap = new HashMap<String, Set<GeneInfoRecord>>();
    private Map<String, Set<String>> duplicateEnsemblIds = new HashMap<String, Set<String>>();
    private Map<String, Set<String>> duplicateSymbols = new HashMap<String, Set<String>>();

    /**
     * Construct the parser with the file to read, the input file can be for a single taxon or for
     * multiple.
     * @param reader a reader for the gene_info file to be parsed
     * @throws IOException if problems reading file
     */
    public NcbiGeneInfoParser(Reader reader) throws IOException {
        Iterator<String[]> lineIter = FormattedTextParser.parseTabDelimitedReader(reader);

        while (lineIter.hasNext()) {
            String[] line = lineIter.next();

            String taxonId = line[0];
            String entrez = line[1];
            String defaultSymbol = line[2];
            String synonyms = line[4];
            String xrefs = line[5];
            String mapLocation = line[7];
            String defaultName = line[8];
            String officialSymbol = line[10];
            String officialName = line[11];

            GeneInfoRecord record = new GeneInfoRecord(taxonId, entrez, officialSymbol,
                    defaultSymbol, officialName, defaultName, mapLocation);
            record.ensemblIds.addAll(parseXrefs(xrefs, "Ensembl"));

            for (String synonym : synonyms.split("\\|")) {
                record.synonyms.add(synonym);
            }

            Set<GeneInfoRecord> taxonRecords = recordMap.get(taxonId);
            if (taxonRecords == null) {
                taxonRecords = new HashSet<GeneInfoRecord>();
                recordMap.put(taxonId, taxonRecords);
            }
            taxonRecords.add(record);
        }
    }

    /**
     * Return information read from the file, for each taxon id there is one GeneInfoRecord per row
     * in the file.
     * @return a map from taxon id to gene information records
     */
    public Map<String, Set<GeneInfoRecord>> getGeneInfoRecords() {
        return recordMap;
    }

    /**
     * Return true if the given Ensembl identifier is assigned to only one gene for the taxon
     * specified.
     * @param taxonId the taxon of the gene being looked up
     * @param ensemblId an ensembl gene identifier
     * @return true if this ensembl id is only mapped to one NCBI gene
     */
    public boolean isUniquelyMappedEnsemblId(String taxonId, String ensemblId) {
        Set<String> taxonDuplicates = duplicateEnsemblIds.get(taxonId);
        if (taxonDuplicates == null) {
            taxonDuplicates = findDuplicateEnsemblIds(taxonId);
            duplicateEnsemblIds.put(taxonId, taxonDuplicates);
        }
        return !taxonDuplicates.contains(ensemblId);
    }

    private Set<String> findDuplicateEnsemblIds(String taxonId) {
        Set<String> duplicates = new HashSet<String>();
        if (recordMap.containsKey(taxonId)) {
            Set<String> seenEnsembl = new HashSet<String>();
            for (GeneInfoRecord record : recordMap.get(taxonId)) {
                for (String ensembl : record.ensemblIds) {
                    if (seenEnsembl.contains(ensembl)) {
                        duplicates.add(ensembl);
                    } else {
                        seenEnsembl.add(ensembl);
                    }
                }
            }
        }
        return duplicates;
    }

    /**
     * Return true if the given symbol is is assigned to only one gene for the taxon specified.
     * @param taxonId the taxon of the gene being looked up
     * @param symbol a gene symbl to check
     * @return true if this true id is only assigned to one NCBI gene
     */
    public boolean isUniqueSymbol(String taxonId, String symbol) {
        Set<String> taxonDuplicates = duplicateSymbols.get(taxonId);
        if (taxonDuplicates == null) {
            taxonDuplicates = findDuplicateSymbols(taxonId);
            duplicateSymbols.put(taxonId, taxonDuplicates);
        }
        return !taxonDuplicates.contains(symbol);
    }

    private Set<String> findDuplicateSymbols(String taxonId) {
        Set<String> duplicates = new HashSet<String>();
        if (recordMap.containsKey(taxonId)) {
            Set<String> seenSymbols = new HashSet<String>();
            for (GeneInfoRecord record : recordMap.get(taxonId)) {
                for (String symbol : new String[] {record.officialSymbol, record.defaultSymbol}) {
                    if (seenSymbols.contains(symbol)) {
                        duplicates.add(symbol);
                    } else {
                        seenSymbols.add(symbol);
                    }
                }
            }
        }
        return duplicates;
    }

    private Set<String> parseXrefs(String xrefs, String prefix) {
        if (!prefix.endsWith(":")) {
            prefix = prefix + ":";
        }
        Set<String> matched = new HashSet<String>();
        for (String xref : xrefs.split("\\|")) {
            if (xref.startsWith(prefix)) {
                matched.add(xref.substring(prefix.length()));
            }
        }
        return matched;
    }
}

@@ -4804,38 +4804,37 @@
 l);%0A    %7D%0A%0A    p
-rivate
+ublic
  Set%3CString%3E fin

Make duplicate method available.