<?php

/**
 * This file defines the DispositionHandler class
 *
 * @package CGProductBuilder
 * @category Utilities
 * @author  calguy1000 <calguy1000@cmsmadesimple.org>
 * @copyright Copyright 2010 by Robert Campbell
 */

namespace CGProductBuilder;

/**
 * A class to manage and interact with dispositions.
 *
 * @package CGProductBuilder
 * @see \CGProductBuilder\disposition
 */
final class DispositionHandler
{
    /**
     * @ignore
     */
    const k_MODULE = 'module';

    /**
     * @ignore
     */
    const k_CLASS = 'class';

    /**
     * @ignore
     */
    private function __construct() {}

    /**
     * @ignore
     */
    private static $_dispositions;

    /**
     * @ignore
     */
    private static function _load_dispositions()
    {
        if( !is_array(self::$_dispositions) ) {
            $mod = \cms_utils::get_module(MOD_CGPRODUCTBUILDER);
            $tmp = $mod->GetPreference(__CLASS__);
            self::$_dispositions = array();
            if( $tmp ) self::$_dispositions = unserialize($tmp);
        }
    }

    /**
     * @ignore
     */
    private static function _save_dispositions()
    {
        $tmp = null;
        if( is_array(self::$_dispositions) && count(self::$_dispositions) ) {
            $mod = \cms_utils::get_module(MOD_CGPRODUCTBUILDER);
            $mod->SetPreference(__CLASS__,serialize(self::$_dispositions));
        }
    }

    /**
     * Register a disposition.
     *
     * The class name must be unique to all registered dispositions.  It is recommended that namespaces be used.
     * This method will try to instantiate the class specified to ensure that it can be loaded properly.
     *
     * @param string $module The module that contains this disposition.
     * @param string $classname The name of the class.  The class must be derived from \CGProductBuilder\disposition
     */
    public static function register_disposition($module,$classname)
    {
        $module = (string) $module;
        $classname = (string) $classname;
        if( !$module || !$classname ) throw new \LogicException('Invalid data passed to '.__METHOD__);

        self::_load_dispositions();
        $found = false;
        foreach( self::$_dispositions as $row ) {
            if( $row[self::k_CLASS] == $classname ) {
                $found = TRUE;
                break;
            }
        }

        if( !$found ) {
            $t_mod = \cms_utils::get_module($modulename);
            $obj = new $classname; // try to instantiate it
            if( !$obj instanceof disposition ) {
                throw new \RuntimeException($classname.' is not a valid disposition class');
            }
            unset($obj);
            self::$_dispositions[] = array(self::k_MODULE=>$module,self::k_CLASS=>$classname);
            self::_save_dispositions();
        }
    }

    /**
     * Unregister all dispositions registered for a specific module.
     *
     * @param string $module
     */
    public static function unregister_module_dispositions($module)
    {
        $module = (string) $module;
        if( !$module ) throw new \LogicException('Invalid data passed to '.__METHOD__);

        self::_load_dispositions();
        $list = array();
        $fnd = FALSE;
        foreach( self::$_dispositions as $row ) {
            if( $row[self::k_MODULE] == $module ) {
                $fnd = TRUE;
            }
            else {
                $list[] = $row;
            }
        }
        if( $fnd ) {
            self::_save_dispositions();
        }
    }

    /**
     * Un-register a specific disposition by its calss name.
     *
     * @param string $class
     */
    public static function unregister_disposition_class($class)
    {
        $class = (string) $class;
        if( !$class ) throw new \LogicException('Invalid data passed to '.__METHOD__);

        self::_load_dispositions();
        $list = array();
        $fnd = FALSE;
        foreach( self::$_dispositions as $row ) {
            if( $row[self::k_CLASS] == $class ) {
                $fnd = TRUE;
            }
            else {
                $list[] = $row;
            }
        }
        if( $fnd ) {
            self::_save_dispositions();
        }
    }

    /**
     * Get the list of registered classes.
     *
     * @return string[]
     */
    public static function get_registered_classes()
    {
        self::_load_dispositions();
        $out = array();
        foreach( self::$_dispositions as $row ) {
            $out[] = $row[self::k_CLASS];
        }
        return array_unique($out);
    }

    /**
     * Get all of the registered dispositions.
     *
     * @return \CGProductBuilder\disposition[]
     */
    public static function get_dispositions()
    {
        self::_load_dispositions();
        $out = array();
        foreach( self::$_dispositions as $row ) {
            $out[] = new $row[self::k_CLASS];
        }
        return $out;
    }

    /**
     * Get a list (suitable for use in dropdowns) of dispositions.
     * The keys of the returned array are the class names.  The values are disposition friendly names.
     * Only those dispositions deemed as 'compatible' with the current install will be returned.
     *
     * @param bool $all Wether or not to return all dispositions, compatible or not.
     * @return array
     */
    public static function get_disposition_list($all = FALSE)
    {
        $all = (bool) $all;
        $list = self::get_dispositions();
        if( is_array($list) && count($list) ) {
            $out = array();
            foreach( $list as $obj ) {
                if( !$all && !$obj->compatible() ) continue;
                $classname = get_class($obj);
                if( !startswith($classname,'\\') ) $classname = '\\'.$classname;
                $out[get_class($obj)] = $obj->get_friendlyname();
            }
            return $out;
        }
    }

    /**
     * Get a list of dispositions that are ready,and can be used for disposing of the current form.
     *
     * @return array
     */
    public static function get_ready_disposition_list()
    {
        $list = self::get_dispositions();
        if( !count($list) ) return;

        $out = array();
        foreach( $list as $obj ) {
            if( !$obj->ready() ) continue;
            $classname = get_class($obj);
            if( !startswith($classname,'\\') ) $classname = '\\'.$classname;
            $out[get_class($obj)] = $obj->get_friendlyname();
        }
        return $out;
    }

    /**
     * Get dispositions matching the class name or class names supplied.
     *
     * @param string|string[] $classnames
     * @return \CGProductBuilder\disposition|\CGProductBuilder\disposition[]
     */
    public static function get_disposition($classnames)
    {
        if( !is_array($classnames) ) {
            $classnames = (string) $classnames;
            if( !$classnames ) throw new \LogicException('Invalid data passed to '.__METHOD__);
            $classnames = array($classnames);
        }

        self::_load_dispositions();
        $out = array();
        foreach( $classnames as $class ) {
            $class = trim($class);
            if( !$class ) continue;
            if( !startswith($class,'\\') ) $class = '\\'.$class;
            $fnd = FALSE;
            foreach( self::$_dispositions as $row ) {
                if( $class == $row[self::k_CLASS] ) {
                    $obj = new $row[self::k_CLASS];
                    $out[] = $obj;
                    $fnd = TRUE;
                }
            }
            if( !$fnd ) throw new \LogicException($class.' is not a registered dispositon');
        }

        if( count($out) == 1 ) return $out[0];
        return $out;
    }
} // end of class
?>
