/*
 * Copyright 2008 Google Inc.
 *
 * 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.gwt.dev.javac.typemodel;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.dev.javac.typemodel.test.EnumInterface;
import com.google.gwt.dev.javac.typemodel.test.EnumOfInterface;
import com.google.gwt.dev.javac.typemodel.test.MyEnum;
import com.google.gwt.dev.javac.typemodel.test.TestAnnotation;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;

import junit.framework.TestCase;

/**
 * Tests for {@link JEnumType}.
 */
public class JEnumTypeTest extends TestCase {

  private final boolean logToConsole = false;

  private final ModuleContext moduleContext = new ModuleContext(logToConsole
      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
      "com.google.gwt.dev.javac.typemodel.TypeOracleTest");

  public JEnumTypeTest() throws UnableToCompleteException {
  }

  private final TypeOracle typeOracle = moduleContext.getOracle();

  /**
   * Test method for
   * {@link com.google.gwt.core.ext.typeinfo.JEnumType#getEnumConstants()}.
   *
   * @throws NotFoundException
   */
  public void testGetEnumConstants() throws NotFoundException {
    JClassType type = typeOracle.getType(MyEnum.class.getName());
    JEnumType enumType = type.isEnum();
    assertNotNull(enumType);

    JEnumConstant[] enumConstants = enumType.getEnumConstants();
    assertEquals(3, enumConstants.length);

    for (int i = 0; i < enumConstants.length; ++i) {
      JEnumConstant enumConstant = enumConstants[i];
      // Check the ordinal
      assertEquals(i, enumConstant.getOrdinal());

      // Check that the name matches what reflection expects at the current
      // ordinal.
      assertEquals(MyEnum.class.getEnumConstants()[i].name(),
          enumConstant.getName());
    }
  }

  /**
   * Test method for
   * {@link com.google.gwt.core.ext.typeinfo.JClassType#getFields()}.
   *
   * @throws NotFoundException
   */
  public void testGetFields() throws NotFoundException {
    JClassType type = typeOracle.getType(MyEnum.class.getName());
    JEnumType enumType = validateTypeIsEnum(type);

    assertEquals(5, enumType.getFields().length);
  }

  /**
   * Test method for
   * {@link com.google.gwt.core.ext.typeinfo.JClassType#getField(String)}.
   *
   * @throws NotFoundException
   */
  public void testGetFieldString() throws NotFoundException {
    JClassType type = typeOracle.getType(MyEnum.class.getName());

    validateTypeIsEnum(type);

    type.getField("VAL0");
    type.getField("VAL1");
    type.getField("VAL2");
    type.getField("instanceField");
    type.getField("e");
  }

  /**
   * Test method for {@link JEnumType#getConstructors()}
   *
   * @throws NotFoundException
   * @throws NotFoundException
   */
  public void testGetConstructors() throws NotFoundException {
    JClassType type = typeOracle.getType(MyEnum.class.getName());
    JEnumType enumType = validateTypeIsEnum(type);

    // Enum constructors are not reflected.
    assertEquals(0, enumType.getConstructors().length);
  }

  /**
   * Test method for {@link JEnumType#getMethods()}
   *
   * @throws NotFoundException
   */
  public void testGetMethods() throws NotFoundException {
    JClassType type = typeOracle.getType(MyEnum.class.getName());
    JEnumType enumType = validateTypeIsEnum(type);

    assertEquals(3, enumType.getMethods().length);
  }

  /**
   * Test an enum that implements an interface.
   *
   * @throws NotFoundException
   */
  public void testInterface() throws NotFoundException {
    JClassType type = typeOracle.getType(EnumOfInterface.class.getName());
    JEnumType enumType = validateTypeIsEnum(type);
    JClassType[] intf = enumType.getImplementedInterfaces();
    assertEquals(1, intf.length);
    assertEquals(EnumInterface.class.getName(),
        intf[0].getQualifiedSourceName());
    JMethod getExtra = intf[0].getMethod("getExtra", TypeOracle.NO_JTYPES);
    TestAnnotation annotation = getExtra.getAnnotation(TestAnnotation.class);
    assertNotNull(annotation);
    assertEquals("EnumInterface getExtra", annotation.value());
    JEnumConstant[] constants = enumType.getEnumConstants();
    assertEquals(2, constants.length);
    assertEquals("A", constants[0].getName());
    annotation = constants[0].getAnnotation(TestAnnotation.class);
    assertNotNull(annotation);
    assertEquals("A", annotation.value());
    JClassType aClass = (JClassType) constants[0].getType().isClass();
    {
      JMethod[] methods = aClass.getInheritableMethods();
      assertEquals(10, methods.length);
      boolean found = false;
      for (JMethod method : methods) {
        if ("name".equals(method.getName())) {
          found = true;
          // TODO(jat); any other verification here?
        }
      }
      assertTrue(found);
    }
    {
      JMethod[] methods = aClass.getOverridableMethods();
      assertEquals(4, methods.length);
      // TODO(jat): verify getExtra is from A's anonymous subclass of
      // EnumInterface when/if that is implemented.
      boolean found = false;
      for (JMethod method : methods) {
        if ("getExtra".equals(method.getName())) {
          found = true;
          // TODO(jat); any other verification here?
        }
      }
      assertTrue(found);
    }
  }

  /**
   * Test method for {@link com.google.gwt.core.ext.typeinfo.JEnumType#isEnum()}
   * .
   *
   * @throws NotFoundException
   */
  public void testIsEnum() throws NotFoundException {
    JClassType type = typeOracle.getType(MyEnum.class.getName());
    validateTypeIsEnum(type);
  }

  private static JEnumType validateTypeIsEnum(JClassType type)
      throws NotFoundException {
    JEnumType maybeEnum = type.isEnum();
    assertNotNull(maybeEnum);

    JClassType enumType = type.getOracle().getType(Enum.class.getName());

    assertTrue(enumType.isAssignableFrom(maybeEnum));

    return maybeEnum;
  }
}
