<?php

/**
 * EColumns class file.
 * 
 * Allows to set column visibility and order in CGridView. 
 * 
 * @author Vitaliy Potapov <noginsk@rambler.ru>
 */
yii::import('zii.widgets.jui.CJuiSortable');

class EColumns extends CJuiSortable {

    /**
     * array of columns with keys generated by column data (name. or class or etc)
     *
     * @var mixed
     */
    public $columns = array();
    public $storage = 'session';
    public $gridId = null;
    public $delimiter = ',';
    public $buttonApply = '<input type="submit" value="Apply" style="float: left">';
    public $buttonCancel = null;
    public $buttonReset;
    public $buttonSelectAll = null;
    public $buttonUnselectAll = null;
    public $model = null;
    //model can be used to get attribute labels for header
    public $itemTemplate = '<li class="ui-state-default" id="{id}">{content}</li>';
    /**
     * array of column names to be fixed (not sortable)
     * @var mixed
     */
    public $fixedLeft = array();
    public $fixedRight = array();
    /**
     * weather settings stored for each user or not
     *
     * @var mixed
     */
    public $userSpecific = true;
    private $_visibleKeys = array();

    public function init() {
        parent::init();

        //register assets
        $cssUrl = Yii::app()->getAssetManager()->publish(Yii::getPathOfAlias('ext.ecolumns.assets'));

        if (Yii::app()->session['lang'] == "ar" || Yii::app()->session['lang'] != "en")
            Yii::app()->getClientScript()->registerCssFile($cssUrl . '/css/ecolumns_ar.css');
        else
            Yii::app()->getClientScript()->registerCssFile($cssUrl . '/css/ecolumns.css');

        //gridId is required
        if (empty($this->gridId))
            throw new CException('You must provide gridId');

        if (is_string($this->fixedLeft))
            $this->fixedLeft = array($this->fixedLeft);
        if (is_string($this->fixedRight))
            $this->fixedRight = array($this->fixedRight);

        if (empty($this->columns)) {
            $this->columns = array_keys($this->model->getAttributes());
        }

        //rewriting columns with unique keys
        $columnsByKey = array();
        foreach ($this->columns as $column) {
            $columnKey = $this->getColumnKey($column);
            for ($i = 0; true; $i++) {
                $suffix = ($i) ? '_' . $i : '';
                $columnKey .= $suffix;
                if (!array_key_exists($columnKey, $columnsByKey))
                    break;
            }
            $columnsByKey[$columnKey] = $column;
        }
        $this->columns = $columnsByKey;

        //row id in storage
        $storageId = $this->gridId;
        if ($this->userSpecific)
            $storageId .= '_' . yii::app()->user->id;

        //is form with nnew columns submitted ?
        $isSubmit = yii::app()->request->getParam($this->getRequestSubmit());
        if ($isSubmit) { // load from request, and save to storage
            $this->_visibleKeys = yii::app()->request->getParam($this->getRequestParam(), array());
            $this->saveVisibleKeys($storageId);
        } else {  //load from storage  
            $this->loadVisibleKeys($storageId);
        }
    }

