/**
 * Module containing strategies to handle the 
 * paths in PHP. 
 *
 * @author Eric Bouwers
 */
module php/reflect/inclusion/path

strategies
  /**
   * Normalizes an _absolute_ path to the normalized path. That means that
   * all occurences of '.' are just stripped away and that every occurence
   * of '..' is removed together with the directory right before it.
   * This will give the path to the same file without walking around.
   * The strategy only works with an absolute path!
   *
   * @type String -> String
   */
  normalize-path = ?path;
     separator  := <get-path-separator> path                                                    
   ; parts      := <string-replace(|"/", "!"); string-replace(|"\\", "!"); split-at-bang> path 
   ; parts'     := <[ id  | filter(not(?"." <+ ?"")) ]> parts
   ; parts''    := <repeat-until-fixpoint(once-filter-this-and-before(?".."))> parts'
   ; normalized := <separate-by(|separator); concat-strings> parts''                            

  /**
   * Parses a path to a list of directories
   *
   * @type String -> List(String)
   */
  path-to-list =
   split-at-char(|58)

 /**
  * Determine wheter or not a path is relative. A
  * path is php-relative if it starts with a '.'
  *
  * @type String -> String
  */
  is-php-relative =
      ?path
    ; string-starts-with(|".")

  /**
   * Takes an _absolute_ path and determines the path separator.
   *
   * @type String -> String
   */
  get-path-separator = ?path;
    if <string-starts-with(|"/")> path
    then !"/"
    else !"\\"
    end

/**
 * Utility functions, These would better be off in stratego-lib
 */ 
strategies
  /**
   * Splits a string at every backslash
   *
   * @type String -> List(String)
   */
  split-at-bang = 
    split-at-char(|33)
  
  /**
   * Splits a string at every occurence of a given character.
   * 
   * @param chr Number of the character to split at
   * @type String -> List(String)
   */
  split-at-char(|chr) =
         explode-string
       ; CharSplitInit(|chr) 
       ; rec x( CharSplitExit <+ CharSplitNext ; x)  

rules      
  CharSplitInit(|chr) : 
         x -> ([], [], x, chr)
  CharSplitExit       : 
        (xs, cs, [], _) ->  <reverse> [<reverse; implode-string> cs|xs]
  CharSplitNext       : 
        (xs, cs, [y|ys], chr) -> result
                    where if <eq> (y,chr)
                          then result := ([<reverse; implode-string> cs | xs], [], ys, chr)
                          else result := (xs, [y|cs], ys, chr)
                          end
 

  
/**
 * Strategies that are used by both current- and working directory
 * paths.
 */
strategies 
  /**
   * we do not use the defined 'find-in-path' because we always want to work with 
   * the full path to a file.
   */
  find-file-in-directory(add-prefix|filename) =
       get-php-include-path
     ; path-to-list
     ; map(add-prefix)
     ; ?pathlist
     ; <fetch-elem(<concat-strings; tmp-file-exists> [<id>,"/",filename])> pathlist
     ; !FILE(<normalize-path>)  
  
  /**
   * Add a directory to a path.
   *
   * @param get-dir The strategy that builds the dir to add
   * @param path    The path to which the dir should be added
   */
  add-dir(get-dir | path ) =
     if   <is-php-relative> path
     then <conc-strings> (<get-dir>, path)
     else !path
     end
  
  /**
   * Saves a path to the configuration table.
   *
   * @param dir  The name to be used in the storage
   * @param path The path to save
   */
  set-php-direcory(|dir, path) =
    phpdir := <php-directory-config-name(|dir)>
  ; if  <not(string-ends-with(|"/"))> path
    then path' := <conc-strings> (path,"/")
    else path' := path
    end
  ; if   <is-php-relative> path'
    then <set-config> (phpdir, <conc-strings> (<get-php-working-directory>, path'))
    else <set-config> (phpdir, path')
    end

  /**
   * Retrieves a path from the configuration table.
   *
   * @param dir  The name of the path
   */
  get-php-direcory(|dir) =
    phpdir := <php-directory-config-name(|dir)>
   ;(  <get-config> phpdir
    <+ <conc-strings> (<current-working-dir>,"/")
    )

  /**
   * Wrapes a directory within "php-" and "-directory"
   */
  php-directory-config-name(|dir) =
    <conc-strings> ("php-",<conc-strings> (dir, "-directory") )

/**
 * Common utilities neccesary for path strategies.
 */     
rules
  /**
   * This strategy filters 2 elements from a list of the second
   * element satisfies a given strategy. It does this once.
   *
   * @param Strategy that should be satisfied
   * @type List(a) -> List(a)
   */
  once-filter-this-and-before(s):
    [x] -> [x]

  once-filter-this-and-before(s):
    [before | rest ] -> filtered
      where  <?[testitem | tail]> rest
           ; if <s> testitem                    //succes, delete this and previous node
             then filtered := tail
             else rest'    := <once-filter-this-and-before(s)> rest
                ; filtered := <conc> ([before],rest')
             end

strategies
  /**
   * Repeats an application of a strategy until the strategy has
   * no effect anymore.
   *
   * @param strategy to be applied
   * @type ATerm -> ATerm
   */
   repeat-until-fixpoint(s) = ?term;
     term' := <s> term
   ; if <eq> (term,term')
     then !term
     else <repeat-until-fixpoint(s)> term'
     end