<?php

/**
 * This file is part of LEPTON Core, released under the GNU GPL
 * Please see LICENSE and COPYING files in your package for details, specially for terms and warranties.
 *
 * NOTICE:LEPTON CMS Package has several different licenses.
 * Please see the individual license in the header of each single file or info.php of modules and templates.
 *
 * @author          LEPTON Project
 * @copyright       2010-2022 LEPTON Project
 * @link            https://lepton-cms.org
 * @license         http://www.gnu.org/licenses/gpl.html
 * @license_terms   please see LICENSE and COPYING files in your package
 *
 */

/**
 *  This is only an abstract class for LEPTON specific classes and inside modules.
 *
 */
abstract class LEPTON_abstract
{
    /**
     *  Array with the language(-array) of the child-object.
     *  @type   array
     *
     */
    public $language = array();

    /**
     *  Array with the names of all parents (desc. order)
     *  @type   array
     *
     */
    public $parents = array();

    /**
     *  The module directory from the info.php of the child.
     *  @type   string
     *
     */
    public $module_directory = "";

    /**
     *  The module name from the info.php of the child.
     *  @type   string
     *
     */
    public $module_name = "";

    /**
     *  The module function from the info.php of the child.
     *  @type   string
     *
     */
    public $module_function = "";

    /**
     *  The module version from the info.php of the child.
     *  @type   string
     *
     */
    public $module_version = "";

    /**
     *  The module platform from the info.php of the child.
     *  @type   string
     *
     */
    public $module_platform = "";
	
    /**
     *  The module delete from the info.php of the child.
     *  @type   boolean
     *
     */
    public $module_delete = true;	

    /**
     *  The module author from the info.php of the child.
     *  @type   string
     *
     */
    public $module_author = "";

    /**
     *  The module license from the info.php of the child.
     *  @type   string
     *
     */
    public $module_license = "";

    /**
     *  The module license terms from the info.php of the child.
     *  @type   string
     *
     */
    public $module_license_terms = "";

    /**
     *  The module description from the info.php of the child.
     *  @type   string
     *
     */
    public $module_description = "";

    /**
     *  The module guid from the info.php of the child.
     *  @type   string
     *
     */
    public $module_guid = "";

    /**
     *  The module home from the info.php of the child.
     *  @type   string
     *
     */
    public $module_home = "";

    /**
     *  @var    object  The reference to the *Singleton* instance of this class.
     *  @notice         Keep in mind that a child-object has to have his own one!
     */
    static $instance;

    /**
     *  Return the instance of this class.
     *
     */
    public static function getInstance()
    {
        if (null === static::$instance)
        {
            static::$instance = new static();
            static::$instance->getParents();
            static::$instance->getLanguageFile();
            static::$instance->getModuleInfo();
            static::$instance->initialize();
        }
        return static::$instance;
    }

    /**
     *  Try to get all parents form the current instance as a simple linear list.
     */
    protected function getParents()
    {
        // First the class itself
        static::$instance->parents[] = get_class(static::$instance);

        // Now the parents
        $aTempParents = class_parents( static::$instance, true );
        foreach($aTempParents as $sParentname)
        {
            static::$instance->parents[] = $sParentname;
        }
    }

