package org.pharmgkb.parser.vcf.model;

import com.google.common.base.Preconditions;
import org.pharmgkb.parser.vcf.VcfUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;

/**
 * This class contains sample data for a VCF position line.
 *
 * @author Mark Woon
 */
public class VcfSample {

  private LinkedHashMap<String, String> m_properties = new LinkedHashMap<>();

  public VcfSample(@Nullable List<String> keys, @Nullable List<String> values) {
    if (keys == null) {
      if (values == null || values.size() == 0) {
        return;
      }
      throw new IllegalArgumentException("keys is null but values is not");
    } else if (values == null) {
      throw new IllegalArgumentException("values is null but keys is not");
    }
    Preconditions.checkArgument(keys.size() == values.size(), "Number of keys does not match number of values");
    for (int x = 0; x < keys.size(); x++) {
      m_properties.put(keys.get(x), values.get(x));
    }
    init();
  }

  public VcfSample(@Nonnull LinkedHashMap<String, String> properties) {
    m_properties = properties;
    init();
  }

  private void init() {
    for (Map.Entry<String, String> entry : m_properties.entrySet()) {
      if (entry.getKey().contains("\n") || entry.getValue().contains("\n")) {
        throw new IllegalArgumentException("FORMAT [[[" + entry.getKey() + "=" + entry.getValue() + "]]] contains a newline");
      }
    }
  }

  public @Nullable String getProperty(@Nonnull String key) {
    return m_properties.get(key);
  }

  /**
   * Returns the value for the reserved property as the type specified by both {@link ReservedFormatProperty#getType()}
   * and {@link ReservedFormatProperty#isList()}.
   * @param <T> The type specified by {@code ReservedInfoProperty.getType()} if {@code ReservedFormatProperty.isList()}
   *           is false;
   *           otherwise {@code List<V>} where V is the type specified by {@code ReservedFormatProperty.getType()}.
   */
  @SuppressWarnings("InfiniteRecursion")
  public @Nullable <T> T getProperty(@Nonnull ReservedFormatProperty key) {
    return VcfUtils.convertProperty(key, getProperty(key.getId()));
  }

  public boolean containsProperty(@Nonnull String key) {
    return m_properties.containsKey(key);
  }

  public boolean containsProperty(@Nonnull ReservedFormatProperty key) {
    return m_properties.containsKey(key.getId());
  }

  public void putProperty(@Nonnull String key, @Nullable String value) {
    m_properties.put(key, value);
  }

  public void putProperty(@Nonnull ReservedFormatProperty key, @Nullable String value) {
    m_properties.put(key.getId(), value);
  }

  public void removeProperty(@Nonnull String key) {
    m_properties.remove(key);
  }

  public void removeProperty(@Nonnull ReservedFormatProperty key) {
    m_properties.remove(key.getId());
  }

  public void clearProperties() {
    m_properties.clear();
  }

  /**
   * @return A set of the property keys, which has guaranteed order
   */
  @Nonnull
  public Set<String> getPropertyKeys() {
    // LinkedHashMap.keySet() returns a LinkedKeySet, which has guaranteed order
    return m_properties.keySet();
  }

  @Nonnull
  public Set<Map.Entry<String, String>> propertyEntrySet() {
    return m_properties.entrySet();
  }

}

@@ -44,87 +44,99 @@
 ort 
-com.google.common.base.Preconditions;%0Aimport org.pharmgkb.parser.vcf.VcfUtils;%0A
+java.util.LinkedHashMap;%0Aimport java.util.List;%0Aimport java.util.Map;%0Aimport java.util.Set;
 %0Aimp
@@ -210,19 +210,85 @@
 ort 
-java.util.*
+com.google.common.base.Preconditions;%0Aimport org.pharmgkb.parser.vcf.VcfUtils
 ;%0A%0A/
@@ -914,16 +914,24 @@
 .size(),
+%0A       
  %22Number
@@ -934,19 +934,29 @@
 mber of 
-key
+FORMAT entrie
 s does n
@@ -974,20 +974,28 @@
 mber of 
-valu
+sample entri
 es%22);%0A  

improve error message when # of format entries does not match # of sample entries