package com.bina.varsim.types;

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

/**
 * 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>();
        String[] infos = infoString.split(";");
        for (int i = 0; i < infos.length; i++) {
            String[] keyAndValue = infos[i].split("=");
            if (keyAndValue.length > 1) {
                this.info2Value.put(keyAndValue[0], new VCFInfoElement(keyAndValue[0], keyAndValue[1]));
            } else {
                //must be boolean or flag
                this.info2Value.put(keyAndValue[0], new VCFInfoElement());
            }
        }
    }

    /**
     * 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 {
        private String[] stringFields;
        //TODO: Integer should be changed to Long if varsim is used for large genome.
        private int[] numberFields;
        private Boolean flagValue;
        private Class type;

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

        /**
         * store id as a boolean field
         */
        public VCFInfoElement() {
            this.type = Boolean.class;
            this.flagValue = true;
        }

        /**
         * return appropriate values based on types
         * @return return should be casted before being return
         */
        private <T> T getValue(Class<T> t) {
            if (t == int[].class) {
                return t.cast(numberFields);
            } else if (t == String[].class) {
                return t.cast(stringFields);
            } else if (t == Boolean.class) {
                return t.cast(flagValue);
            }
            return null;
        }
    }

    /**
     * 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 if (infoID.equals("ISINV")) {
            return Boolean.class;
        } else {
            //unrecognized INFO ID, return String for now
            return String[].class;
        }
    }
}

@@ -1110,16 +1110,30 @@
 Element(
+keyAndValue%5B0%5D
 ));%0A    
@@ -1476,16 +1476,19 @@
 oElement
+%3CT%3E
  %7B%0A     
@@ -1502,182 +1502,11 @@
 ate 
-String%5B%5D stringFields;%0A        //TODO: Integer should be changed to Long if varsim is used for large genome.%0A        private int%5B%5D numberFields;%0A        private Boolean flagV
+T v
 alue
@@ -1528,16 +1528,19 @@
 te Class
+%3CT%3E
  type;%0A%0A
@@ -1670,16 +1670,21 @@
 @param v
+cfIdV
 alue%0A   
@@ -1747,16 +1747,21 @@
 String v
+cfIdV
 alue) th
@@ -1855,24 +1855,29 @@
 lueArray = v
+cfIdV
 alue.split(%22
@@ -1936,27 +1936,25 @@
         
-numberField
+int%5B%5D num
 s = new 
@@ -2062,24 +2062,16 @@
      num
-berField
 s%5Bi%5D = I
@@ -2112,32 +2112,78 @@
               %7D%0A
+                this.value = type.cast(nums);%0A
             %7D el
@@ -2239,23 +2239,31 @@
     
-stringFields = 
+this.value = type.cast(
 valu
@@ -2268,16 +2268,17 @@
 lueArray
+)
 ;%0A      
@@ -2530,16 +2530,31 @@
 Element(
+final String id
 ) %7B%0A    
@@ -2573,29 +2573,27 @@
 .type = 
-Boolean.class
+getType(id)
 ;%0A      
@@ -2607,24 +2607,31 @@
 his.
-flagV
+v
 alue = 
+type.cast(
 true
+)
 ;%0A  
@@ -2839,292 +2839,28 @@
     
-if (t == int%5B%5D.class) %7B%0A                return t.cast(numberFields);%0A            %7D else if (t == String%5B%5D.class) %7B%0A                return t.cast(stringFields);%0A            %7D else if (t == Boolean.class) %7B%0A                return t.cast(flagValue);%0A            %7D%0A            return null
+return t.cast(value)
 ;%0A  

replace specific types with generic types for vcfinfoelement