/*
 * Copyright (C) 2014-2020 Lightbend Inc. <https://www.lightbend.com>
 */

package akka.stream.scaladsl

import akka.NotUsed
import akka.stream.{ Graph, Shape }

trait GraphApply {
  /**
   * Creates a new [[Graph]] by passing a [[GraphDSL.Builder]] to the given create function.
   */
  def create[S <: Shape]()(buildBlock: GraphDSL.Builder[NotUsed] => S): Graph[S, NotUsed] = {
    val builder = new GraphDSL.Builder
    val s = buildBlock(builder)

    createGraph(s, builder)
  }

  /**
   * Creates a new [[Graph]] by importing the given graph `g1` and passing its [[Shape]]
   * along with the [[GraphDSL.Builder]] to the given create function.
   */
  def create[S <: Shape, Mat](g1: Graph[Shape, Mat])(buildBlock: GraphDSL.Builder[Mat] => (g1.Shape) => S): Graph[S, Mat] = {
    val builder = new GraphDSL.Builder
    val s1 = builder.add(g1, Keep.right)
    val s = buildBlock(builder)(s1)

    createGraph(s, builder)
  }


  [2..#

  /**
   * Creates a new [[Graph]] by importing the given graphs and passing their [[Shape]]s
   * along with the [[GraphDSL.Builder]] to the given create function.
   */
  def create[S <: Shape, Mat, [#M1#]]([#g1: Graph[Shape, M1]#])(combineMat: ([#M1#]) => Mat)(buildBlock: GraphDSL.Builder[Mat] => ([#g1.Shape#]) => S): Graph[S, Mat] = {
    val builder = new GraphDSL.Builder
    val curried = combineMat.curried
    val s##1 = builder.add(g##1, (m##1: M##1) => curried(m##1))
    [2..#val s1 = builder.add(g1, (f: M1 => Any, m1: M1) => f(m1))#
    ]
    val s = buildBlock(builder)([#s1#])

    createGraph(s, builder)
  }#

  ]

  private def createGraph[S <: Shape, Mat](shape: S, graphBuilder: GraphDSL.Builder[Mat]): Graph[S, Mat] =
    new GenericGraph(shape, graphBuilder.result(shape))
}
