/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.samples.apps.iosched.server.schedule.model;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;

/**
 * A generic holder for JSON data.
 *
 */
public class JsonDataSource implements Comparable<JsonDataSource>, Iterable<JsonObject> {
  private Enum<?> sourceType;
  private HashMap<String, JsonObject> data;

  public JsonDataSource(Enum<?> sourceType) {
    this.sourceType = sourceType;
    this.data = new HashMap<String, JsonObject>();
  }

  public JsonDataSource(Enum<?> sourceType, JsonArray arr) {
    this(sourceType);
    if (arr != null) {
      addAll(arr);
    }
  }

  public Enum<?> getSourceType() {
    return sourceType;
  }

  public JsonObject getElementById(String id) {
    return data.get(id);
  }

  @Override
  public Iterator<JsonObject> iterator() {
    return data.values().iterator();
  }

  public int size() {
    return data.size();
  }

  public void addElement(String id, JsonObject obj) {
    data.put(id, obj);
  }

  /* (non-Javadoc)
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    return obj instanceof JsonDataSource &&
        this.sourceType.equals(((JsonDataSource) obj).sourceType);
  }

  /* (non-Javadoc)
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    return sourceType.hashCode();
  }

  /* (non-Javadoc)
   * @see java.lang.Comparable#compareTo(java.lang.Object)
   */
  @Override
  public int compareTo(JsonDataSource o) {
    return sourceType.name().compareTo(o.sourceType.name());
  }

  private String getKeyProperty(JsonObject obj) {
    JsonElement idEl = obj.get("id");
    if (idEl == null) {
      // reflection is not efficient in general, but the overhead should be insignificant
      // compared to the usual times taken to load a datasource (either from HTTP, disk or
      // cloud storage)
      Method m;
      try {
        m = sourceType.getClass().getMethod("getKey");
        if (m != null) {
          return (String) m.invoke(sourceType);
        }
      } catch (Exception e) {
        throw new IllegalArgumentException("Error! Cannot add JsonArray of type "
            + sourceType.name()+" because there is no property \"id\" in the JsonArray element "
            + "nor a static getKey() method in the enum.", e);
      }
    }
    return "id";
  }

  public void addAll(JsonArray arr) {
    String idProperty = null;
    for (int i=0; i<arr.size(); i++) {
      JsonObject obj = arr.get(i).getAsJsonObject();
      if (idProperty == null) {
        idProperty = getKeyProperty(obj);
      }
      String id = obj.get(idProperty).getAsString();
      addElement(id, obj);
    }
  }
}
