( function _l3_Routine_s_()
{

'use strict';

const _global = _global_;
const _ = _global_.wTools;

// --
// implementation
// --

/* qqq : for Dmytro : update jsdoc, please */
/**
 * The routine er() extend mechanism of routines constructing of routine routine.unite().
 * The routine er() adds to routine {-routine-} field {-er-} that is a functor for generating
 * of new routine similar to original routine but with changed map {-defaults-}.
 *
 * @example
 * function test_head( routine, args )
 * {
 *   let o = args[ 0 ];
 *   if( !_.mapIs( o ) )
 *   {
 *     if( o !== undefined )
 *     o = { arg : 0 };
 *     else
 *     o = Object.create( null );
 *   }
 *
 *   _.routine.options( routine, o );
 *   return o;
 * }
 *
 * function test_body( o )
 * {
 *   return o;
 * }
 * test_body.defaults = { arg : null, arg2 : 'arg2' };
 *
 * let routine = _.routine.unite( test_head, test_body );
 * console.log( routine.er === undefined );
 * // log : true
 *
 * let erhead = ( routine, args ) =>
 * {
 *   if( _.mapIs( args[ 0 ] ) )
 *   return args[ 0 ];
 *   return { 'arg' : args[ 0 ] };
 * };
 * _.routine.er( routine, erhead );
 * console.log( _.routine.is( routine.er ) );
 * // log : true
 *
 * let newRoutine = routine.er( 'arg1' );
 * console.log( newRoutine.defaults );
 * // log : { arg : 'arg1', arg2 : 'arg2' }
 *
 * var resultOld = routine();
 * console.log( resultOld );
 * // log : { arg : null, arg2 : 'arg2' }
 * var resultNew = newRoutine();
 * console.log( resultNew );
 * // log : { arg : 'arg1', arg2 : 'arg2' }
 *
 * @param { Function } routine - The routine from which generates new routine.
 * Routine should be generated by routine.unite.
 * @param { Function } erhead - The routine to make map {-defaults-} for new routine.
 * @returns { Function } - Returns original routine with functor in field {-er-}.
 * @function er
 * @throws { Error } If arguments.length neither is 1, nor 2.
 * @throws { Error } If {-routine-} is not a Function.
 * @throws { Error } If {-erhead-} is not a Function.
 * @throws { Error } If {-routine-} has not fields {-head-} and {-body-}.
 * The fields should have type Function.
 * @namespace Tools
 */

function er( routine, erhead )
{
  if( routine.er )
  return routine.er; /* Dmytro : maybe before return should be assert like : _.assert( _.routine.is( routine.er ) ) */
  routine.er = _.routine.erMake( ... arguments );
  return routine;
}

//

/* qqq : for Dmytro : update jsdoc, please */
/**
 * The routine erMake() extend mechanism of routines constructing of routine routine.unite().
 * The routine erMake() returns functor for generating of new routine similar to original
 * routine {-routine-} but with changed map {-defaults-}.
 *
 * @example
 * function test_head( routine, args )
 * {
 *   let o = args[ 0 ];
 *   if( !_.mapIs( o ) )
 *   {
 *     if( o !== undefined )
 *     o = { arg : 0 };
 *     else
 *     o = Object.create( null );
 *   }
 *
 *   _.routine.options( routine, o );
 *   return o;
 * }
 *
 * function test_body( o )
 * {
 *   return o;
 * }
 * test_body.defaults = { arg : null, arg2 : 'arg2' };
 *
 * let routine = _.routine.unite( test_head, test_body );
 * let erhead = ( routine, args ) =>
 * {
 *   if( _.mapIs( args[ 0 ] ) )
 *   return args[ 0 ];
 *   return { 'arg' : args[ 0 ] };
 * };
 * let functor = _.routine.erMake( routine, erhead );
 * console.log( _.routine.is( functor ) );
 * // log : true
 *
 * let newRoutine = functor( 'arg1' );
 * console.log( newRoutine.defaults );
 * // log : { arg : 'arg1', arg2 : 'arg2' }
 *
 * var resultOld = routine();
 * console.log( resultOld );
 * // log : { arg : null, arg2 : 'arg2' }
 * var resultNew = newRoutine();
 * console.log( resultNew );
 * // log : { arg : 'arg1', arg2 : 'arg2' }
 *
 * @param { Function } routine - The routine from which generates new routine.
 * Routine should be generated by routine.unite.
 * @param { Function } erhead - The routine to make map {-defaults-} for new routine.
 * @returns { Function } - Returns functor to generate new routine with changed map {-defaults-}.
 * @function erMake
 * @throws { Error } If arguments.length neither is 1, nor 2.
 * @throws { Error } If {-routine-} is not a Function.
 * @throws { Error } If {-erhead-} is not a Function.
 * @throws { Error } If {-routine-} has not fields {-head-} and {-body-}.
 * The fields should have type Function.
 * @namespace Tools
 */

function erMake( routine, erhead )
{

  erhead = erhead || routine.erhead || routine.head;
  let head = routine.head;
  let body = routine.body;
  let defaults = routine.defaults;

  _.assert( arguments.length === 1 || arguments.length === 2 );
  _.assert( _.routine.is( routine ) );
  _.assert( _.routine.is( erhead ) );
  _.assert( _.routine.is( head ) );
  _.assert( _.routine.is( body ) );
  _.assert( _.object.isBasic( defaults ) );

  return erMake;

  function erMake()
  {
    let self = this;
    let op = erhead.call( self, routine, arguments );

    _.assert( _.mapIs( op ) );
    _.map.assertHasOnly( op, defaults );

    er.defaults = _.props.supplement( op, defaults );

    return er;

    function er()
    {
      let result;
      let op2 = head.call( self, er, arguments );
      if( _.unrollIs( op2 ) )
      result = body.apply( self, op2 );
      else if( _.mapIs( op2 ) )
      result = body.call( self, op2 );
      return result;
    }

  }

}

// --
//
// --

function exportStringDiagnosticShallow( src )
{
  _.assert( arguments.length === 1, 'Expects exactly one argument' );
  _.assert( _.routine.is( src ) );

  if( src.name )
  return `{- routine ${src.name} -}`;
  else
  return `{- routine.anonymous -}`;
}

// --
// extension
// --

let RoutineExtension =
{

  // er

  er,
  erMake,

  // exporter

  exportString : exportStringDiagnosticShallow,
  // exportStringDiagnosticShallow : exportStringDiagnosticShallow,
  exportStringDiagnosticShallow,
  exportStringCodeShallow : exportStringDiagnosticShallow,
  // exportStringDiagnostic : exportStringDiagnosticShallow,
  // exportStringCode : exportStringDiagnosticShallow,

}

//

Object.assign( _.routine, RoutineExtension );

})();
