//
//  Mix in template utilities for generators.
//

'use strict';

var _ = require( 'lodash' );

module.exports =
    {
        /**
         *  Takes an array of prompt definitions, as would be passed to `this.prompt()`, filters away those for which valid values have been supplied via the
         *  identically named command line options, fills the `this.templateData` object with those values and returns the pruned list.

         *  @method     _promptPruneByOptions
         *
         *  @param      {Array}         prompts     An array of prompt definitions as would be passed to `this.prompt()`.
         *
         *  @return     {Array}                     The prompts for which no valid value has been supplied through a command line option.
         *
         */

        _promptsPruneByOptions: function ( prompts )
        {
            var options     = this.options
            ,   data        = this.templateData
            ,   remaining   = []
            ,   prompt
            ,   name
            ,   value
            ,   final
            ,   index
            ,   validate
            ,   filter
            ;

            for ( index = prompts.length; index--; )
            {
                prompt  = prompts[ index ];
                name    = prompt.name;
                value   = options[ name ];
                final   = null;

                if ( value != null )
                {
                    if ( !(( validate = prompt.validate )) || validate( value ))
                    {
                        final = (( filter = prompt.filter )) ? filter( value ) : value;
                    }
                }

                if ( final != null )
                {
                    data[ name ] = final;
                }
                else
                {
                    remaining.unshift( prompt );
                }
            }

            return remaining;
        }

    ,
        /**
         *  Process template files; either copying them as-is from `#sourceRoot()` to `#destinationRoot()`, or expanding them with `#templateData` first.
         *
         *  @method     _templatesProcess
         *
         *  @param      {Array|Object}  templates
         *
         *    - Either an array of template files to copy or expand keeping relative paths identical in both roots.
         *      In this case, the destination path will be cleared from any '@' exempt markers that may be present in the
         *      source path.
         *
         *    - Or an object hash mapping source- to destination paths relative to each root respectively.
         *      In this case, the destination path is used as-is.
         *
         *    In either case, the hash- or array element values can be simple strings or arrays.
         *
         *    * A string indicates to do a simple `#fs.copy()`;
         *    * An array indicates to use `fs.copyTpl()` instead, its first element providing the path and the (optional) second element becoming the `options`
         *      arguments to `#fs.copyTpl()`.
         */

        _templatesProcess: function ( templates )
        {
            /* jshint forin: false */

            var src, dst, args, method
            ,   fs = this.fs
            ;

            for ( src in templates )
            {
                dst = templates[ src ];

                if ( _.isArray( dst ) )
                {
                    //  Template expansion needed.

                    method  = fs.copyTpl;
                    args    = [ this.templateData ].concat( dst.slice( 1, 2 ));
                    dst     = dst[ 0 ];
                }
                else
                {
                    method  = fs.copy;
                    args    = [];
                }

                if ( /^\d+$/.test( src ))   //  numeric index, ergo: `templates` is an `Array`.
                {
                    src = dst;

                    //  Remove exempt markers.
                    //
                    dst = dst.replace( /(^|\/)@/g, '$1' );
                }

                args.unshift( this.templatePath( src ), this.destinationPath( dst ));

                method.apply( fs, args );
            }
        }
    }
;

@@ -66,16 +66,22 @@
 ;%0A%0Avar _
+      
  = requi
@@ -94,16 +94,79 @@
 odash' )
+%0A,   fsCore  = require( 'fs' )%0A,   path    = require( 'path' )%0A
 ;%0A%0Amodul
@@ -4279,21 +4279,1385 @@
  %7D%0A        %7D
+%0A%0A    ,%0A        /**%0A         *  Make a symbolic link within %60#destinationRoot()%60.%0A         *%0A         *  @method     _symLink%0A         *%0A         *  @param      %7BString%7D        src         The path relative to %60#destinationRoot()%60 that should become the target of the symbolic link.%0A         *  @param      %7BString%7D        dst         The path relative to %60#destinationRoot()%60 that should become the symbolic link.%0A         *%0A         */%0A%0A        _symLink: function ( src, dst )%0A        %7B%0A            src = this.destinationPath( src );%0A            dst = this.destinationPath( dst );%0A%0A            this.conflicter.checkForCollision(%0A%0A                dst%0A            ,   this.read( src )%0A            ,   function ( err, status ) %7B%0A%0A                    if ( 'identical' === status ) return;%0A%0A                    //  The symlink may exist, but may link to a nonexistent file in which case %60status%60 will be %60'create'%60, so try unlinking it anyway.%0A                    //%0A                    try%0A                    %7B%0A                        fsCore.unlinkSync( dst );%0A                    %7D%0A                    catch ( error )%0A                    %7B%0A                        if ( 'ENOENT' !== error.code  ) throw error;%0A                    %7D%0A%0A                    fsCore.symlinkSync( path.relative( path.dirname( dst ), src ), dst );%0A                %7D%0A            );%0A        %7D
 %0A    %7D%0A;%0A

add mixin method for symlinking files within `this.destinationRoot()`