    public function run() {
        //create items for JUISortable
        $widgetColumns = $this->getWidgetColumns();
        $this->items = array();
        foreach ($widgetColumns as $key => $column) {
            $this->items[$key] = '<label><input type="checkbox" name="' . $this->getRequestParam() . '[]" value="' . $key . '" ' . (isset($column['visible']) ? 'checked' : '') . '>&nbsp;' . CHtml::encode($column['header']) . '</label>';
        }

        $formId = $this->gridId . '-ecolumns';

        echo CHtml::beginForm('', 'POST', array(
            'id' => $formId,
        ));

        echo CHtml::hiddenField($this->getRequestSubmit(), 1);

        parent::run();
        //define buttons
        $this->buttonReset = '<input type="button" class="reset" value="' . Yii::t('ecolumns_messages', 'Reset') . '" style="float: right">';        
        $this->buttonSelectAll = '<input type="button" class="selectall" value="' . Yii::t('ecolumns_messages', 'Select All') . '" style="float: right">';
        $this->buttonUnselectAll = '<input type="button" class="unselectall" value="' . Yii::t('ecolumns_messages', 'Unselect All') . '" style="float: left">';

        //submit button
        echo '<div>';
        if (!empty($this->buttonApply))
            echo $this->buttonApply;
        if (!empty($this->buttonCancel))
            echo $this->buttonCancel;
        if (!empty($this->buttonReset))
            echo $this->buttonReset;
        if (!empty($this->buttonSelectAll))
            echo $this->buttonSelectAll;
        if (!empty($this->buttonUnselectAll))
            echo $this->buttonUnselectAll;
        echo '</div>';

        //submit handler
        Yii::app()->getClientScript()->registerScript(__CLASS__ . '#' . $formId, "
        jQuery('#{$formId}').on('submit', function(){
            jQuery('#{$this->gridId}').yiiGridView('update', {
                data: jQuery(this).serializeArray(),
                type: 'post'
            });
            return false;
        });
        ");

        if (!empty($this->buttonReset)) {
            $defaultOrder = array();
            foreach ($this->columns as $key => $column) {
                $defaultOrder[] = array('key' => $key, 'visible' => $this->isVisible($column));
            }

            Yii::app()->getClientScript()->registerScript(__CLASS__ . '#' . $formId . '-reset', "
                jQuery('#{$formId} .reset').on('click', function(){
                   var ul = jQuery('#{$formId} ul'),
                       defaultOrder = " . CJSON::encode($defaultOrder) . ",
                       buffer = jQuery('<ul>').append(ul.children().detach()),
                       el;
                                                                                              
                   for(var i=0; i<defaultOrder.length; i++) {
                      el = buffer.children('#'+defaultOrder[i].key);
                      el.find('input').attr('checked', defaultOrder[i].visible);
                      ul.append(el);
                   }

                   ul.sortable('refresh');
                });
            ");
        }
        //---------------------added by Safaa -----------------
        if (!empty($this->buttonSelectAll)) {
            $defaultOrder = array();
            foreach ($this->columns as $key => $column) {
                $defaultOrder[] = array('key' => $key, 'visible' => $this->isVisible($column));
            }

            Yii::app()->getClientScript()->registerScript(__CLASS__ . '#' . $formId . '-selectall', "
                jQuery('#{$formId} .selectall').on('click', function(){
                jQuery('#{$formId} ul').find('input').attr('checked', 'checked');
                      
                });
            ");
        }

        //---------------------added by Safaa -----------------
        if (!empty($this->buttonUnselectAll)) {
            $defaultOrder = array();
            foreach ($this->columns as $key => $column) {
                $defaultOrder[] = array('key' => $key, 'visible' => $this->isVisible($column));
            }

            Yii::app()->getClientScript()->registerScript(__CLASS__ . '#' . $formId . '-unselectall', "
                jQuery('#{$formId} .unselectall').on('click', function(){
                 jQuery('#{$formId} ul').find('input').removeAttr('checked');
                });
            ");
        }
        //---------------------added by Safaa -----------------

        echo Chtml::endForm();
    }

    /**
     * returns curently visible columns
     *
     */
    public function columns() {
        $result = array();

        $fixed = array_merge($this->fixedLeft, $this->fixedRight);

        //left fixed columns
        foreach ($this->fixedLeft as $key) {
            $this->addVisibleColumn($key, $result);
        }

        // middle sortable columns
        if ($this->_visibleKeys === false) {
            //when nothing in storage
            foreach ($this->columns as $key => $column) {
                if (in_array($key, $fixed))
                    continue;
                $this->addVisibleColumn($key, $result);
            }
        } else {
            //loaded from storage  
            for ($i = 0; $i < count($this->_visibleKeys); $i++) {
                $key = $this->_visibleKeys[$i];
                if (in_array($key, $fixed))
                    continue;
                $this->addVisibleColumn($key, $result);
            }
        }

        //right fixed columns 
        foreach ($this->fixedRight as $key) {
            $this->addVisibleColumn($key, $result);
        }

        return $result;
    }

    /* --------- PROTECTED SECTION ----------------- */

    /**
     * returns unique column key
     *
     * @param mixed $column
     * @return mixed
     */
    protected function getColumnKey($column) {
        if (!is_array($column)) {
            if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $column, $matches))
                throw new CException(Yii::t('zii', 'The column must be specified in the format of "Name:Type:Label", where "Type" and "Label" are optional.'));
            $columnKey = $matches[1];
        } elseif (!empty($column['name'])) {
            $columnKey = $column['name'];
        } elseif (!empty($column['header'])) {
            $columnKey = $column['header'];
        } elseif (!empty($column['class'])) {
            $columnKey = $column['class'];
        } else {
            $columnKey = null;
        }
        // ----------------added by Safaa ------------------
        if ($columnKey == "CButtonColumn") {
            $columnKey = Yii::t('ecolumns_messages', 'Control Buttons');
        }
        // ----------------added by Safaa ------------------
        $columnKey = str_replace(' ', '', $columnKey);

        return $columnKey;
    }

    /**
     * load visible keys from specific storage
     *
     * @param mixed $id
     */
    protected function loadVisibleKeys($id) {
        $strKeys = false;
        switch ($this->storage) {
            case 'session':
                if (isset(Yii::app()->session[$id])) {
                    $strKeys = yii::app()->session->get($id);
                }
                break;
            case 'cookie':
                if (isset(Yii::app()->request->cookies[$id])) {
                    $strKeys = yii::app()->request->cookies[$id]->value;
                }
                break;
            case 'db':
                $strKeys = yii::app()->db->createCommand('select data from tbl_columns where id = :id')->queryScalar(array(':id' => $id));
                break;
            default:
                throw new CException('Unknown storage: ' . $this->storage);
        }

        if ($strKeys === false) {
            //take visible keys from grid config
            $this->_visibleKeys = array();
            foreach ($this->columns as $key => $column) {
                if ($this->isVisible($column)) {
                    $this->_visibleKeys[] = $key;
                }
            }
        } else {
            $this->_visibleKeys = explode($this->delimiter, $strKeys);
        }
    }

    /**
     * save visible keys to storage
     *
     * @param mixed $id
     */
    protected function saveVisibleKeys($id) {
        $strKeys = implode($this->delimiter, $this->_visibleKeys);
        switch ($this->storage) {
            case 'session':
                yii::app()->session[$id] = $strKeys;
                break;
            case 'cookie':
                $cookie = new CHttpCookie($id, $strKeys);
                $cookie->expire = time() + 60 * 60 * 24 * 100; // 100 days
                Yii::app()->request->cookies[$id] = $cookie;
                break;
            case 'db':
                $params = array(':id' => $id);
                $exists = yii::app()->db->createCommand('select id from tbl_columns where id = :id')->queryScalar($params);
                $data = array('data' => $strKeys);
                if ($exists) {
                    $criteria = new CDbCriteria(array('condition' => 'id = :id', 'params' => $params));
                    yii::app()->db->getCommandBuilder()->createUpdateCommand('tbl_columns', $data, $criteria)->execute();
                } else {
                    $data['id'] = $id;
                    yii::app()->db->getCommandBuilder()->createInsertCommand('tbl_columns', $data)->execute();
                }
                break;
            default:
                throw new CException('Unknown storage: ' . $this->storage);
        }
    }

    /**
     * param name in request that store string of column names separated by delimiter
     *
     */
    protected function getRequestParam() {
        return $this->gridId . '-ecolumns';
    }

    /**
     * param name in request to show that new columns submitted
     *
     */
    protected function getRequestSubmit() {
        return $this->gridId . '-ecolumns-set';
    }

    /**
     * returns array of all available columns in format (sort visible at first):
     * columns[key] = array(
     *    'header' =>
     *    'visible' =>
     * )
     *
     *  used when manage columns
     *
     */
    protected function getWidgetColumns() {
        $widgetColumns = array();

        $fixed = array_merge($this->fixedLeft, $this->fixedRight);

        //list of visible columns first
        if (is_array($this->_visibleKeys)) {
            for ($i = 0; $i < count($this->_visibleKeys); $i++) {
                $key = $this->_visibleKeys[$i];
                if (in_array($key, $fixed))
                    continue;
                if (array_key_exists($key, $this->columns)) {
                    $column = $this->columns[$key];
                    $widgetColumns[$key] = array(
                        'header' => $this->getColumnHeader($key, $column),
                        'visible' => true,
                    );
                }
            }
        }

        //list of hidden columns at the end
        foreach ($this->columns as $key => $column) {
            if (in_array($key, $fixed))
                continue;
            if (array_key_exists($key, $widgetColumns))
                continue;
            $widgetColumns[$key] = array(
                'header' => $this->getColumnHeader($key, $column),
            );
        }

        return $widgetColumns;
    }

    /**
     * adds column to array with correct 'visible' attribute
     *
     * @param mixed $key
     * @param mixed $result
     */
    protected function addVisibleColumn($key, &$result) {
        if (array_key_exists($key, $this->columns)) {
            $column = $this->columns[$key];
            if (is_array($column)) {
                $column['visible'] = true;
            }
            $result[] = $column;
        }
    }

    /**
     * is column visible
     *
     * @param mixed $column
     * @return mixed
     */
    protected function isVisible($column) {
        return (!is_array($column) || !isset($column['visible']) || $column['visible'] === true);
    }

    /**
     * returns column header
     *
     * @param mixed $key
     * @param mixed $column
     */
    protected function getColumnHeader($key, $column) {
        if (is_string($column)) {
            if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $column, $matches))
                throw new CException(Yii::t('zii', 'The column must be specified in the format of "Name:Type:Label", where "Type" and "Label" are optional.'));
            $name = $matches[1];
            if (isset($matches[5]))
                return $matches[5];  //header specified in format "Name:Type:Label"
 if ($this->model instanceOf CModel)
                return $this->getLabel($this->model, $name);
            return $name; //header = name
        } else {
            if (is_array($column)) {
                if (isset($column['header']))
                    return $column['header'];
                if (isset($column['name']) && $this->model instanceOf CModel)
                    return $this->getLabel($this->model, $column['name']);
            }
            return $key;
        }
    }

    /**
     * This function is responsible for getting normalized label
     * @param Object $model model we want to get its attribute
     * @param string $attribute attribute of the model
     * @return string label of given attribute
     * @author Safaa AlNabulsi
     */
    function getLabel($model, $attribute) {
        //when header defined like this:
        //'shareholder_identifier' => yii::t('default', 'Shareholder') . "<font class='red-star' color='#B94A48'> * </font>",
        $attr_header = $model->getAttributeLabel($attribute);
        $res = explode("<", $attr_header);
        return $res[0];
    }

}
