/*
 * 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 org.apache.olingo.fit.proxy.staticservice;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.olingo.client.api.EdmEnabledODataClient;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.ext.proxy.AbstractService;
import org.apache.olingo.ext.proxy.api.AbstractTerm;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.AccessLevel;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Account;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.AccountInfo;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Address;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Asset;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Club;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Color;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Company;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.CompanyAddress;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.CompanyCategory;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.CreditCardPI;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.CreditRecord;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Customer;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Department;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Employee;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.GiftCard;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.HomeAddress;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.IsBoss;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.LabourUnion;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Order;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.OrderDetail;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.PaymentInstrument;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Person;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Product;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.ProductDetail;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.ProductReview;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.PublicCompany;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Statement;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.StoredPI;
import org.apache.olingo.fit.proxy.staticservice.odatawcfservice.types.Subscription;

public class Service<C extends EdmEnabledODataClient> extends AbstractService<C> {

  // CHECKSTYLE:OFF (Maven checkstyle)
  private static final String COMPRESSED_METADATA =
      " ";
  private static final String METADATA_ETAG = null;
  // CHECKSTYLE:ON (Maven checkstyle)

  private static final Map<String, Service<?>> SERVICES = new ConcurrentHashMap<String, Service<?>>();

  @SuppressWarnings("unchecked")
  private static <C extends EdmEnabledODataClient> Service<C> getInstance(
      final ODataServiceVersion version, final String serviceRoot, final boolean transactional) {

    if (!SERVICES.containsKey(serviceRoot)) {
      final Service<C> instance = new Service<C>(COMPRESSED_METADATA, METADATA_ETAG,
          version, serviceRoot, transactional);
      SERVICES.put(serviceRoot, instance);
    }

    return (Service<C>) SERVICES.get(serviceRoot);
  }

  /**
   * Gives an OData 4.0 instance for given service root, operating in transactions (with batch requests).
   *
   * @param serviceRoot OData service root
   * @return OData 4.0 instance for given service root, operating in transactions (with batch requests)
   */
  public static Service<EdmEnabledODataClient> getV4(
      final String serviceRoot) {

    return getV4(serviceRoot, true);
  }

  /**
   * Gives an OData 4.0 instance for given service root.
   *
   * @param serviceRoot OData service root
   * @param transactional whether operating in transactions (with batch requests) or not
   * @return OData 4.0 instance for given service root
   */
  public static Service<EdmEnabledODataClient> getV4(
      final String serviceRoot, final boolean transactional) {

    return getInstance(ODataServiceVersion.V40, serviceRoot, transactional);
  }

  private final Map<String, Class<?>> entityTypes = new HashMap<String, Class<?>>();

  private final Map<String, Class<?>> complexTypes = new HashMap<String, Class<?>>();

  private final Map<String, Class<?>> enumTypes = new HashMap<String, Class<?>>();

  private final Map<String, Class<? extends AbstractTerm>> terms = new HashMap<String, Class<? extends AbstractTerm>>();

  public Service(final String compressedMetadata, final String metadataETag,
      final ODataServiceVersion version, final String serviceRoot, final boolean transactional) {

    super(compressedMetadata, metadataETag, version, serviceRoot, transactional);

    // CHECKSTYLE:OFF (Maven checkstyle)
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.CreditCardPI",
        CreditCardPI.class);
    entityTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Account",
        Account.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.ProductDetail",
        ProductDetail.class);
    entityTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Order",
        Order.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.Statement",
        Statement.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.Subscription",
        Subscription.class);
    entityTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Person",
        Person.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.GiftCard",
        GiftCard.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.OrderDetail",
        OrderDetail.class);
    entityTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Product",
        Product.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.PaymentInstrument",
        PaymentInstrument.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.Customer",
        Customer.class);
    entityTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Club",
        Club.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.ProductReview",
        ProductReview.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.Department",
        Department.class);
    entityTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Asset",
        Asset.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.Employee",
        Employee.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.StoredPI",
        StoredPI.class);
    entityTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Company",
        Company.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.CreditRecord",
        CreditRecord.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.LabourUnion",
        LabourUnion.class);
    entityTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.PublicCompany",
        PublicCompany.class);
    complexTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Address",
        Address.class);
    complexTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.CompanyAddress",
        CompanyAddress.class);
    complexTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.AccountInfo",
        AccountInfo.class);
    complexTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.HomeAddress",
        HomeAddress.class);
    enumTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.AccessLevel",
        AccessLevel.class);
    enumTypes.put("Microsoft.Test.OData.Services.ODataWCFService.Color",
        Color.class);
    enumTypes
    .put(
        "Microsoft.Test.OData.Services.ODataWCFService.CompanyCategory",
        CompanyCategory.class);
    terms.put("Microsoft.Test.OData.Services.ODataWCFService.IsBoss",
        IsBoss.class);
    // CHECKSTYLE:ON (Maven checkstyle)
  }

  @Override
  public Class<?> getEntityTypeClass(final String name) {
    return entityTypes.get(name);
  }

  @Override
  public Class<?> getComplexTypeClass(final String name) {
    return complexTypes.get(name);
  }

  @Override
  public Class<?> getEnumTypeClass(final String name) {
    return enumTypes.get(name);
  }

  @Override
  public Class<? extends AbstractTerm> getTermClass(final String name) {
    return terms.get(name);
  }

}
