package org.specs.runner

import org.specs._
import org.specs.specification._
import java.util.regex.Pattern.compile

/**
 * This class can be used to search for specifications on a given path 
 * and execute them.<br>
 * Usage: <code>object myFileRunner extends SpecsFileRunner(path, pattern)</code><br>
 * Where <code>path</code> is a path to a directory containing scala files (it can be a glob: i.e. "dir/**/*spec.scala")
 * and <code>pattern</code> is a regular expression which is supposed to match an object name extending a Specification
 * class named ".*Spec.*"
 * 
 * The systems and examples can also be filtered by specifying patterns. These patterns can be passed as System properties,
 * by specifying -Dsus="regexp" or -Dexample="regexp".
 */  
class SpecsFileRunner(path: String, specFilterPattern: String, susFilterPattern: String, exampleFilterPattern: String) extends 
  SpecsFinder(path, specFilterPattern, true) with Console {
  
  /** short constructor with no filter for sus or examples. */
  def this(path: String, specFilterPattern: String) = this(path, specFilterPattern, ".*", ".*")

  /** short constructor with the path only. */
  def this(path: String) = this(path, ".*", ".*", ".*")
  
  /** filtered specs to run. */
  override lazy val specs = filter(collectSpecs(asOneSpecification))

  /** pattern for the sus. */
  lazy val susFilter = compile(susPattern)

  /** pattern for the examples. */
  lazy val exampleFilter = compile(examplePattern)
  
  /** filter a list of specifications. */
  def filter(specifications: List[Specification]): List[Specification] = {
    specifications.flatMap(filter(_)).toList
  }

  /** 
   * filter a specification.
   * @return None if the resulting specification has no SUS or systems
   */
  def filter(specification: Specification): Option[Specification] = {
    specification.subSpecifications = specification.subSpecifications.flatMap(filter(_)).toList
    specification.systems = specification.systems.flatMap(filter(_)).toList
    if (specification.subSpecifications.isEmpty && specification.systems.isEmpty)
      None
    else
      Some(specification)
  }

  /** 
   * filter a SUS.
   * @return None if the resulting SUS has no examples
   */
  def filter(sus: Sus): Option[Sus] = {
    if (susFilter.matcher(sus.description).find) {
      filterExamples(sus) match {
        case s if s.examples.size > 0 => Some(s)
        case _ => None
      }
    } 
    else
      None
  }

  /** 
   * filter the examples of a SUS according to the regular expression.
   * @return None if the resulting SUS has no examples
   */
  def filterExamples(sus: Sus): Sus = {
    if (exampleFilterPattern == ".*") // to speed up the execution
      sus
    else {
      sus.examples_=(sus.examples.flatMap(filterExample(_)).toList)
      sus
    }
  }
  /** 
   * filter one example.
   * @return None if the example does not match the expected regular expression
   */
  def filterExample(example: Example): Option[Example] = {
    if (exampleFilter.matcher(example.description).find) 
      Some(example)
   else
      None
  }

  /** 
   * @return either the system property named "sus" or the class attribute
   */
  def susPattern: String = {
    System.getProperty("sus") match {
      case null => susFilterPattern
      case something => something
    }
  } 
  /** 
   * @return either the system property named "example" or the class attribute
   */
  def examplePattern: String = {
    System.getProperty("example") match {
      case null => exampleFilterPattern
      case something => something
    }
  } 
}