    /**
     *  Try to read the module specific info.php from the module-Directory
     *  and update the current class-properties.
     *
     */
    protected function getModuleInfo()
    {

        foreach(static::$instance->parents as $sModuleDirectory)
        {
            //  strip namespace
            $aTemp = explode("\\", $sModuleDirectory);
            foreach($aTemp as $sModuleDirectory)
            {             

                $aMainClassNames = $this->getMainClassNames( $sModuleDirectory );

                foreach($aMainClassNames as $sTempModuleDirectory)
                {
                    $sLookUpPath = __DIR__."/../../modules/".$sTempModuleDirectory."/info.php";

                    if( file_exists($sLookUpPath) )
                    {
                        require $sLookUpPath;
                        // [1.1] mandantory informations
						static::$instance->module_name = $module_name;
						static::$instance->module_directory = $module_directory;
						static::$instance->module_function = $module_function;
						static::$instance->module_version = $module_version;
						static::$instance->module_author = $module_author;
						static::$instance->module_license = $module_license;
						static::$instance->module_description = $module_description;
						static::$instance->module_guid = $module_guid;

                        // [1.2] optional informations
                        if(isset($module_platform))
                        {
                            static::$instance->module_platform = $module_platform;
                        }
                        if(isset($module_delete))
                        {
                            static::$instance->module_delete = $module_delete;
                        }						
                        if(isset($module_license_terms))
                        {
                            static::$instance->module_license_terms = $module_license_terms;
                        }						
                        if(isset($module_home))
                        {
                            static::$instance->module_home = $module_home;
                        }
                        break;
                    }
                }
            }            
        }
    }

    /**
     *  Try to get a module-spezific language file.
     */
    protected function getLanguageFile()
    {
        if(defined("LEPTON_PATH"))
        {
            $aLookUpFilenames = [
                LANGUAGE."_custom.php",
                LANGUAGE.".php",
                "EN_custom.php",
                "EN.php"
            ];

            foreach( static::$instance->parents as $sClassName)
            {
                //  strip namespace
                $aTemp = explode("\\", $sClassName);
                $bExitGracefull = false;
                foreach( $aTemp as $sClassName )
                {          
                    $aMainClassNames = $this->getMainClassNames( $sClassName );

                    foreach($aMainClassNames as $sTempModuleDirectory)
                    {
                        $lookUpPath = LEPTON_PATH."/modules/".$sTempModuleDirectory."/languages/";

                        $bFoundFile = false;

                        foreach($aLookUpFilenames as $sTempFilename)
                        {
                            if(true === file_exists( $lookUpPath.$sTempFilename ) )
                            {
                                require $lookUpPath.$sTempFilename;
                                $bFoundFile = true;
                                break;
                            }
                        }

                        if(false === $bFoundFile)
                        {
                            continue;
                        }

                        $tempName = "MOD_".strtoupper($sTempModuleDirectory);
                        if(isset(${$tempName}))
                        {
                            static::$instance->language = ${$tempName};
                            $bExitGracefull = true;
                            break;
                        }
                    }
                    
                    if(true === $bExitGracefull)
                    {
                        break;
                    }
                }
            }
        }
    }

    /**
     *  Needed for modules like e.g. "news", "topics", "bakery", "forum", "xDisplay".
     *  To be overwrite by the module(-child) itself.
     *
     *  @return array   Assoc array with at last three keys for (additional) "title","description" and "keywords"
     */
    public function getHeadInfo()
    {
        return array(
            "title"         => "",
            "description"   => "",
            "keywords"      => ""
        );
    }

    /**
     *  Private function to "handle" sub_class-names
     *
     */
    protected function getMainClassNames( $sAnyClassname )
    {
        $aElements = explode("_",  $sAnyClassname);

        $sTempName = array_shift($aElements);

        $aReturnValue = array( $sTempName );

        foreach($aElements as $term)
        {
            $sTempName .= "_".$term;
            $aReturnValue[] = $sTempName;
        }

        if( 1 < count($aReturnValue) )
        {
            $aReturnValue = array_reverse( $aReturnValue );
        }

        return $aReturnValue;
    }

    /**
     *  Public function to "store" a given section_id into the $_SESSION (superglobal).
     *
     *  @param  integer iSectionID  A valid section_id. If not given or NULL parsed, the last global "section_id" is used!.
     *  @return nothing
     *
     *  @code{.php}
	 *		$this->saveLastEditSection( $section_id);
	 *      // or looking into global skope.
	 *      $this->saveLastEditSection();
	 *      // or static
	 *      LEPTON_abstact::saveLastEditSection();
	 *	@endcode
	 *
     */
    public static function saveLastEditSection( $iSectionID = NULL )
    {
        if( NULL === $iSectionID )
        {
            if( true === isset($GLOBALS['section_id']) )
            {
                $_SESSION['last_edit_section'] = $GLOBALS['section_id'];
            }
        } else {
            $_SESSION['last_edit_section'] = $iSectionID;
        }
    }

