/*
 * Copyright (C) 2010 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.googlecode.android_scripting.language;

import com.googlecode.android_scripting.rpc.MethodDescriptor;
import com.googlecode.android_scripting.rpc.ParameterDescriptor;

import java.util.HashMap;
import java.util.Map;

/**
 * Represents the programming language supported by the SL4A.
 * 
 * @author igor.v.karp@gmail.com (Igor Karp)
 */
public class Language {

  private final static Map<Character, String> AUTO_CLOSE_MAP = buildAutoCloseMap('[', "[]", '{',
      "{}", '(', "()", '\'', "''", '"', "\"\"");

  /** Returns the initial template for newly created script. */
  public String getContentTemplate() {
    StringBuilder content = new StringBuilder(getImportStatement());
    if (content.length() != 0) {
      content.append('\n');
    }
    content.append(getRpcReceiverDeclaration(getDefaultRpcReceiver()));
    return content.toString();
  }

  /** Returns the Android package import statement. */
  protected String getImportStatement() {
    return "";
  }

  /** Returns the RPC receiver declaration. */
  protected String getRpcReceiverDeclaration(String rpcReceiver) {
    return "";
  }

  /** Returns the default RPC receiver name. */
  protected String getDefaultRpcReceiver() {
    return "droid";
  }

  /**
   * Returns the string containing opening and closing tokens if the input is an opening token.
   * Returns {@code null} otherwise.
   */
  public String autoClose(char token) {
    return AUTO_CLOSE_MAP.get(token);
  }

  /** Returns the RPC call text with given parameter values. */
  public final String getRpcText(String content, MethodDescriptor rpc, String[] values) {
    return getMethodCallText(getRpcReceiverName(content), rpc.getName(),
        rpc.getParameterValues(values));
  }

  /** Returns the RPC receiver found in the given script. */
  protected String getRpcReceiverName(String content) {
    return getDefaultRpcReceiver();
  }

  /** Returns the method call text in the language. */
  protected String getMethodCallText(String receiver, String method,
      ParameterDescriptor[] parameters) {
    StringBuilder result =
        new StringBuilder().append(getApplyReceiverText(receiver)).append(getApplyOperatorText())
            .append(method).append(getLeftParametersText());
    String separator = "";
    for (ParameterDescriptor parameter : parameters) {
      result.append(separator).append(getValueText(parameter));
      separator = getParameterSeparator();
    }
    result.append(getRightParametersText());

    return result.toString();
  }

  /** Returns the apply receiver text. */
  protected String getApplyReceiverText(String receiver) {
    return receiver;
  }

  /** Returns the apply operator text. */
  protected String getApplyOperatorText() {
    return ".";
  }

  /** Returns the text to the left of the parameters. */
  protected String getLeftParametersText() {
    return "(";
  }

  /** Returns the text to the right of the parameters. */
  protected String getRightParametersText() {
    return ")";
  }

  /** Returns the parameter separator text. */
  protected String getParameterSeparator() {
    return ", ";
  }

  /** Returns the text of the quotation. */
  protected String getQuote() {
    return "\"";
  }

  /** Returns the text of the {@code null} value. */
  protected String getNull() {
    return "null";
  }

  /** Returns the text of the {{@code true} value. */
  protected String getTrue() {
    return "true";
  }

  /** Returns the text of the false value. */
  protected String getFalse() {
    return "false";
  }

  /** Returns the parameter value suitable for code generation. */
  protected String getValueText(ParameterDescriptor parameter) {
    if (parameter.getValue() == null) {
      return getNullValueText();
    } else if (parameter.getType().equals(String.class)) {
      return getStringValueText(parameter.getValue());
    } else if (parameter.getType().equals(Boolean.class)) {
      return getBooleanValueText(parameter.getValue());
    } else {
      return parameter.getValue();
    }
  }

  /** Returns the null value suitable for code generation. */
  private String getNullValueText() {
    return getNull();
  }

  /** Returns the string parameter value suitable for code generation. */
  protected String getStringValueText(String value) {
    // TODO(igorkarp): do not quote expressions once they could be detected.
    return getQuote() + value + getQuote();
  }

  /** Returns the boolean parameter value suitable for code generation. */
  protected String getBooleanValueText(String value) {
    if (value.equals(Boolean.TRUE.toString())) {
      return getTrue();
    } else if (value.equals(Boolean.FALSE.toString())) {
      return getFalse();
    } else {
      // If it is neither true nor false it is must be an expression.
      return value;
    }
  }

  private static Map<Character, String> buildAutoCloseMap(char c1, String s1, char c2, String s2,
      char c3, String s3, char c4, String s4, char c5, String s5) {
    Map<Character, String> map = new HashMap<Character, String>(5);
    map.put(c1, s1);
    map.put(c2, s2);
    map.put(c3, s3);
    map.put(c4, s4);
    map.put(c5, s5);
    return map;
  }
}
