package org.broad.igv.scatterplot;

import java.util.ArrayList;
import java.util.Map;


/*
*   Container class to encapsulate ScatterPlot data for display purposes
*
*   Note: the data reference is copied for peformance reasons, but the data
*   is not protected from modification. This could be considerd a benefit
*   or a defect.
*
*   @author martind
* */
public class ScatterPlotData {


    /**
     * Array of sample names
     */
    private String[] igvSampleNames;

    /**
     * Map of data type (symbol) =>  data array.  Data order must coincide with sample name array
     */
    private Map<String, double[]> igvDataMap;  // Data measurement keyname, data values

    /**
     * Sample attribute map.   Attribute heading => Array of values.  Value order must coincide with sample name array
     */
    private Map<String, String[]> igvSymbolMap; // Attribute keyname, symbol map


    private int igvSampleCount;
    private boolean igvDataOK;     // sample count matches map entries
    // Cached map key lists
    private ArrayList<String> igvDataNames;   // list of names used for data map keys
    private ArrayList<String> igvSymbolNames;   // list of names used for symbol map keys


    /*
    * Default null constructor is provided for derived classes.
    * */
    public ScatterPlotData() {
        // need to load data to be useful
    }

    /*
   *    Constructor for containing IGV scatterplot data.
   *
   *    Parameters:
   *    sampleNames: data sample names for N samples
   *    symbolMap: symbol names for data sample attributes; e.g. "treated",
   *            "hyper mutated", etc.
   *            Each symbol map has an array of N symbol entries.
   *    dataMap: data values for data measurements; e.g. "copy number" ,
   *            "expression", "methylation", etc.
   *            Each data map has an array of N data entries.
   *
   * */
    public ScatterPlotData(String[] sampleNames, Map<String, String[]> symbolMap,
                           Map<String, double[]> dataMap) {

        igvSampleNames = sampleNames;
        igvSampleCount = sampleNames.length;
        igvSymbolMap = symbolMap;
        igvSymbolNames = new ArrayList<String>(igvSymbolMap.keySet());
        igvDataMap = dataMap;
        igvDataNames = new ArrayList<String>(igvDataMap.keySet());

        // check for data set consistancy
        // todo: test number of key entries and compare with sample coumt
        igvDataOK = true;
    }

    /*
   *   Load or reload a data set
   *
   *    Parameters:
   *    sampleNames: data sample names for N samples
   *    symbolMap: symbol maps for data sample attributes; e.g. "treated",
   *            "hyper mutated", etc.
   *            Each symbol map has an array of N symbol entries.
   *    dataMap: data values for data measurements; e.g. "copy number" ,
   *            "expression", "methylation", etc.
   *            Each data map has an array of N data entries.
   *
   * */
    public void loadData(String[] sampleNames, Map<String, String[]> symbolMap, Map<String, double[]> dataMap) {
        igvSampleNames = sampleNames;
        igvSampleCount = sampleNames.length;
        igvSymbolMap = symbolMap;
        igvSymbolNames = new ArrayList<String>(igvSymbolMap.keySet());
        igvDataMap = dataMap;
        igvDataNames = new ArrayList<String>(igvDataMap.keySet());

        // check for data set consistancy
        // todo: test number of key entries and compare with sample coumt

        // For now, always validate data
        igvDataOK = true;
    }

    /*
    *   Returns status on data/symbol map consistancy with sample count
    * */
    public boolean isDataOK() {
        return igvDataOK;
    }

    /*
    *   Returns the number of  data samples
    * */
    public int getSampleCount() {
        return igvSampleCount;
    }

    /*
    *   Returns array of data sample names
    * */
    public String[] getSampleNames() {
        return igvSampleNames;
    }

    /*
    *   Returns the number of symbol keys in the SymbolMap
    * */
    public int getDataMapSize() {
        return igvDataMap.size();
    }

    /*
    *   Returns array of  data names
    * */
    public ArrayList<String> getDataNames() {
        return igvDataNames;
    }

    /*
   *   Returns data measurement key name for given data map index
   * */
    public String getDataKeyName(int index) {
        return igvDataNames.get(index);
    }

