/**
 * Copyright (C) 2014-2018 Lightbend Inc. <https://www.lightbend.com>
 */
package akka.stream.scaladsl

import akka.NotUsed
import akka.stream.impl.TraversalBuilder
import akka.stream.{ Graph, Attributes, 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))
}
