/**
 * Copyright © 2006-2016 Web Cohesion (info@webcohesion.com)
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.webcohesion.enunciate.modules.jackson.api.impl;

import com.webcohesion.enunciate.api.ApiRegistrationContext;
import com.webcohesion.enunciate.api.datatype.*;
import com.webcohesion.enunciate.modules.jackson.model.EnumTypeDefinition;
import com.webcohesion.enunciate.modules.jackson.model.ObjectTypeDefinition;
import com.webcohesion.enunciate.modules.jackson.model.SimpleTypeDefinition;
import com.webcohesion.enunciate.modules.jackson.model.TypeDefinition;
import com.webcohesion.enunciate.modules.jackson.model.types.JsonArrayType;
import com.webcohesion.enunciate.modules.jackson.model.types.JsonClassType;
import com.webcohesion.enunciate.modules.jackson.model.types.JsonMapType;
import com.webcohesion.enunciate.modules.jackson.model.types.JsonType;

import java.util.LinkedList;
import java.util.List;

/**
 * @author Ryan Heaton
 */
public class DataTypeReferenceImpl implements DataTypeReference {

  private final String label;
  private final String slug;
  private final List<ContainerType> containers;
  private final DataType dataType;
  private final JsonType jsonType;
  private final String dataTypeFormat;
  private final ApiRegistrationContext registrationContext;

  public DataTypeReferenceImpl(JsonType jsonType, ApiRegistrationContext registrationContext) {
    this(jsonType, null, registrationContext);
  }

  public DataTypeReferenceImpl(JsonType jsonType, String jsonStringFormat, ApiRegistrationContext registrationContext) {
    String label;
    LinkedList<ContainerType> containers = null;
    String slug = null;
    DataType dataType = null;


    while (jsonType instanceof JsonArrayType || jsonType instanceof JsonMapType || (jsonType instanceof JsonClassType && ((JsonClassType) jsonType).getTypeDefinition() instanceof SimpleTypeDefinition)) {
      if (jsonType instanceof JsonArrayType) {
        containers = containers == null ? new LinkedList<ContainerType>() : containers;
        containers.add(ContainerType.array);
        jsonType = ((JsonArrayType) jsonType).getComponentType();
      }
      else if (jsonType instanceof JsonMapType) {
        containers = containers == null ? new LinkedList<ContainerType>() : containers;
        containers.add(ContainerType.map);
        jsonType = ((JsonMapType) jsonType).getValueType();
      }
      else if (((JsonClassType) jsonType).getTypeDefinition() instanceof EnumTypeDefinition) {
        break;
      }
      else {
        jsonType = ((SimpleTypeDefinition) ((JsonClassType) jsonType).getTypeDefinition()).getBaseType();
      }
    }

    if (jsonType instanceof JsonClassType) {
      TypeDefinition typeDef = ((JsonClassType) jsonType).getTypeDefinition();
      if (typeDef instanceof ObjectTypeDefinition) {
        dataType = new ObjectDataTypeImpl((ObjectTypeDefinition) typeDef, registrationContext);
      }
      else if (typeDef instanceof EnumTypeDefinition) {
        dataType = new EnumDataTypeImpl((EnumTypeDefinition) typeDef, registrationContext);
      }
      else {
        throw new IllegalStateException();
      }
      label = dataType.getLabel();
      slug = dataType.getSlug();
    }
    else if (jsonType instanceof JsonMapType) {
      label = "object";
    }
    else {
      label = jsonType.isBoolean() ? "boolean" : jsonType.isNumber() ? "number" : jsonType.isString() ? "string" : "object";

      if (jsonType.isArray()) {
        containers = containers == null ? new LinkedList<ContainerType>() : containers;
        containers.add(ContainerType.array);
      }
    }


    this.jsonType = jsonType;
    this.dataTypeFormat = jsonStringFormat == null ? jsonType.getFormat() : jsonStringFormat;
    this.label = label;
    this.slug = slug;
    this.containers = containers;
    this.dataType = dataType;
    this.registrationContext = registrationContext;
  }

  public JsonType getJsonType() {
    return jsonType;
  }

  @Override
  public BaseType getBaseType() {
    return jsonType.isBoolean() ? BaseType.bool : jsonType.isNumber() ? BaseType.number : jsonType.isString() ? BaseType.string : BaseType.object;
  }

  @Override
  public String getLabel() {
    return this.label;
  }

  @Override
  public String getSlug() {
    return this.slug;
  }

  @Override
  public List<ContainerType> getContainers() {
    return this.containers;
  }

  @Override
  public DataType getValue() {
    return this.dataType;
  }

  @Override
  public Example getExample() {
    Example example = null;
    if (this.dataType instanceof ObjectDataTypeImpl) {
      ObjectTypeDefinition typeDefinition = ((ObjectDataTypeImpl) this.dataType).typeDefinition;
      example = typeDefinition == null || typeDefinition.getContext().isDisableExamples() ? null : new DataTypeExampleImpl(typeDefinition, this.containers, registrationContext);
    }
    else if (this.dataType instanceof EnumDataTypeImpl) {
      String body = "...";
      List<? extends Value> values = this.dataType.getValues();
      if (values != null && !values.isEmpty()) {
        body = values.get(0).getValue();
      }
      example = new CustomExampleImpl('"' + body + '"');
    }
    return example;
  }

  @Override
  public String getBaseTypeFormat() {
    return this.dataTypeFormat;
  }
}
