/*
 * Copyright 2013 GWT project.
 *
 * 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.resources.gss;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.css.SourceCodeLocation;
import com.google.common.css.compiler.ast.CssCompilerPass;
import com.google.common.css.compiler.ast.CssDeclarationNode;
import com.google.common.css.compiler.ast.CssFunctionArgumentsNode;
import com.google.common.css.compiler.ast.CssFunctionNode;
import com.google.common.css.compiler.ast.CssFunctionNode.Function;
import com.google.common.css.compiler.ast.CssLiteralNode;
import com.google.common.css.compiler.ast.CssPropertyNode;
import com.google.common.css.compiler.ast.CssPropertyValueNode;
import com.google.common.css.compiler.ast.CssValueNode;
import com.google.common.css.compiler.ast.DefaultTreeVisitor;
import com.google.common.css.compiler.ast.ErrorManager;
import com.google.common.css.compiler.ast.GssError;
import com.google.common.css.compiler.ast.MutatingVisitController;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.resources.client.ImageResource.ImageOptions;
import com.google.gwt.resources.client.ImageResource.RepeatStyle;
import com.google.gwt.resources.ext.ResourceContext;
import com.google.gwt.resources.ext.ResourceGeneratorUtil;
import com.google.gwt.resources.gss.ast.CssDotPathNode;

import java.util.List;

public class ImageSpriteCreator extends DefaultTreeVisitor implements CssCompilerPass {
  private static final String SPRITE_PROPERTY_NAME = "gwt-sprite";
  private final MutatingVisitController visitController;
  private final ErrorManager errorManager;
  private final ResourceContext context;
  private final JClassType imageResourceType;

  public ImageSpriteCreator(MutatingVisitController visitController, ResourceContext context,
      ErrorManager errorManager) {
    this.visitController = visitController;
    this.errorManager = errorManager;
    this.context = context;
    this.imageResourceType = context.getGeneratorContext().getTypeOracle().findType(
        ImageResource.class.getName());
  }

  @Override
  public boolean enterDeclaration(CssDeclarationNode declaration) {
    String propertyName = declaration.getPropertyName().getPropertyName();

    if (SPRITE_PROPERTY_NAME.equals(propertyName)) {
      createSprite(declaration);
      return true;
    }

    return super.enterDeclaration(declaration);
  }

  @Override
  public void runPass() {
    assert imageResourceType != null;

    visitController.startVisit(this);
  }

  private CssDeclarationNode buildBackgroundDeclaration(String imageResource, String repeatText,
      SourceCodeLocation location) {
    // build the url function
    CssFunctionNode urlFunction = new CssFunctionNode(Function.byName("url"), location);
    CssDotPathNode imageUrl = new CssDotPathNode(imageResource + ".getSafeUri.asString", null,
        null);
    CssFunctionArgumentsNode urlFunctionArguments = new CssFunctionArgumentsNode();
    urlFunctionArguments.addChildToBack(imageUrl);
    urlFunction.setArguments(urlFunctionArguments);

    // build left offset
    CssDotPathNode left = new CssDotPathNode(imageResource + ".getLeft", "-", "px");

    // build top offset
    CssDotPathNode top = new CssDotPathNode(imageResource + ".getTop", "-", "px");

    // build repeat
    CssLiteralNode repeat = new CssLiteralNode(repeatText);


    CssPropertyNode propertyNode = new CssPropertyNode("background", location);
    CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(urlFunction,
        left, top, repeat));
    return new CssDeclarationNode(propertyNode, propertyValueNode);
  }

  private CssDeclarationNode buildHeightDeclaration(String imageResource,
      SourceCodeLocation location) {
    CssPropertyNode propertyNode = new CssPropertyNode("height", location);
    CssValueNode valueNode = new CssDotPathNode(imageResource + ".getHeight", null, "px");

    CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(valueNode));
    return new CssDeclarationNode(propertyNode, propertyValueNode);
  }

  private CssDeclarationNode buildOverflowDeclaration(SourceCodeLocation location) {
    CssPropertyNode propertyNode = new CssPropertyNode("overflow", location);
    CssValueNode valueNode = new CssLiteralNode("hidden", location);

    CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(valueNode));
    return new CssDeclarationNode(propertyNode, propertyValueNode);
  }

  private CssDeclarationNode buildWidthDeclaration(String imageResource,
      SourceCodeLocation location) {
    CssPropertyNode propertyNode = new CssPropertyNode("width", location);
    CssValueNode valueNode = new CssDotPathNode(imageResource + ".getWidth", null, "px");

    CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(valueNode));
    return new CssDeclarationNode(propertyNode, propertyValueNode);
  }

  private void createSprite(CssDeclarationNode declaration) {
    List<CssValueNode> valuesNodes = declaration.getPropertyValue().getChildren();

    if (valuesNodes.size() != 1) {
      errorManager.report(new GssError(SPRITE_PROPERTY_NAME + " must have exactly one value",
          declaration.getSourceCodeLocation()));
    }

    String imageResource = valuesNodes.get(0).getValue();


    JMethod imageMethod;
    try {
      imageMethod = ResourceGeneratorUtil.getMethodByPath(context.getClientBundleType(),
          ImmutableList.of(imageResource), imageResourceType);
    } catch (NotFoundException e) {
      errorManager.report(new GssError("Unable to find ImageResource method "
          + imageResource + " in " + context.getClientBundleType().getQualifiedSourceName() + " : "
          + e.getMessage(), declaration.getSourceCodeLocation()));
      return;
    }

    ImageOptions options = imageMethod.getAnnotation(ImageOptions.class);
    RepeatStyle repeatStyle = options != null ? options.repeatStyle() : RepeatStyle.None;

    Builder<CssDeclarationNode> listBuilder = ImmutableList.builder();
    SourceCodeLocation sourceCodeLocation = declaration.getSourceCodeLocation();

    String repeatText;
    switch (repeatStyle) {
      case None:
        repeatText = " no-repeat";
        listBuilder.add(buildHeightDeclaration(imageResource, sourceCodeLocation));
        listBuilder.add(buildWidthDeclaration(imageResource, sourceCodeLocation));
        break;
      case Horizontal:
        repeatText = " repeat-x";
        listBuilder.add(buildHeightDeclaration(imageResource, sourceCodeLocation));
        break;
      case Vertical:
        repeatText = " repeat-y";
        listBuilder.add(buildWidthDeclaration(imageResource, sourceCodeLocation));
        break;
      case Both:
        repeatText = " repeat";
        break;
      default:
        errorManager.report(new GssError("Unknown repeatStyle " + repeatStyle,
            sourceCodeLocation));
        return;
    }

    listBuilder.add(buildOverflowDeclaration(sourceCodeLocation));
    listBuilder.add(buildBackgroundDeclaration(imageResource, repeatText, sourceCodeLocation));

    visitController.replaceCurrentBlockChildWith(listBuilder.build(), false);
  }
}

@@ -6088,25 +6088,24 @@
 etValue();%0A%0A
-%0A
     JMethod 

remove duplicated new line