package com.bina.varsim.types;

import com.google.common.base.Splitter;

import java.rmi.UnexpectedException;
import java.util.*;

import static com.google.common.collect.Lists.newArrayList;

/**
 * Created by guoy28 on 10/5/16.
 * CLass for VCF INFO field.
 * TODO: this class should be augmented to handle FORMAT field
 * TODO: constructors of this class should honor specifications outlined in VCF header
 */
public class VCFInfo {
    final private Map<String, VCFInfoElement> info2Value;

    /**
     * parse INFO field string
     * store each key value pair in a map
     * @param infoString
     */
    public VCFInfo(final String infoString) throws UnexpectedException {
        this.info2Value = new HashMap<String, VCFInfoElement>();
        Iterable<String> infos = Splitter.on(';').split(infoString);
        for (String i : infos) {
            List<String> keyAndValue = newArrayList(Splitter.on('=').split(i));

            if (keyAndValue.size() > 1) {
                this.info2Value.put(keyAndValue.get(0), new VCFInfoElement(keyAndValue.get(0), keyAndValue.get(1)));
            } else {
                //must be boolean or flag
                this.info2Value.put(keyAndValue.get(0), new VCFInfoElement(Boolean.class));
            }
        }
    }

    /**
     * return value indexed by id, if id is absent
     * return null
     * @param id
     * @return
     */
    public <T> T getValue(String id, Class<T> type) {
        return type.cast(this.info2Value.containsKey(id) ? this.info2Value.get(id).getValue(type) : null);
    }
    private class VCFInfoElement<T> {
        private T value;
        private Class<T> type;

        /**
         * parse comma separated value and store it
         * in proper types
         * @param id
         * @param vcfIdValue
         */
        public VCFInfoElement(final String id, String vcfIdValue) throws UnexpectedException {
            this.type = getType(id);
            String[] valueArray = vcfIdValue.split(",");
            if (type == int[].class) {
                int[] nums = new int[valueArray.length];
                for (int i = 0; i < valueArray.length; i++) {
                    nums[i] = Integer.parseInt(valueArray[i]);
                }
                this.value = type.cast(nums);
            } else if (type == String[].class) {
                    this.value = type.cast(valueArray);
            } else {
                throw new UnexpectedException("ERROR: only Integer and String supported for INFO field (" + id + ").");
            }
        }

        /**
         * store id as a boolean field
         */
        public VCFInfoElement(final Class<T> type) {
            this.value = type.cast(true);
        }

        /**
         * return appropriate values based on types
         * @return return should be casted before being return
         */
        private <T> T getValue(Class<T> t) {
            return t.cast(value);
        }
    }

    /**
     * return hard-coded type for some INFO IDs (including some reserved IDs in VCF
     * spec)
     * TODO: replace hard-coded infoID-type mapping with VCF header defined mapping
     *
     "##INFO=<ID=SVLEN,Number=.,Type=Integer,Description=\"Length of variant\">\n" +
     "##INFO=<ID=POS2,Number=1,Type=Integer,Description=\"1-based Start position of source sequence\">\n" +
     "##INFO=<ID=END2,Number=1,Type=Integer,Description=\"1-based End position of source sequence\">\n" +
     "##INFO=<ID=SVTYPE,Number=1,Type=String,Description=\"Type of structural variant\">\n" +
     "##INFO=<ID=CHR2,Number=1,Type=String,Description=\"Chromosome of source sequence\">\n" +
     " source sequence deleted (SELFISHNESS); source sequence accepted (CHIVALRY).\">\n"

     * @param infoID
     * @return
     */
    public static Class getType(final String infoID) {
        if (infoID.equals("SVLEN") || infoID.equals("POS2") || infoID.equals("END2") || infoID.equals("END")
                || infoID.equals("DP")) {
            return int[].class;
        } else if (infoID.equals("SVTYPE") || infoID.equals("CHR2") || infoID.equals("TRAID")) {
            return String[].class;
        } else {
            //unrecognized INFO ID, return String for now
            return String[].class;
        }
    }
}

@@ -122,16 +122,55 @@
 .util.*;
+%0Aimport java.util.stream.StreamSupport;
 %0A%0Aimport
@@ -896,20 +896,16 @@
     
-List%3C
 String
-%3E
+%5B%5D
  key
@@ -915,28 +915,36 @@
 Value = 
-newArrayList
+StreamSupport.stream
 (Splitte
@@ -961,16 +961,60 @@
 split(i)
+.spliterator(), false).toArray(String%5B%5D::new
 );%0A%0A    
@@ -1041,14 +1041,14 @@
 lue.
-size()
+length
  %3E 1
@@ -1090,39 +1090,35 @@
 .put(keyAndValue
-.get(0)
+%5B0%5D
 , new VCFInfoEle
@@ -1133,23 +1133,19 @@
 AndValue
-.get(0)
+%5B0%5D
 , keyAnd
@@ -1153,15 +1153,11 @@
 alue
-.get(1)
+%5B1%5D
 ));%0A
@@ -1270,15 +1270,11 @@
 alue
-.get(0)
+%5B0%5D
 , ne
@@ -2037,16 +2037,60 @@
 Array = 
+StreamSupport.stream(Splitter.on(',').split(
 vcfIdVal
@@ -2095,18 +2095,53 @@
 alue
+)
 .split
-(%22,%22
+erator(), false).toArray(String%5B%5D::new
 );%0A 

improve performance