    public static function getConstants()
    {
        // "static::class" here does the magic
        $reflectionClass = new \ReflectionClass(static::class);
        return $reflectionClass->getConstants();
    }
    /**
     *  Abstact declarations - to be overwrite by the child-instance.
     */
    abstract protected function initialize();

	/**
	 * Show the module info
	 *
	 * @access  public
	 * @param   $modvalues  As optional array containing module specialized values
	 * @param   $bPrompt    True for direct output via echo, false for returning the generated source.
	 * @return  mixed       Depending on the $bPrompt param: boolean or string.
	 */
	public function showmodinfo( $modvalues = null, $bPrompt = true )
	{
		// create a default data set
		$showmodinfo = array(
	/* the leptoken */
			'leptoken'		=> get_leptoken() // $leptoken
	/* this object including translations (THIS->language[ code ]) */
			,"THIS"			=> $this
	/* the color to be used for frames */
			,"COLOR"		=> ( $this->module_function == "page" ? "olive" : "blue" )
	/* the header title to be shown */
			,"HEADER"		=> $this->module_name
	/* the module description to be shown */
			,"DESCRIPTION"	=> ( isset( $this->module_description ) ? $this->module_description : "" )
	/* the full url to the image to be shown */
			,"IMAGE_URL"	=> LEPTON_URL."/modules/".$this->module_directory. "/icon.png"
	/* array of buttons to be shown (or not) and how */
			,"BUTTONS"		=> array(
				/*
				 * Listed below are the 4 default buttons, but additional buttons can be added in $modvalues
				 *
				 * Supported attributes:
				 *  AVAILABLE	=> true (show active), false (show as no) or hide (do not show, default if not available or empty);
				 *  URL			=> url for support details/contact (required when AVAILABLE => true, otherwise not used)
				 *  TITLE		=> title to be shown for support button if not default
				 *  ICON		=> icon to be shown if not hide
				 */
				 "LIVE_SUPPORT"	=> array( "AVAILABLE"	=> false
											,"URL"		=> ""
											,"TITLE"	=> ""
											,"ICON"		=> "call square"
										)
				,"FORUM_SUPPORT"=> array( "AVAILABLE"	=> true
											,"URL"		=> "https://forum.lepton-cms.org/viewforum.php?f=14"	// LEPTON forum for addons
											,"TITLE"	=> ""
											,"ICON"		=> "align left"
										)
				,"README"		=> array( "AVAILABLE"	=> "hide"
											,"URL"		=> ""
											,"TITLE"	=> ""
											,"ICON"		=> "book"
										)
				,"HELP"			=> array( "AVAILABLE"	=> "hide"
											,"URL"		=> ""
											,"TITLE"	=> ""
											,"ICON"		=> "question"
										)
				)
	/* the data to be shown in sequence added. Also spacers with unique key can be added.
	 * values can also be a link
	 * empty values are not shown
	 * also spacers can be added, but key must be unique
	 */
			,"INFO"			=> array()
		);
		if ( isset( $this->module_version ) && empty( $this->module_version ) === false )
			{ $showmodinfo[ "INFO" ][ "module_version" ]	= $this->module_version; }
		if ( isset( $this->module_platform ) && empty( $this->module_platform ) === false )
			{ $showmodinfo[ "INFO" ][ "module_platform" ]	= $this->module_platform; }
		if ( isset( $this->module_guid ) && empty( $this->module_guid ) === false )
			{ $showmodinfo[ "INFO" ][ "module_guid" ]		= $this->module_guid; }
		if ( isset( $this->module_author ) && empty( $this->module_author ) === false )
			{ $showmodinfo[ "INFO" ][ "module_author" ]		= $this->module_author; }
		if ( isset( $this->module_license ) && empty( $this->module_license ) === false )
		{
			$showmodinfo[ "INFO" ][ "S1" ]				= "spacer2";
			$showmodinfo[ "INFO" ][ "module_license" ]	= $this->module_license;
			if ( isset( $this->module_license_terms ) && empty( $this->module_license_terms ) === false )
			{
				$showmodinfo[ "INFO" ][ "module_license" ]	.= "<br />" . $this->module_license_terms;
			}
		}
		if ( isset( $this->module_home ) && empty( $this->module_home ) === false )
		{
			$showmodinfo[ "INFO" ][ "S2" ]				= "spacer2";
			if ( strpos ( $this->module_home , "href" ) == 0 )
			{
				$showmodinfo[ "INFO" ][ "module_home" ]		= "<a href='".$this->module_home."' >".$this->module_home."</a>";
			}
			else
			{
				$showmodinfo[ "INFO" ][ "module_home" ]		= $this->module_home;
			}
		}
		// additional info values are normally not shown or are already used explicitly

        // [1] load input params if available and merge with the default data set
        if ( ( is_null( $modvalues ) === false )    // ist gesetzt
            && ( is_array( $modvalues ) === true )  // ist ein array
            && ( !empty( $modvalues ) )             // ist NICHT leer
        )
        {
            $showmodinfo = array_replace_recursive( $showmodinfo, $modvalues );
        }

		// prepare needed libraries
		$oTWIG = lib_twig_box::getInstance();

		// create & render twig template engine
		$sSource = $oTWIG->render(
			 "@theme/showmodinfo.lte"
			,$showmodinfo
		);
		
		if( true === $bPrompt )
		{
			echo $sSource;

			return true;
		} else {
			return $sSource;
		}
	}

