/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 myservice.mynamespace.util;

import java.util.List;
import java.util.Locale;

import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.UriInfoResource;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;

public class Util {
  
  public static EdmEntitySet getEdmEntitySet(UriInfoResource uriInfo) throws ODataApplicationException {

    List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
    // To get the entity set we have to interpret all URI segments
    if (!(resourcePaths.get(0) instanceof UriResourceEntitySet)) {
      // Here we should interpret the whole URI but in this example we do not support navigation so we throw an
      // exception
      throw new ODataApplicationException("Invalid resource type for first segment.", HttpStatusCode.NOT_IMPLEMENTED
          .getStatusCode(), Locale.ENGLISH);
    }

    UriResourceEntitySet uriResource = (UriResourceEntitySet) resourcePaths.get(0);

    return uriResource.getEntitySet();
  }
  
  public static Entity findEntity(EdmEntityType edmEntityType, EntityCollection entitySet,
      List<UriParameter> keyParams) throws ODataApplicationException {

    List<Entity> entityList = entitySet.getEntities();

    // loop over all entities in order to find that one that matches
    // all keys in request e.g. contacts(ContactID=1, CompanyID=1)
    for (Entity entity : entityList) {
      boolean foundEntity = entityMatchesAllKeys(edmEntityType, entity, keyParams);
      if (foundEntity) {
        return entity;
      }
    }

    return null;
  }

  public static boolean entityMatchesAllKeys(EdmEntityType edmEntityType, Entity entity, List<UriParameter> keyParams) 
      throws ODataApplicationException {

    // loop over all keys
    for (final UriParameter key : keyParams) {
      // key
      String keyName = key.getName();
      String keyText = key.getText();
      
      // Edm: we need this info for the comparison below
      EdmProperty edmKeyProperty = (EdmProperty) edmEntityType.getProperty(keyName);
      Boolean isNullable = edmKeyProperty.isNullable();
      Integer maxLength = edmKeyProperty.getMaxLength();
      Integer precision = edmKeyProperty.getPrecision();
      Boolean isUnicode = edmKeyProperty.isUnicode();
      Integer scale = edmKeyProperty.getScale();
      // get the EdmType in order to compare
      EdmType edmType = edmKeyProperty.getType();
      EdmPrimitiveType edmPrimitiveType = (EdmPrimitiveType) edmType;

      // Runtime data: the value of the current entity
      // don't need to check for null, this is done in olingo library
      Object valueObject = entity.getProperty(keyName).getValue();

      // now need to compare the valueObject with the keyText String
      // this is done using the type.valueToString //
      String valueAsString = null;
      try {
        valueAsString = edmPrimitiveType.valueToString(valueObject, isNullable, maxLength, precision, scale, isUnicode);
      } catch (EdmPrimitiveTypeException e) {
        throw new ODataApplicationException("Failed to retrieve String value", HttpStatusCode.INTERNAL_SERVER_ERROR
            .getStatusCode(), Locale.ENGLISH, e);
      }

      if (valueAsString == null) {
        return false;
      }

      boolean matches = valueAsString.equals(keyText);
      if (!matches) {
        // if any of the key properties is not found in the entity, we don't need to search further
        return false;
      }
    }

    return true;
  }
  
  /**
   * Example:
   * For the following navigation: DemoService.svc/Categories(1)/Products
   * we need the EdmEntitySet for the navigation property "Products"
   *
   * This is defined as follows in the metadata:
   * <code>
   * 
   * <EntitySet Name="Categories" EntityType="OData.Demo.Category">
   * <NavigationPropertyBinding Path="Products" Target="Products"/>
   * </EntitySet>
   * </code>
   * The "Target" attribute specifies the target EntitySet
   * Therefore we need the startEntitySet "Categories" in order to retrieve the target EntitySet "Products"
   */
  public static EdmEntitySet getNavigationTargetEntitySet(EdmEntitySet startEdmEntitySet,
      EdmNavigationProperty edmNavigationProperty)
      throws ODataApplicationException {

    EdmEntitySet navigationTargetEntitySet = null;

    String navPropName = edmNavigationProperty.getName();
    EdmBindingTarget edmBindingTarget = startEdmEntitySet.getRelatedBindingTarget(navPropName);
    if (edmBindingTarget == null) {
      throw new ODataApplicationException("Not supported.",
              HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
    }

    if (edmBindingTarget instanceof EdmEntitySet) {
      navigationTargetEntitySet = (EdmEntitySet) edmBindingTarget;
    } else {
      throw new ODataApplicationException("Not supported.",
              HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
    }

    return navigationTargetEntitySet;
  }
}
