<?php

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

namespace CGProductBuilder;

/**
 * Define an option set.
 * An option set contains an id, name, description, and also contains a list of options.
 * This class handles saving and loading objects of this type.
 *
 * @package CGProductBuilder
 * @property-read int $id The option set id (a valid integer once saved to the database).
 * @property string $name The option set name.
 * @property string $description A description for this option set (may include HTML)
 */
class option_set
{
    /**
     * @ignore
     */
    private $_data = array('id'=>null,'name'=>null,'description'=>null,'iorder'=>null,'multiselect'=>null);

    /**
     * @ignore
     */
    private $_options = array();

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

    /**
     * @ignore
     */
    public function __get($key)
    {
        switch( $key ) {
        case 'id':
        case 'name':
        case 'description':
        case 'iorder':
            return $this->_data[$key];

        case 'multiselect':
            return (bool) $this->_data[$key];

        default:
            throw new \CmsInvalidDataException($key.' is not a valid member of '.__CLASS__);
        }
    }

    /**
     * @ignore
     */
    public function __set($key,$value)
    {
        switch( $key ) {
        case 'id':
            throw new \RuntimeException($key.' is not a settable member of '.__CLASS__);

        case 'name':
        case 'description':
            $value = trim((string) $value);
            $this->_data[$key] = $value;
            break;

        case 'iorder':
            $value = (int) $value;
            $value = max(0,$value);
            $this->_data[$key] = $value;
            break;

        case 'multiselect':
            $this->_data[$key] = \cge_utils::to_bool($value);
            break;

        default:
            throw new \CmsInvalidDataException($key.' is not a valid member of '.__CLASS__);
        }
    }

    /**
     * @ignore
     */
    public function __isset($key)
    {
        switch( $key ) {
        case 'id':
        case 'name':
        case 'description':
        case 'iorder':
        case 'multiselect':
            return isset($this->_data[$key]);

        default:
            throw new \CmsInvalidDataException($key.' is not a valid member of '.__CLASS__);
        }
    }

    /**
     * @ignore
     */
    public function __unset($key)
    {
        switch( $key ) {
        case 'id':
            throw new \RuntimeException($key.' is not a settable member of '.__CLASS__);

        case 'name':
        case 'description':
        case 'iorder':
        case 'multiselect':
            $this->_data[$key] = null;
            break;

        default:
            throw new \CmsInvalidDataException($key.' is not a valid member of '.__CLASS__);
        }

    }

    /**
     * @ignore
     */
    protected function _insert()
    {
        if( $this->id > 0 ) throw new \CmsInvalidDataException(__METHOD__.' cannot be called with a previously saved '.__CLASS__);
        $db = cmsms()->GetDb();

        // figure out a new option iorder value
        $query = 'SELECT COALESCE(MAX(iorder),0)+1 FROM '.CGPB_OPTSETS_TABLE;
        $iorder = (int) $db->GetOne($query);
        $this->_data['iorder'] = $iorder;

        $query = 'INSERT INTO '.CGPB_OPTSETS_TABLE.' (name, description, iorder, options, multiselect) VALUES (?,?,?,?,?)';
        $dbr = $db->Execute($query,array($this->name,$this->description,$this->iorder, serialize($this->_options), $this->multiselect));
        $this->_data['id'] = (int) $db->Insert_ID();
    }

    /**
     * @ignore
     */
    protected function _update()
    {
        if( $this->id < 1 ) throw new \CmsInvalidDataException(__METHOD__.' cannot be called with a previously saved '.__CLASS__);
        $db = cmsms()->GetDb();

        $query = 'UPDATE '.CGPB_OPTSETS_TABLE.' SET name = ?, description = ?, iorder = ?, options = ?, multiselect = ? WHERE id = ?';
        $dbr = $db->Execute($query,array($this->name,$this->description, $this->iorder, serialize($this->_options), $this->multiselect, $this->id));
    }

    /**
     * Ensure that the contents of the option set are valid.
     */
    public function validate()
    {
        $mod = \cms_utils::get_module(MOD_CGPRODUCTBUILDER);
        if( $this->name == '' ) throw new \CmsInvalidDataException($mod->Lang('error_name_required'));
        $t_id = self::get_id_by_name($this->name);
        if( $this->id > 0 ) {
            if( $this->iorder < 1  ) throw new \CmsInvalidDataException($mod->Lang('error_invalid_order'));
            if( $t_id > 0 && $t_id != $this->id ) throw new \CmsInvalidDataException($mod->Lang('err_optionset_exists'));
        } else {
            if( $t_id > 0 ) throw new \CmsInvalidDataException($mod->Lang('err_optionset_exists'));
        }
    }

    /**
     * Save this option set to the database.
     */
    public function save()
    {
        $this->validate();
        if( $this->id > 0 ) {
            $this->_update();
        }
        else {
            $this->_insert();
        }
    }

