/*
 * Copyright (C) 2019 Cricin
 *
 * 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 cn.cricin.folivora.dom;

import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.util.xml.GenericAttributeValue;
import com.intellij.util.xml.reflect.DomExtender;
import com.intellij.util.xml.reflect.DomExtenderEP;
import com.intellij.util.xml.reflect.DomExtensionsRegistrar;

import org.jetbrains.android.dom.AndroidDomElement;
import org.jetbrains.android.dom.AttributeProcessingUtil;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.facet.AndroidFacet;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Set;

import javax.annotation.Nonnull;

@SuppressWarnings("unchecked")
public final class FolivoraDomExtender extends DomExtender<AndroidDomElement> {
  private static HashMap<Object, Class> sValueClasses = new HashMap<>();

  static {
    try {
      Class legacyAttributeFormatClass = Class.forName("org.jetbrains.android.dom.attrs.AttributeFormat");
      sValueClasses.put(Enum.valueOf(legacyAttributeFormatClass, "Boolean"), boolean.class);
      sValueClasses.put(Enum.valueOf(legacyAttributeFormatClass, "Reference"), ResourceValue.class);
      sValueClasses.put(Enum.valueOf(legacyAttributeFormatClass, "Dimension"), ResourceValue.class);
      sValueClasses.put(Enum.valueOf(legacyAttributeFormatClass, "Color"), ResourceValue.class);
    } catch (ClassNotFoundException ignore) {
    }
    try {
      Class.forName("com.android.ide.common.rendering.api.AttributeFormat");
      sValueClasses.put(com.android.ide.common.rendering.api.AttributeFormat.BOOLEAN, boolean.class);
      sValueClasses.put(com.android.ide.common.rendering.api.AttributeFormat.REFERENCE, ResourceValue.class);
      sValueClasses.put(com.android.ide.common.rendering.api.AttributeFormat.DIMENSION, ResourceValue.class);
      sValueClasses.put(com.android.ide.common.rendering.api.AttributeFormat.COLOR, ResourceValue.class);
    } catch (ClassNotFoundException ignore) {
    }
  }

  @Override
  public boolean supportsStubs() {
    return false;
  }

  private static Class getValueClass(Object format) {
    Class cls = sValueClasses.get(format);
    return cls == null ? String.class : cls;
  }

  @Override
  public void registerExtensions(@Nonnull AndroidDomElement element,
                                 @Nonnull final DomExtensionsRegistrar registrar) {
    final AndroidFacet facet = AndroidFacet.getInstance(element);

    if (facet == null) {
      return;
    }

    AttributeProcessingUtil.AttributeProcessor callback = (xmlName, attrDef, parentStyleableName)
      -> {
      Set<?> formats = attrDef.getFormats();
      Class valueClass = formats.size() == 1 ? getValueClass(formats.iterator().next()) : String
        .class;
      registrar.registerAttributeChildExtension(xmlName, GenericAttributeValue.class);
      return registrar.registerGenericAttributeValueChildExtension(xmlName, valueClass);
    };

    try {
      FolivoraAttrProcessing.registerFolivoraAttributes(facet, element, callback);
    } catch (Exception ignore) {}
  }


  public static void install() {
    ExtensionPoint<DomExtenderEP> point = Extensions.getRootArea().getExtensionPoint
      (DomExtenderEP.EP_NAME);
    String clazzName = FolivoraDomExtender.class.getCanonicalName();
    DomExtenderEP androidDomExtenderEp = null;
    DomExtenderEP[] eps = point.getExtensions();
    for (DomExtenderEP ep : eps) {
      if (clazzName.equals(ep.extenderClassName)) {
        return;// already registered
      }
      if (ep.extenderClassName.endsWith("AndroidDomExtender")) {
        androidDomExtenderEp = ep;
      }
    }
    if (androidDomExtenderEp != null) {
      DomExtenderEP ep = new DomExtenderEP();
      ep.setPluginDescriptor(androidDomExtenderEp.getPluginDescriptor());
      ep.domClassName = AndroidDomElement.class.getCanonicalName();
      ep.extenderClassName = FolivoraDomExtender.class.getCanonicalName();
      try {
        Field field = ep.getClass().getDeclaredField("myExtender");
        field.setAccessible(true);
        field.set(ep, new FolivoraDomExtender());
        point.registerExtension(ep);
      } catch (Exception ignore) {}
    }
  }
}