package org.genemania.dw.entity;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.TreeMap;
import org.genemania.dw.util.GenUtil;

/**
 * Abstraction for an external bioinfo resource.
 *
 * @author rashadbadrawi
 */

public abstract class ExtResource implements Cloneable {

    //External Resources
    public static final String RES_ENSEMBL = "Ensembl";
    public static final String RES_ENTREZ = "Entrez";
    public static final String RES_TAIR = "TAIR";
    public static final String RES_UNIPROT = "Uniprot";
    public static final String RES_GO = "GO";

    //used for a helper method in subclasses.
    public static final String LIST_ID = "ExtID";
    public static final String LIST_SYN = "Synonyms";
    public static final String LIST_DEFN = "Defn";

    public static final String LIST_REFSEQ_RNA = "RefSeqRNA";
    public static final String LIST_REFSEQ_PRO = "RefSeqPro";
    public static final String LIST_UNIPROT = "Uniprot";       //temp
    public static final String LIST_MGD = "MGD";
    public static final String LIST_GI = "GI";                 //parital support

    //Controlled vocab for entry status
    public static final String STATUS_CURRENT = "C";   //current and released
    public static final String STATUS_OLD = "O";       //old, not released

    protected int DBID = -1;
    protected String source;
    protected String speciesName;                   //not for all
    protected String ID;
    protected String name;
    protected String definition;
    protected String status;
    protected ArrayList <String> synList = new ArrayList <String> ();  //not for all
    protected TreeMap <String, TreeMap<String, ExtResource>> xRefMap = new
                       TreeMap <String, TreeMap<String, ExtResource>> ();

    protected static ArrayList <String> xResTypesList;

    static {
        xResTypesList = new ArrayList <String> ();
        xResTypesList.add (ExtResource.RES_ENSEMBL);
        xResTypesList.add (ExtResource.RES_ENTREZ);
        xResTypesList.add (ExtResource.RES_TAIR);
        xResTypesList.add (ExtResource.RES_UNIPROT);
        xResTypesList.add (ExtResource.RES_GO);
    }

    protected static PrintWriter log = GenUtil.getDefaultLog();

    public void setDBID (int DBID) {

        GenUtil.validatePositiveInt (DBID);
        this.DBID = DBID;
    }

    public void setSpeciesName (String speciesName) {

        GenUtil.validateString(speciesName);
        this.speciesName = speciesName;
    }

    public void setID (String extID) {

        GenUtil.validateString(extID);
        this.ID = extID;
    }

    public void setName (String name) {

        GenUtil.validateString(name);
        this.name = name;
    }

    public void resetName () {

        this.name = null;
    }

    public void setDefinition (String definition) {

        GenUtil.validateString(definition);
        this.definition = definition;
    }

    public void setSource (String source) {

        GenUtil.validateString(source);
        this.source = source;
    }

    public void setStatus (String status) {

        GenUtil.validateString (status);
        this.status = status;
    }

    public void addSyn (String syn) {

        GenUtil.validateString(syn);
        if (!synList.contains (syn.trim ())) {
            synList.add (syn);
        } /*else {
            System.out.println ("Synonym: " + syn + " already exists for: " +
                    ID);
        }*/
    }

    public void removeSyn (String syn) {

        GenUtil.validateString(syn);
        if (synList.contains (syn)) {
            synList.remove (syn);
        } else {
            System.out.println ("removeSyn: No such syn: " + syn);
        }
    }

    public void addXRef (String xRefType, ExtResource er) {

        validateXRefType (xRefType);
        if (er == null) {
            throw new IllegalArgumentException ("Invalid argument: " +
                    er);
        }
        TreeMap <String, ExtResource> tempMap = xRefMap.get (xRefType);
        //overwritten if already there.
        if (tempMap == null) {                   //first one
            tempMap = new TreeMap <String, ExtResource> ();
        }
        tempMap.put(er.getID(), er);
        xRefMap.put (xRefType, tempMap);
    }

    public void removeXRef (String xRefType, ExtResource er) {

        removeXRef (xRefType, er.getID ());
    }

    //handy for geneIDs
    public void removeXRef (String xRefType, String xRefID) {

        validateXRefType (xRefType);
        GenUtil.validateString(xRefID);
        TreeMap <String, ExtResource> tempMap = xRefMap.get (xRefType);
        //overwritten if already there.
        if (tempMap == null) {                   //no xrefs.
            System.err.println ("No such Xref: " + xRefType);
            return;
        }
        if (tempMap.containsKey(xRefID)) {
            tempMap.remove(xRefID);
            xRefMap.put (xRefType, tempMap);
        } else {
            System.out.println ("removeXref: No such ID: " + xRefID);
        }
    }

    public void setXRef (String xRefType, TreeMap <String, ExtResource>
                         tempMap) {

        if (tempMap == null || tempMap.size () == 0) {
            throw new IllegalArgumentException ("Invalid argument: " +
                    tempMap);
        }
        if (!xResTypesList.contains (xRefType)) {
            throw new IllegalArgumentException ("Unsupported xRef type: " +
                    xRefType);
        }
        //overwritten if already there.
        xRefMap.put (xRefType, tempMap);
    }

    public int getDBID () { return DBID; }

    public String getSpeciesName () { return speciesName; }

    public String getID () { return ID; }

    public String getName () { return name; }

    public String getDefinition () { return definition; }

    public String getSource () { return source; }

    public String getStatus () { return status; }

    public TreeMap <String, ExtResource> getXRef (String xRefType) {

        validateXRefType (xRefType);
        TreeMap <String, ExtResource> tempMap = xRefMap.get (xRefType);

        return tempMap;
    }

    public ArrayList <String> getSynList () { return synList; }

    private void validateXRefType (String xRefType) {

        GenUtil.validateString(xRefType);
        if (!xResTypesList.contains (xRefType)) {
            throw new IllegalArgumentException ("Unsupported xRef type: " +
                    xRefType);
        }
        if (source.equals (xRefType)) {
            throw new IllegalArgumentException ("Source and xRef identical: "
                    + source + " " + xRefType);
        }
    }

    //generic version
    @Override
    public String toString () {

        String tempStr;

        tempStr = getDBID() + "";
        tempStr += GenUtil.TAB;

        tempStr += getID();
        tempStr += GenUtil.TAB;

        if (name != null) {
            tempStr += name;
        } else {
            tempStr += GenUtil.NA;
        }
        tempStr += GenUtil.TAB;
        
        if (definition != null) {
            tempStr += definition;
        } else {
            tempStr += GenUtil.NA;
        }
        tempStr += GenUtil.TAB;

        return tempStr;
    }
}