	/**
	 * Generate a hash for form validation
	 *
	 * @access  public
	 * @param   none
	 * @return  hash string
	 */
	public function generateHash()
	{
		// add function if not present
		if ( false === function_exists( "createGUID" ))
		{
		    require_once LEPTON_PATH . DIRECTORY_SEPARATOR . "framework" . DIRECTORY_SEPARATOR . "functions" . DIRECTORY_SEPARATOR . "function.createGUID.php" ;
		}

		// create & return the hash
		return createGUID();
	}
        
        /**
         * [7.1] L* V.5: additional custom-classes
         * Try to get an instance from a "setting"-class or the "settings_custom" of the parent.
         * 
         * @since   5.5.0
         * @return  mixed: Instance of the (custom-) class, or NULL if none found.
         * 
         */
        public function getEditorSettings()
        {
            // [1] base class name
            $sBaseClassName = $this->parents[0];
            
            // [2] custom class?
            $sCustom = (class_exists( $sBaseClassName."_settings_custom", true)) ? "_custom" : "";
            // [2.1] class exists?
            if(class_exists($sBaseClassName."_settings".$sCustom, true))
            {
                //  [2.2] try to get instance of the class
                return eval("return ".$sBaseClassName."_settings".$sCustom."::getInstance();");    
            }
            else
            {   
                //  [2.3] class not exists - return NULL
                return NULL;
            }
        }
        
        /**
         * [7.2] L* V.5: like [7.1] but more flexible.
         * Usefull to get informations from other classes/modules out of this context.
         * 
         * @since 5.5.0
         * @param string $basename  Default is "settings".
         * @param string $classname Default is "" - looking for the parent of this instance.
         * 
         * @return mixed Instance of the (custom-) class, or NULL if none found.
         * 
         */
        public function getAdditionalClass( string $basename="settings", string $classname="") : mixed
        {
            // [1] Basename
            if($classname === "")
            {
                $classname = $this->parents[0];
            }
            
            // [2] Custom?
            $custom = (class_exists($classname."_".$basename."_custom", true)) ? "_custom" : "";
            
            // [3] class exists?
            if( class_exists( $classname."_".$basename.$custom, true))
            {
                return eval("return ".$classname."_".$basename.$custom."::getInstance();");
            }
            else
            {
                return NULL;
            }
        }
}