/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * 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.intellij.util;

import org.jdom.Attribute;
import org.jdom.Element;

import java.text.MessageFormat;
import java.util.List;

@SuppressWarnings({"HardCodedStringLiteral"})
public class JDOMCompare {
  private JDOMCompare() {
  }

  public static String diffElements(Element elementExpected, Element elementActual){
    return diffElements(elementExpected, elementActual, "", 0 );
  }

  public static String diffElements(Element elementExpected, Element elementActual, String pathPrefix, int order) {
    final String expectedTag = elementExpected.getName();
    pathPrefix = MessageFormat.format("{0}/{1}[{2}]", pathPrefix, expectedTag, order);

    final String actualTag = elementActual.getName();
    if ( ! elementExpected.getName().equals(actualTag) ) {
      return MessageFormat.format("Tag mismatch at {0}: expected={1}, actual={2}", pathPrefix, expectedTag, actualTag);
    }

    final String expectedText = elementExpected.getText().trim();
    final String actualText = elementActual.getText().trim();
    if (expectedText != null) {
      if (actualText == null) {
        return MessageFormat.format("Text content missing at {0}: expected={1}", pathPrefix, expectedText);
      } else if (!expectedText.equals(actualText)) {
        return MessageFormat.format("Text content mismatch at {0}: expected={1}, actual={2}", pathPrefix, expectedText, actualText);
      }
    } else if (actualText != null) {
        return MessageFormat.format("Text content unexpected at {0}: actual={1}", pathPrefix, actualText);
    }

    String result = diffAttributes(elementExpected, elementActual, pathPrefix, "missing");
    if ( result != null ) return result;

    result = diffAttributes(elementActual, elementExpected, pathPrefix, "unexpected");
    if ( result != null ) return result;

    List childrenExpected = elementExpected.getChildren();
    List childrenActual = elementActual.getChildren();

    for (int i = 0; i != childrenExpected.size(); i++) {
      final Element expectedChild = (Element) childrenExpected.get(i);
      if ( i >= childrenActual.size () ) {
        return MessageFormat.format("Too few children at {0}, expected={1}, actual={2}, first missing tag={3}",
                pathPrefix, childrenExpected.size(), childrenActual.size(), expectedChild.getName());
      }
      result = diffElements(expectedChild, (Element) childrenActual.get(i), pathPrefix, i);
      if ( result != null ) return result;
    }

    if ( childrenExpected.size() != childrenActual.size()) {
      return MessageFormat.format("Too many children at {0}, expected={1}, actual={2}, first unexpected tag={3}",
              pathPrefix, childrenExpected.size(), childrenActual.size(), ((Element) childrenActual.get(childrenExpected.size())).getName() );
    }

    return null;
  }

  public static String diffAttributes(Element element1, Element element2, String pathPrefix, String mode) {
    for (Object o : element1.getAttributes()) {
      final Attribute attr = (Attribute) o;
      final String name = attr.getName();
      final String value1 = attr.getValue();
      final String value2 = element2.getAttributeValue(name);
      if ( value2 == null ) {
        return MessageFormat.format("Attribute {2} at {0}/@{1}", pathPrefix, name, mode);
      }
      if ( ! value1.equals(value2)){
       return MessageFormat.format("Attribute value mismatch at {0}/@{1}, expected={2}, actual={3}", pathPrefix, name, value1, value2);
      }
    }
    return null;
  }
}