/*
 * Copyright 2009 the original author or authors.
 *
 * 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 org.spockframework.runtime;

import java.util.Iterator;

import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.control.SourceUnit;

import org.spockframework.runtime.model.*;
import org.spockframework.util.Assert;
import org.spockframework.util.TextUtil;

/**
 *
 * @author Peter Niederwieser
 */
public class ExpressionInfoBuilder {
  private final String text;
  private final String adjustedText;
  private final TextPosition startPos;
  private final Iterable<Object> values;
  private final String[] lines;

  public ExpressionInfoBuilder(String text, TextPosition startPos, Iterable<Object> values) {
    this.text = text;
    this.startPos = startPos;
    this.values = values;
    adjustedText = TextUtil.repeatChar(' ', startPos.getColumnIndex()) + text;
    lines = adjustedText.split("\n");
  }

  public ExpressionInfo build() {
    SourceUnit unit = SourceUnit.create("Spec expression", adjustedText);
    unit.parse();
    unit.completePhase();
    unit.convert();

    BlockStatement blockStat = unit.getAST().getStatementBlock();
    Assert.that(blockStat != null && blockStat.getStatements().size() == 1);
    Statement stat = blockStat.getStatements().get(0);
    Assert.that(stat instanceof ExpressionStatement);
    Expression expr = ((ExpressionStatement)stat).getExpression();

    ExpressionInfo exprInfo = new ExpressionInfoConverter(lines).convert(expr);

    // IDEA: rest of this method could be moved to ExpressionInfoConverter (but: might make EIC less testable)
    // IDEA: could make ExpressionInfo immutable
    Iterator<?> iter = values.iterator();
    for (ExpressionInfo info : exprInfo.inPostfixOrder(false)) {
      info.setText(findText(info.getRegion()));
      if (!iter.hasNext())
        Assert.fail("Missing value for expression '%s' in condition '%s'", info.getText(), text);
      info.setValue(iter.next());
      if (startPos.getLineIndex() > 0)
        info.shiftVertically(startPos.getLineIndex());
    }

    return exprInfo;
  }

  private String findText(TextRegion region) {
    if (region == TextRegion.NOT_AVAILABLE)
      return ExpressionInfo.TEXT_NOT_AVAILABLE;
    
    try {
      String text = "";
      for (int i = 0; i <= region.getEnd().getLineIndex(); i++) {
        String line = lines[i];
        if (i == region.getEnd().getLineIndex()) line = line.substring(0, region.getEnd().getColumnIndex());
        if (i == region.getStart().getLineIndex()) line = line.substring(region.getStart().getColumnIndex());
        text += line;
        if (i != region.getEnd().getLineIndex()) text += '\n';
      }
      return text;
    } catch (IndexOutOfBoundsException e) {
      return ExpressionInfo.TEXT_NOT_AVAILABLE;
    }
  }
}