    /**
     * Load an option set from the database.
     *
     * @param int $id The option set id.
     * @return \CGProductBuilder\option_set
     */
    public static function load($id)
    {
        $id = (int) $id;
        if( $id < 1 ) throw new \CmsInvalidDataException('Invalid id value passed to '.__METHOD__);

        $db = cmsms()->GetDb();
        $query = 'SELECT id,name,description,iorder,options,multiselect FROM '.CGPB_OPTSETS_TABLE.' WHERE id = ?';
        $row = $db->GetRow($query,array($id));
        if( !is_array($row) ) throw new \cg_notfoundException('Could not find an option set with id '.$id);

        return self::fill_from_data($row);
    }

    public static function get_id_by_name($name)
    {
        $name = trim($name);
        if( !$name ) throw new \CmsInvalidDataException('Invalid name value passed to '.__METHOD__);

        $db = cmsms()->GetDb();
        $query = 'SELECT id FROM '.CGPB_OPTSETS_TABLE.' WHERE name = ?';
        $tmp = $db->GetOne($query);
        return $tmp;
    }

    /**
     * @ignore
     */
    protected static function fill_from_data(array $row)
    {
        $obj = new self();
        foreach( array_keys($obj->_data) as $key ) {
            if( !array_key_exists($key,$row) ) throw new \CmsInvalidDataException($key.' is missing from input data');
            switch( $key ) {
            case 'iorder':
            case 'id':
                $obj->_data[$key] = (int) $row[$key];
                break;

            case 'name':
            case 'description':
                $value = trim((string) $row[$key]);
                $obj->_data[$key] = trim($value);
                break;

            case 'multiselect':
                $obj->_data[$key] = (bool) $row[$key];
                break;
            }
        }

        if( isset($row['options']) ) {
            $tmp = unserialize($row['options']);
            if( is_array($tmp) ) {
                foreach( $tmp as $one ) {
                    if( ! $one instanceof \CGProductBuilder\option ) {
                        throw new \CmsInvalidDataException('Invalid option data specified');
                    }
                }
            }
            $obj->_options = $tmp;
        }
        return $obj;
    }

    /**
     * Load all option sets.
     *
     * @return \CGProductBuilder\option_set[]
     */
    public static function load_all()
    {
        if( !is_array(self::$_all_data) ) {
            $db = cmsms()->GetDb();
            $query = 'SELECT id,name,description,iorder,options,multiselect FROM '.CGPB_OPTSETS_TABLE.' ORDER BY iorder';
            $list = $db->GetArray($query);
            $out = null;
            if( is_array($list) && count($list) ) {
                $out = array();
                foreach( $list as $row ) {
                    $out[] = self::fill_from_data($row);
                }
            }
            self::$_all_data = $out;
        }
        return self::$_all_data;
    }

    /**
     * Generate a list (suitable for use in dropdowns) of all option sets.
     *
     * @return array
     */
    public static function load_list()
    {
        $tmp = self::load_all();
        if( is_array($tmp) ) {
            $out = array();
            foreach( $tmp as $one ) {
                $out[$one->id] = $one->name;
            }
            return $out;
        }
    }

    /**
     * Copy this option set
     */
    public function copy()
    {
        $obj = clone $this;
        $obj->_data['id'] = $obj->_data['name'] = $obj->_data['iorder'] = null;
        return $obj;
    }

    /**
     * Delete this option set.
     */
    public function delete()
    {
        if( $this->id < 1 ) throw new \LogicException('Cannot delete a '.__CLASS__.' that has not been saved');

        $db = cmsms()->GetDb();
        $query = 'DELETE FROM '.CGPB_OPTSETS_TABLE.' WHERE id = ?';
        $dbr = $db->Execute($query,array($this->id));
        $sorting = $this->iorder;
        $this->_data['id'] = null;
        $this->_data['iorder'] = null;

        $query = 'UPDATE '.CGPB_OPTSETS_TABLE.' SET iorder = iorder - 1 WHERE iorder > ?';
        $db->Execute($query,array($sorting));
    }

    /**
     * Test if this option set has options.
     *
     * @return bool
     */
    public function has_options()
    {
        return (count($this->_options) > 0);
    }


    /**
     * Get all of the options associated with this option set.
     *
     * @return \CGProductBuilder\option[]
     */
    public function get_options()
    {
        return $this->_options;
    }

    /**
     * Set all of the options associated with this option set.
     *
     * @param \CGProductBuilder\option[] $options
     */
    public function set_options(array $options)
    {
        foreach( $options as $opt ) {
            if( ! $opt instanceof \CGProductBuilder\option ) {
                throw new \CmsInvalidDataException('Invalid option data specified');
            }
        }

        $this->_options = $options;
    }

    /**
     * Delete all of the options that are currently associated with this option set.
     */
    public function delete_options()
    {
        $this->_options = null;
    }

} // end of class

?>
