/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.spark.sql.catalyst.analysis.resolver

import org.apache.spark.sql.catalyst.expressions.{Alias, Expression}
import org.apache.spark.sql.catalyst.util.toPrettySQL
import org.apache.spark.sql.types.Metadata

/**
 * [[AutoGeneratedAliasProvider]] is a tool to create auto-generated aliases in the plan.
 * All the auto-generated aliases have to be registered in [[ExpressionIdAssigner]].
 */
class AutoGeneratedAliasProvider(expressionIdAssigner: ExpressionIdAssigner) {

  /**
   * Create a new auto-generated [[Alias]]. If the `name` is not provided, [[toPrettySql]] is going
   * to be used to generate a proper alias name. We call [[ExpressionIdAssigner.mapExpression]] to
   * register this alias in the [[ExpressionIdAssigner]].
   */
  def newAlias(
      child: Expression,
      name: Option[String] = None,
      explicitMetadata: Option[Metadata] = None): Alias = {
    newAliasImpl(child = child, name = name, explicitMetadata = explicitMetadata)
  }

  /**
   * Create a new auto-generated [[Alias]]. If the `name` is not provided, [[toPrettySql]] is going
   * to be used to generate a proper alias name. We don't call
   * [[ExpressionIdAssigner.mapExpression]] for outer aliases, because thy should be manually
   * mapped in the context of an outer query.
   */
  def newOuterAlias(
      child: Expression,
      name: Option[String] = None,
      explicitMetadata: Option[Metadata] = None): Alias = {
    newAliasImpl(
      child = child,
      name = name,
      explicitMetadata = explicitMetadata,
      skipExpressionIdAssigner = true
    )
  }

  def newAliasImpl(
      child: Expression,
      name: Option[String] = None,
      explicitMetadata: Option[Metadata] = None,
      skipExpressionIdAssigner: Boolean = false): Alias = {
    val alias = Alias(
      child = child,
      name = name.getOrElse(toPrettySQL(child))
    )(
      explicitMetadata = explicitMetadata
    )
    if (skipExpressionIdAssigner) {
      alias
    } else {
      expressionIdAssigner.mapExpression(alias)
    }
  }
}
