/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2002-2009, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id$

package scala.collection.immutable

import util.matching.Regex
import annotation.experimental

/** An incomplete implementation of all the methods on Vector,
 *  written as static methods which take a String as a faux "this"
 *  argument and which are all entirely String-specific, with the
 *  attendant improvements in performance, return types, etc.
 *  I looked for the fastest way to do these operations so there
 *  is some unattractiveness.
 *
 *  @author Paul Phillips
 *  @version 2.8
 */

@experimental
object StringVector
{  
  private def noSuch(msg: String) = throw new NoSuchElementException(msg)
  
  final def foreach[T](s: String, f: Char => T): Unit = {
    val len = s.length
    var i = 0
    while (i < len) {
      f(s charAt i)
      i += 1
    }
  }
  
  final def filter(s: String, f: Char => Boolean): String = {
    val len = s.length
    val chars = new Array[Char](len)
    var index, i = 0
    while (i < len) {
      val c = s charAt i
      if (f(c)) {
        chars(index) = c
        index += 1
      }
      i += 1
    }
    if (index == i) s
    else new String(chars, 0, index)
  }
  
  final def map(s: String, f: Char => Char): String = {
    val len = s.length
    val chars = new Array[Char](len)
    var i = 0
    while (i < len) {
      chars(i) = f(s charAt i)
      i += 1
    }
    new String(chars, 0, len)
  }
  
  final def flatMap(s: String, f: Char => String): String = {
    val sb = new StringBuilder
    val len = s.length
    var i = 0
    while (i < len) {
      sb append f(s charAt i)
      i += 1
    }
    sb.toString()
  }
  
  final def take(s: String, len: Int): String = 
    if (len <= 0) ""
    else if (len >= s.length) s
    else s.substring(0, len)
    
  final def drop(s: String, len: Int): String = 
    if (len >= s.length) "" 
    else if (len <= 0) s
    else s.substring(len, s.length)
  
  final def takeRight(s: String, len: Int): String =
    if (len <= 0) ""
    else if (len >= s.length) s
    else s.substring(s.length - len, s.length)
  
  final def dropRight(s: String, len: Int): String =
    if (len <= 0) s
    else if (len >= s.length) ""
    else s.substring(0, s.length - len)
  
  final def splitAt(s: String, index: Int): (String, String) =
    if (index <= 0) ("", s)
    else if (index >= s.length) (s, "")
    else (take(s, index), drop(s, index))
  
  final def head(s: String): Char = 
    if (s == "") noSuch("head of empty list")
    else s charAt 0
  
  final def tail(s: String): String =
    if (s == "") noSuch("tail of empty list")
    else drop(s, 1)
  
  final def last(s: String): Char =
    if (s == "") noSuch("empty.last")
    else s charAt (s.length - 1)
  
  final def init(s: String): String =
    if (s == "") noSuch("empty.init")
    else dropRight(s, 1)
  
  final def endsWith(s: String, suffix: String) =
    takeRight(s, suffix.length) == suffix
    
  final def startsWith(s: String, prefix: String, offset: Int = 0) =
    take(drop(s, offset), prefix.length) == prefix
  
  final def takeWhile(s: String, f: Char => Boolean) = {
    val len = s.length
    var i = 0
    while (i < len && f(s charAt i)) i += 1
    
    take(s, i)
  }
  final def dropWhile(s: String, f: Char => Boolean) = {
    val len = s.length
    var i = 0
    while (i < len && f(s charAt i)) i += 1
    
    drop(s, i)
  }
  final def indexWhere(s: String, p: Char => Boolean, from: Int = 0): Int = {
    val len = s.length
    var i = from
    while (i < len) {
      if (p(s charAt i))
        return i
    }
    -1
  }
  final def lastIndexWhere(s: String, p: Char => Boolean): Int = lastIndexWhere(s, p, s.length - 1)
  final def lastIndexWhere(s: String, p: Char => Boolean, end: Int): Int = {
    var i = end
    while (i >= 0) {
      if (p(s charAt i))
        return i
      i -= 1
    }
    -1
  }
  
  final def reverse(s: String): String = {
    val sb = new StringBuilder
    var i = s.length - 1
    while (i >= 0) {
      sb append (s charAt i)
      i -= 1
    }
    sb.toString
  }
  
  final def find(s: String, p: Char => Boolean): Option[Char] = {
    val len = s.length
    var i = 0
    while (i < len) {
      val c = s charAt i
      if (p(c)) return Some(c)
    }
    None
  }
    
  final def count(s: String, p: Char => Boolean): Int = {
    val len = s.length
    var total, i = 0
    while (i < len)
      if (p(s charAt i))
        total += 1
    
    total
  }
  final def headOption(s: String): Option[Char] =
    if (s.isEmpty) None else Some(s charAt 0)
  final def lastOption(s: String): Option[Char] =
    if (s.isEmpty) None else Some(last(s))
  
  final def slice(s: String, from: Int, to: Int) =
    s.substring(from, to)
    
  final def span(s: String, p: Char => Boolean): (String, String) = {
    val len = s.length
    var i = 0
    while (p(s charAt i)) i += 1
    
    splitAt(s, i)
  }
  
  final def partition(s: String, p: Char => Boolean): (String, String) = {
    val len = s.length
    val sb1, sb2 = new StringBuilder
    var i = 0
    while (i < len) {
      val c = s charAt i
      if (p(c)) sb1 append c else sb2 append c
      i += 1
    }
    (sb1.toString, sb2.toString)
  }
}