package org.genemania.dw.entity;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeMap;
import org.genemania.dw.db.ExtResourceGeneTable;
import org.genemania.dw.util.DWUtil;
import org.genemania.dw.util.GenUtil;

/**
 * An attempt at abstracting the gene entity loaded from the different 
 * external resources used in DW. 
 * The attribute definition is also referred to as description.
 * If the names provided by the xref is different than the one used by the 
 * reference resource, then that xref name can also be added as a syn.
 * remove*() methods are used during Identifier validation.
 * 
 * @author rashadbadrawi
 */

public abstract class ExtResourceGene extends ExtResource {
    
    //controlled vocab for gene type, from external resources
    public static final String TYPE_PROTEIN_CODING_ENSEMBL = "protein_coding";
    public static final String TYPE_PROTEIN_CODING_ENTREZ = "protein-coding";
    public static final String TYPE_PROTEIN_CODING_TAIR = "protein_coding";

    protected String type;
    protected ArrayList <String> uniprotList = new ArrayList <String> ();
    protected ArrayList <String> MGDList = new ArrayList <String> ();

    public abstract String toStringLeftOver (String xRefType);

    public static void saveAll (TreeMap <String, ExtResource> extResMap) {

        if (extResMap == null) {
            throw new IllegalArgumentException ("Invalid argument: " + extResMap);
        }
        try {
            ExtResourceGeneTable.saveAll (extResMap);
        } catch (SQLException sqlEx) {
            throw new RuntimeException (sqlEx);
        }
    }

    public void setType (String type) {

        GenUtil.validateString (type);
        this.type = type;
    }
      
    public void addUniprotID (String uniprotID) {
        
        GenUtil.validateString(uniprotID);
        if (!uniprotList.contains (uniprotID)) {
            uniprotList.add (uniprotID);
        }
    }

    public void removeUniprotID (String uniprotID) {

        GenUtil.validateString (uniprotID);
        if (uniprotList.contains (uniprotID)) {
            uniprotList.remove(uniprotID);
        } else {
            System.out.println ("removeUniprot: No such ID " + uniprotID);
        }
    }

    public void addMGDID (String MGDID) {

        if (!speciesName.equals (DWUtil.SP_MOUSE)) {
            throw new UnsupportedOperationException ("MGD IDs are supported for " +
                    DWUtil.SP_MOUSE + " only.");
        }
        GenUtil.validateString(MGDID);
        if (!MGDList.contains (MGDID)) {
            MGDList.add (MGDID);
        }
    }

    public void removeMGDID (String MGDID) {

        if (!speciesName.equals (DWUtil.SP_MOUSE)) {
            throw new UnsupportedOperationException ("MGD IDs are supported for " +
                    DWUtil.SP_MOUSE + " only.");
        }
        GenUtil.validateString(MGDID);
        if (MGDList.contains (MGDID)) {
            MGDList.remove (MGDID);
        } else {
            System.out.println ("remove MGD: No such ID " + MGDID);
        }
    }

    public String getType () { return type; }

    public ArrayList <String> getUniprotList () { return uniprotList; }

    public ArrayList <String> getMGDList () { 
    
        if (!speciesName.equals (DWUtil.SP_MOUSE)) {
            throw new UnsupportedOperationException ("MGD IDs are supported for " +
                    DWUtil.SP_MOUSE + " only.");
        }
        
        return MGDList;
    }
    
    public ArrayList <String> getRefinedSynList () {
       
       ArrayList <String> refSynList = new ArrayList <String> ();
       ArrayList <String> tempList = getRefinedXrefList(LIST_SYN);
       tempList.addAll (getSynList());
       for (int i = 0; i < tempList.size (); i++) {
           if (!refSynList.contains (tempList.get (i))) {
               refSynList.add (tempList.get (i));
           }
       }
   
       return refSynList;
    } 
    
    public ArrayList <String> getRefinedXrefList (String attName) {
       
       ArrayList <String> refList = new ArrayList <String> ();
       //TreeMap <String, String> defsMap = new TreeMap <String, String> ();
       for (int i = 0; i < xResTypesList.size (); i++) {
           if (xResTypesList.get (i).equals (getSource())) {
               continue;
           }
           TreeMap <String, ExtResource> extMap = 
               getXRef(xResTypesList.get (i));  
           if (extMap == null) {
               continue;
           }
           Iterator iterator = extMap.keySet().iterator();
           while (iterator.hasNext()) {
               String extID = (String)iterator.next();
               ExtResource er = extMap.get (extID);
               if (er == null) {
                   System.err.println ("getRefinedXrefList: xRef not found: " + extID);
                   //continue;
               }
               //single atts
               if (attName.equals (LIST_ID)) {
                   if (!refList.contains(er.getID())) {
                       refList.add (er.getID());
                   }
                   continue;
               } else if (attName.equals (LIST_DEFN)) {
                   if (er.getDefinition() != null) {
                       if (!refList.contains (er.getDefinition().trim())) {
                           refList.add (er.getDefinition().trim());
                       }
                       //defsMap.put(er.getID(), er.getDefinition());
                   }
                   continue;
               }
               //list atts
               ArrayList <String> tempList = null;
               if (attName.equals (LIST_UNIPROT)) {
                   tempList = ((ExtResourceGene)er).getUniprotList();
               } else if (attName.equals (LIST_MGD)) {
                   tempList = ((ExtResourceGene)er).getMGDList();
               } else if (attName.equals (LIST_REFSEQ_RNA)) {
                   tempList = ((EntrezGene)er).getRefSeqRNAList();
               } else if (attName.equals (LIST_REFSEQ_PRO)) { 
                   tempList = ((EntrezGene)er).getRefSeqProList();                   
               } else if (attName.equals (LIST_SYN)) {
                   tempList = er.getSynList();
                   if (er.getName() != null) {
                       tempList.add (er.getName());
                   }
               }
               for (int j = 0; j < tempList.size (); j++) {
                   if (!refList.contains(tempList.get (j))) {
                       refList.add (tempList.get (j));
                   }
               }
           }
       }
       //follow-up for defs
       /*if (attName.equals(ExtResourceGene.LIST_DEFN)) {
           refList = refineDefList (defsMap);
       }*/

       return refList;
    }

    @Override
    public String toString () {
        
        String tempStr;
        
        tempStr = getDBID() + "";
        tempStr += GenUtil.TAB;
        
        tempStr += getID();
        tempStr += GenUtil.TAB;
        
        if (TYPE_PROTEIN_CODING_ENTREZ.equals (getType ())) {
            tempStr += "True";
        } else {
            tempStr += "False";
        }
        tempStr += GenUtil.TAB;
        
        if (name != null) {
            tempStr += name;
        } else {
            tempStr += GenUtil.NA;
        }
        tempStr += GenUtil.TAB;
        
        tempStr = GenUtil.addList (tempStr, getUniprotList());
        tempStr += GenUtil.TAB;

        if (DWUtil.SP_MOUSE.equals (speciesName)) {
            tempStr = GenUtil.addList (tempStr, getMGDList());
            tempStr += GenUtil.TAB;
        }
        
        tempStr = GenUtil.addList (tempStr, getSynList());
        tempStr += GenUtil.TAB;
        
        if (definition != null) {
            tempStr += definition;
        } else {
            tempStr += GenUtil.NA;
        }
        tempStr += GenUtil.TAB;
        
        return tempStr;
    }
}