    /*
    *   Returns a map index for a given data measurement key name
    * */
    public int getDataKeyIndex(String keyName) {

        int labelIndex = -1;    // not assigned yet
        int nLabels = igvDataNames.size();

        for (int index = 0; index < nLabels; ++index) {
            if (igvDataNames.get(index).equals(keyName)) {
                labelIndex = index;
                // only finds the first instance
                break;
            }
        }
        return labelIndex;
    }

    /*
    *   Returns sample data measurement for a given data measurement key name
    *   and sample index.
    * */
    public double getDataKeyValue(String keyName, int sampleIndex) {
        return igvDataMap.get(keyName)[sampleIndex];
    }

    /*
   *   Returns an array of data measurements for a given data name key
   * */
    public double[] getDataValues(String name) {
        return igvDataMap.get(name);
    }

    /*
    *   Returns the number of symbol keys in the SymbolMap
    * */
    public int getSymbolMapSize() {
        return igvSymbolMap.size();
    }

    /*
   *   Returns array of  symbol names
   * */
    public ArrayList<String> getSymbolNames() {
        return igvSymbolNames;
    }

    /*
   *   Returns symbol key name for given map index
   * */
    public String getSymbolKeyName(int index) {
        return igvSymbolNames.get(index);
    }

    /*
    *   Returns a map index for a given symbol key name
    * */
    public int getSymbolKeyIndex(String name) {

        int symbolIndex = -1;    // not assigned yet
        int nSymbols = igvSymbolNames.size();

        for (int index = 0; index < nSymbols; ++index) {
            if (igvSymbolNames.get(index).equals(name)) {
                symbolIndex = index;
                break;
            }
        }
        return symbolIndex;
    }

    /*
    *   Returns a sample values for a symbol key name and sample index.
    * */
    public String getSymbolKeyValue(String keyName, int sampleIndex) {
        return igvSymbolMap.get(keyName)[sampleIndex];
    }

    /*
    *   Returns an array of all sample values for a symbol key name.
    * */
    public String[] getSymbolValues(String keyName) {
        return igvSymbolMap.get(keyName);
    }

    /*
    *   Returns an array of unique named values found
    *   for a symbol attribute key.
    * */
    public String[] getAttributeCategories(String attribute) {

        // get the symbol values and load the first unique variant
        String[] symbolValues = getSymbolValues(attribute);
        ArrayList<String> uniqueVals = new ArrayList<String>();

        // find all unique symbol values for attribute key
        for (String x : symbolValues) {
            if (!uniqueVals.contains(x))
                uniqueVals.add(x);
        }

        int nCategories = uniqueVals.size();
        String[] categories = new String[nCategories];
        uniqueVals.toArray(categories);

        return categories;
    }

    /*
    *   Returns a sample description String for a particular
    *   data sample built from all sample point information.
    * */
    public String getSampleDescription(int sampleIndex, boolean isHTML) {
        String description = "<html>" + igvSampleNames[sampleIndex] + ": <br>";
        String keyName;
        int keyCount;
        int index;

        Double Value;
        keyCount = getDataMapSize();
        for (index = 0; index < keyCount; ++index) {
            keyName = getDataKeyName(index);
            Value = getDataKeyValue(keyName, sampleIndex);
            description += keyName + " = " + Value.toString() + "<br>";
        }

        String Sample;
        keyCount = getSymbolMapSize();

        if (isHTML) {
            for (index = 0; index < keyCount; ++index) {
                keyName = getSymbolKeyName(index);
                Sample = getSymbolKeyValue(keyName, sampleIndex);
                description += keyName + " = " + Sample + "<br>";
            }

            // end of description
            description += "</html>";
        } else {
            for (index = 0; index < keyCount; ++index) {
                keyName = getSymbolKeyName(index);
                Sample = getSymbolKeyValue(keyName, sampleIndex);
                if (index < keyCount - 1)
                    description += keyName + " = " + Sample + ", ";
                else
                    description += keyName + " = " + Sample;
            }

        }
        return description;
    }

}
