<?php
/**
 * -------------------------------------------------------------------------
 *
 * The basis for a system module that has a node structure.
 *
 * -------------------------------------------------------------------------
 *
 * In the simplest case, the application being developed consists of two
 * layers. Such an application represents the imaginary first layer of
 * development. This app is a node for all callable modules, which are
 * the imaginary second layer.
 *
 * For example:
 *
 *     $someApp ─┬──> someModule1 ──> someMethod()
 *               ├──> someModule2 ──> someMethod()
 *               ├──> someModule3 ──> someMethod()
 *               └──> someModule4 ──> someMethod()
 *
 * These modules are derived from the MimimiModule class. And they are
 * each in their own subfolder, in the same place where the application
 * source file is located.
 *
 * In more complex development cases, it is necessary to be able to make
 * an arbitrary module of the second layer look like a node.
 *
 * For example:
 *
 *     $someApp ─┬──> someModule1 ──> someMethod()
 *               ├──> someModule2 ─┬──> someModuleA ──> someMethod()
 *               │                 ├──> someModuleB ──> someMethod()
 *               │                 └──> someModuleC ──> someMethod()
 *               ├──> someModule3 ──> someMethod()
 *               └──> someModule4 ──> someMethod()
 *
 * Such a module must derive from the MimimiNodeModuleWithTable or
 * MimimiNodeModule class. And all the modules it calls are located each
 * in its own subfolder, in the same place where the node module source
 * file is located.
 *
 * -------------------------------------------------------------------------
 *
 * Look at this diagram to understand how the module works:
 *
 *     │               ┌─<─ class mimimi.core/Module.php
 *     │               │                      │ └─> __construct()
 *     │               │                      │
 *     │               │                      ├─<─ property $owner
 *     │               │                      ├─<─ property $app
 *     │               │                      │
 *     └─> mimimi.core/NodeModule.php         └─<─ method run()
 *                     │    └─> __construct()*
 *                     │
 *                     ├─<─ property $myNodeFile
 *                     │
 *                     ├─<─ method __get()
 *                     └─<─ method getNodePath()
 *
 * The right arrows show the order in which app files are loaded and their
 * methods that are called when processing a request to this module. The
 * left arrows show the class from which the corresponding application file
 * is derived. They also show some public routines, some public methods, or
 * some constants that the corresponding file exposes to other application
 * modules. The method or property that has been overridden is marked with
 * an asterisk.
 *
 * -------------------------------------------------------------------------
 *
 * Implemented properties below are:
 *     PROTECTED  $myNodeFile     -->  to simulate a namespacing
 *
 * Overridden methods below are:
 *     __construct ( $owner    )  -->  to create a module instance
 *
 * Implemented methods below are:
 *     __get       ( $property )  -->  to load a module
 *     getNodePath ( $offcut   )  -->  to get the directory of this module
 *
 * -------------------------------------------------------------------------
 *
 * @package    MimimiFramework
 * @subpackage Core
 * @copyright  2022 MiMiMi Community
 *             https://mimimi.software/
 * @license    GPL-2.0
 *             https://opensource.org/license/gpl-2-0/
 *
 * -------------------------------------------------------------------------
 */

    mimimiInclude ( 'Module.php' );

    class MimimiNodeModule extends MimimiModule {

        /**
         * -----------------------------------------------------------------
         *
         * Full filename of this script file.
         *
         * -----------------------------------------------------------------
         *
         * This property only applies to node modules. Remember that the
         * property must be reset in the same way in all files declaring the
         * class of the new node module.
         *
         * -----------------------------------------------------------------
         *
         * @var    string
         * @access protected
         *
         * -----------------------------------------------------------------
         */

        protected $myNodeFile = __FILE__;

        /**
         * -----------------------------------------------------------------
         *
         * Creates this module.
         *
         * -----------------------------------------------------------------
         *
         * First we execute the same method of the parent class. We then
         * clone the HAS module that belongs to the master node (which is
         * the nearest node module). And in this clone, we will reassign
         * that our module are now the master node for all modules nested
         * in its node. That is, we become a node module in relation to
         * child modules.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   object|false  $owner  (optional) Reference to the parent module.
         * @return  void
         *
         * -----------------------------------------------------------------
         */

        public function __construct ( & $owner = FALSE ) {
            parent::__construct ( $owner );
            if ( $this->owner ) {
                $this->has        = clone $this->owner->has;
                $this->has->owner = $this;
            }
        }

        /**
         * -----------------------------------------------------------------
         *
         * Loads the same module into a non-existent property.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   string       $property  Name of the property.
         * @return  object|bool             REFERENCE to the loaded module if success.
         *                                  FALSE     if this property exists and it is protected or private.
         *
         * -----------------------------------------------------------------
         */

        public function __get ( $property ) {
            return isset ( $this->$property )
                   ? FALSE
                   : mimimiCreate ( $property, $this );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Retrieves the relative directory of this module.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   int     $offcut  (optional) ON EXIT: The number of cut left parts from the full directory.
         * @return  string           Relative directory (from the website root).
         *
         * -----------------------------------------------------------------
         */

        public function getNodePath ( & $offcut = 0 ) {
            $offcut = 0;
            $path   = '';
            $pieces = preg_split ( '~[/\\\\]~u', $this->myNodeFile );
            if ( $pieces ) {
                array_pop ( $pieces );
                if ( $pieces ) {
                    $path   = array_pop ( $pieces ) . '/';
                    $offcut = count     ( $pieces );
                    if ( $this->app ) {
                        $this->app->getNodePath ( $offcut );
                        while ( count( $pieces ) > $offcut ) {
                            $path = array_pop ( $pieces ) . '/' . $path;
                        }
                    }
                }
            }
            return $path;
        }
    };
