//===--- Join.swift - Protocol and Algorithm for concatenation ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

internal enum _JoinGeneratorState {
  case Start
  case GeneratingElements
  case GeneratingSeparator
  case End
}

/// A generator that presents the elements of the sequences generated
/// by `Base`, concatenated using a given separator.
public struct JoinGenerator<
  Base : GeneratorType where Base.Element : SequenceType
> : GeneratorType {

  /// Creates a generator that presents the elements of the sequences
  /// generated by `base`, concatenated using `separator`.
  ///
  /// - Complexity: O(`separator.count`).
  public init<
    Separator : SequenceType
    where
    Separator.Generator.Element == Base.Element.Generator.Element
  >(base: Base, separator: Separator) {
    self._base = base
    self._separatorData = ContiguousArray(separator)
  }

  /// Advance to the next element and return it, or `nil` if no next
  /// element exists.
  public mutating func next() -> Base.Element.Generator.Element? {
    repeat {
      switch _state {
      case .Start:
        if let nextSubSequence = _base.next() {
          _inner = nextSubSequence.generate()
          _state = .GeneratingElements
        } else {
          _state = .End
          return nil
        }

      case .GeneratingElements:
        let result = _inner!.next()
        if _fastPath(result != nil) {
          return result
        }
        if _separatorData.isEmpty {
          _inner = _base.next()?.generate()
          if _inner == nil {
            _state = .End
            return nil
          }
        } else {
          _inner = _base.next()?.generate()
          if _inner == nil {
            _state = .End
            return nil
          }
          _separator = _separatorData.generate()
          _state = .GeneratingSeparator
        }

      case .GeneratingSeparator:
        let result = _separator!.next()
        if _fastPath(result != nil) {
          return result
        }
        _state = .GeneratingElements

      case .End:
        return nil

      }
    }
    while true
  }

  internal var _base: Base
  internal var _inner: Base.Element.Generator? = nil
  internal var _separatorData: ContiguousArray<Base.Element.Generator.Element>
  internal var _separator: ContiguousArray<Base.Element.Generator.Element>.Generator?
  internal var _state: _JoinGeneratorState = .Start
}

/// A sequence that presents the elements of the `Base` sequences
/// concatenated using a given separator.
public struct JoinSequence<
  Base : SequenceType where Base.Generator.Element : SequenceType
> : SequenceType {

  /// Creates a sequence that presents the elements of `base` sequences
  /// concatenated using `separator`.
  ///
  /// - Complexity: O(`separator.count`).
  public init<
    Separator : SequenceType
    where
    Separator.Generator.Element ==
      Base.Generator.Element.Generator.Element
  >(base: Base, separator: Separator) {
    self._base = base
    self._separator = ContiguousArray(separator)
  }

  /// Return a *generator* over the elements of this *sequence*.
  ///
  /// - Complexity: O(1).
  public func generate() -> JoinGenerator<Base.Generator> {
    return JoinGenerator(
      base: _base.generate(),
      separator: _separator)
  }

  public func _copyToNativeArrayBuffer()
    -> _ContiguousArrayBuffer<Base.Generator.Element.Generator.Element> {
    var result = ContiguousArray<Generator.Element>()
    let separatorSize: Int = numericCast(_separator.count)

    let reservation = _base._preprocessingPass {
      (s: Base) -> Int in
      var r = 0
      for chunk in s {
        r += separatorSize + chunk.underestimateCount()
      }
      return r - separatorSize
    }

    if let n = reservation {
      result.reserveCapacity(numericCast(n))
    }

    if separatorSize != 0 {
      var gen = _base.generate()
      if let first = gen.next() {
        result.appendContentsOf(first)
        while let next = gen.next() {
          result.appendContentsOf(_separator)
          result.appendContentsOf(next)
        }
      }
    } else {
      for x in _base {
        result.appendContentsOf(x)
      }
    }

    return result._buffer
  }

  internal var _base: Base
  internal var _separator:
    ContiguousArray<Base.Generator.Element.Generator.Element>
}

extension SequenceType where Generator.Element : SequenceType {
  /// Returns a view, whose elements are the result of interposing a given
  /// `separator` between the elements of the sequence `self`.
  ///
  /// For example,
  /// `[[1, 2, 3], [4, 5, 6], [7, 8, 9]].joinWithSeparator([-1, -2])`
  /// yields `[1, 2, 3, -1, -2, 4, 5, 6, -1, -2, 7, 8, 9]`.
  @warn_unused_result
  public func joinWithSeparator<
    Separator : SequenceType
    where
    Separator.Generator.Element == Generator.Element.Generator.Element
  >(separator: Separator) -> JoinSequence<Self> {
    return JoinSequence(base: self, separator: separator)
  }
}

@available(*, unavailable, message="call the 'joinWithSeparator()' method on the sequence of elements")
public func join<
  C : RangeReplaceableCollectionType, S : SequenceType
  where S.Generator.Element == C
>(
  separator: C, _ elements: S
) -> C {
  fatalError("unavailable function can't be called")
}

