<?php

/**
 * Goweb Sitemanager 5 Framework
 *
 * @version    5.0.20
 * @category   Goweb
 * @package    Goweb_Admin
 * @author     I&D <dev@goweb.pt>, Pedro Pinto <ppinto@goweb.pt>
 * @copyright  2010 (c) Goweb (http://www.goweb.pt)
 */

/**
 * Classe de definicao de seccao da geracao de interfaces Admin
 *
 * @version    5.0.20
 * @category   Goweb
 * @package    Goweb_Admin
 * @author     I&D <dev@goweb.pt>, Pedro Pinto <ppinto@goweb.pt>
 * @copyright  2010 (c) Goweb (http://www.goweb.pt)
 */
class Goweb_Admin_Section {

    /**
     * O objecto Goweb_Db_Model
     *
     * @var Goweb_Db_Model
     */
    protected $_model = null;

    /**
     * Titulo da seccao
     *
     * @var  array
     */
    protected $_title = '';

    /**
     * Tipo de listagem
     *
     * @var  String
     */
    protected $_listType = 'list';

    /**
     * Titulo da seccao no singular
     *
     * @var  array
     */
    protected $_titleSingular = '';

    /**
     * Array com os labels das colunas
     *
     * @var  array
     */
    protected $_labels = array();

    /**
     * Array com as colunas para a listagem
     *
     * @var  array
     */
    protected $_listCols = array();

    /**
     * Array com as colunas associadas ao model
     *
     * @var array
     */
    protected $_cols = array();

    /**
     * Array com os nomes das colunas para a ordenacao
     *
     * @var  array
     */
    protected $_ordering = array();

    /**
     * Array as accoes a serem feitas
     *
     * @var  array
     */
    protected $_actions = array();

    /**
     * Array com os nomes das colunas pela qual e feita a pesquisa
     *
     * @var  array
     */
    protected $_searchCols = array();

    /**
     * Array com os parametros para gerarobjectos do tipo Goweb_Js_Tree
     *
     * @var  array
     */
    protected $_tree = null;

    /**
     * Node a abrir na tree
     *
     * @var  int
     */
    protected $_treeOpenTo = null;
    /**
     * Array condições de pesquisa (where)
     *
     * @var  array
     */
    protected $_where = array();

    /**
     * Nome da seccao a ser mostrada na listagem
     *
     * @var  string
     */
    protected $_sectionListAction = null;

    /**
     * Nome da accao a ser mostrada na listagem
     *
     * @var  string
     */
    protected $_listAction = null;

    /**
     * Objecto Goweb_Js_Tree
     *
     * @var  Goweb_Js_Tree
     */
    protected $_treeObject = null;

    /**
     * O nome da coluna do ID do pai
     *
     * @var int
     */
    protected $_parentIdCol = null;

    /**
     * Read Only
     *
     * @var  bool
     */
    protected $_updateOnly = false;

    /**
     * Podem ser adicionados novos elementos na secção ?
     *
     * @var  bool
     */
    protected $_canAdd = true;

    /**
     * Elementos que não podem ser editados na secção
     *
     * @var  bool
     */
    protected $_cantEdit = array();

    /**
     * Os elementos podem ser editados na secção ?
     *
     * @var  bool
     */
    protected $_canEdit = true;

    /**
     * Usar o deletelist ?
     *
     * @var  bool
     */
    protected $_canDeleteList= true;

    /**
     * Podem ser apagados elementos na secção ?
     *
     * @var  bool
     */
    protected $_canDelete= true;

    /**
     * Elementos que não podem ser apagados na secção
     *
     * @var  bool
     */
    protected $_cantDelete = array();

    /**
     * Constructor
     */
    public function __construct() {}


    /**
     * Define o titulo da seccao.
     *
     * @param  string  $title  O titulo da seccao
     */
    public function setTitle($title) {
        $this->_title = $title;
    }

    /**
     * Define as condições de pesquisa.
     *
     * @param   array  $where  Array condições de pesquisa
     */
    public function setWhere($where) {
        //$this->_where = (array) $where;
        $this->_where = $where;
    }

    /**
     * Define o titulo da seccao no singular.
     *
     * @param  string  $title  O titulo da seccao
     */
    public function setTitleSingular($titleSingular) {
        $this->_titleSingular = $titleSingular;
    }

    /**
     * Define o objecto Goweb_Db_Model a ser associado e seccao.
     *
     * @param  Goweb_Db_Model  $model  Objecto Model
     */
    public function setModel(Goweb_Db_Model $model) {
        $this->_model  = $model;
        $this->_cols   = $model->getColsNamesWithAliases();
        $this->_labels = $model->getColsLabelsWithAliases();
    }

    /**
     * Define as colunas a serem mostradas na listagem.
     *
     * @param   array  $listCols  Array com os nomes das colunas
     * @throws  Goweb_Exception   Se n&atilde;o for do tipo array
     */
    public function setListCols($listCols) {
        if (func_num_args()>0 && !is_array($listCols))
        $listCols = func_get_args();

        if (!is_array($listCols)) {
            throw new Goweb_Exception('O par&acirc;metro submetido em "Goweb_Admin_Section::listCols()" tem de ser um array');
        }
        $this->_listCols = $listCols;
    }

    /**
     * Define o titulo das colunas.
     *
     * @param   array  $labels   Array com os nomes das colunas
     * @throws  Goweb_Exception  Se n&atilde;o for do tipo array
     */
    public function setLabels($labels) {
        if (func_num_args()>0 && !is_array($labels))
        $labels = func_get_args();

        if (!is_array($labels)) {
            throw new Goweb_Exception('O par&acirc;metro submetido em "Goweb_Admin_Section::labels()" tem de ser um array');
        }
        $this->_labels = $labels;
    }

    /**
     * Define as colunas para a ordenacao.
     *
     * @param   array  $ordering  Array com os nomes das colunas
     */
    public function setOrdering($ordering) {
        if (func_num_args()>0 && !is_array($ordering))
        $ordering = func_get_args();
        $this->_ordering = (array) $ordering;
    }

    /**
     * Define as colunas para a pesquisa.
     *
     * @param   array  $searchCols  Array com os nomes das colunas
     */
    public function setSearchCols($searchCols) {
        if (func_num_args()>0 && !is_array($searchCols))
        $searchCols = func_get_args();

        $this->_searchCols = (array) $searchCols;
    }

    /**
     * Define qual a accao a ser listada.
     *
     * @param   string  $action  O nome da accao
     */
    public function setListAction($action) {
        $this->_listAction = $action;
    }

    /**
     * Define qual a seccao a ser listada.
     *
     * @param   string  $section  O nome da seccao
     */
    public function setSectionListAction($section) {
        $this->_sectionListAction = $section;
    }

    /**
     * Define qual e o nome da coluna do ID do pai
     *
     * @param   string  $parentIdCol
     */
    public function setParentIdCol($parentIdCol) {
        $this->_parentIdCol = $parentIdCol;
    }

    /**
     * Retorna e o no meda coluna do ID do pai
     *
     * @return   string  $parentIdCol
     */
    public function getParentIdCol() {
        return $this->_parentIdCol;
    }

    /**
     * Retorna o titulo da seccao.
     *
     * @return  string
     */
    public function getTitle() {
        if ($this->_title == '') {
            $this->_title = $this->_formatNameForLabel($this->_model->getTableName());
        }
        return $this->_title;
    }

    /**
     * Retorna o titulo da seccao no singular.
     *
     * @return  string
     */
    public function getTitleSingular() {
        if ($this->_titleSingular == '') {
            return $this->getTitle();
        }
        return $this->_titleSingular;
    }

    /**
     * Retorna o model associado.
     *
     * @return  Goweb_Db_Model
     */
    public function getModel() {
        return $this->_model;
    }

    /**
     * Retorna o array com as colunas associadas ao model.
     *
     * @return  array
     */
    public function getCols() {
        return $this->_cols;
    }

    /**
     * Retorna o array com as colunas associadas e listagem.
     *
     * @return  array
     */
    public function getListCols() {
        return $this->_listCols;
    }

    /**
     * Retorna o array os titulos das colunas.
     *
     * @return  array
     */
    public function getLabels() {
        return $this->_labels;
    }

    /**
     * Retorna o nome da seccao da listagem.
     *
     * @return  string  $section  O nome da seccao
     */
    public function getSectionListAction() {
        return $this->_sectionListAction;
    }

    /**
     * Retorna o nome da accao da listagem.
     *
     * @return  string  $section  O nome da seccao
     */
    public function getListAction() {
        return $this->_listAction;
    }

    /**
     * Verifica se existe labels para todas as colunas, caso contrerio
     * defini os labels atraves dos nomes das colunas.
     *
     * @return  void
     */
    public function checkLabels() {
        $numCols = count($this->_cols);
        $numLabels = count($this->_labels);
        $labels = $this->_labels;
        foreach ($this->_cols as $col) {
            if (!array_key_exists($col, $this->_labels) || !strlen($this->_labels[$col])) {
                $labels[$col] = $this->_formatNameForLabel($col);
            }
        }
        $this->_labels = $labels;
    }

    /**
     * Retorna o array com as colunas associadas e ordenacao.
     *
     * @return  array
     */
    public function getOrdering() {
        return $this->_ordering;
    }

    /**
     * Retorna o array com as colunas associadas e pesquisa.
     *
     * @return  array
     */
    public function getSearchCols() {
        return $this->_searchCols;
    }

    /**
     * Tipo de listagem Genérica
     *
     * @param String $type tipo de listagem
     */
    public function setListType($type = 'list'){
        $this->_listType = $type;
    }

    /**
     * Retorna o tipo de listagem Genérica nesta seção
     *
     * @return String tipo de listagem nesta seção
     */
    public function getListType(){
        return $this->_listType;
    }

    /**
     * Atribui par&acirc;metros e ervores.
     *
     * @param   string          $name         Nome da tree
     * @param   Goweb_Db_Model  $model        Objecto do tipo Goweb_Db_Model
     * @param   array           $treeParams   Array com os par&acirc;metros da tree
     * @param   array           $nodesParams  Array com os par&acirc;metros dos nodes
     */
    public function setTree($name, $title,  $model, $treeParams, $nodesParams, $rootNodeParams = null, $ordering = null) {
        $this->_tree = array('name'           => $name,
                             'title'          => $title,
                             'model'          => $model,
                             'treeParams'     => $treeParams,
                             'nodesParams'    => $nodesParams,
                             'rootNodeParams' => $rootNodeParams,
                             'ordering'       => $ordering);
    }

    /**
     * Atribui o ID do node a ser aberto na tree.
     *
     * @param  int  $id  ID do node
     */
    public function treeOpenTo($id) {
        $this->_treeOpenTo = $id;
    }

    /**
     * Retorna o id do node a ser aberto na tree.
     *
     * @return  int
     */
    public function getTreeOpenTo() {
        return $this->_treeOpenTo;
    }

    /**
     * Retorna os par&acirc;metros da tree
     *
     * @return  array
     */
    public function getTreeParams() {
        return $this->_tree;
    }

    /**
     * Atribui um objecto do tipo Goweb_Js_Tree je instanciado
     *
     * @param   Goweb_Js_Tree  Object Goweb_Js_Tree instanciado
     */
    public function setTreeObject($treeObject) {
        $this->_treeObject = $treeObject;
    }

    /**
     * Cria e retorna um objecto do tipo Goweb_Js_Tree|Goweb_Js_Dhtmlx_Tree
     *
     * @param   bool  $cleanModelParts  TRUE se for para remover as partes do objecto Goweb_Db_Select
     * @return Goweb_Js_Tree|Goweb_Js_Dhtmlx_Tree
     */
    public function getTree($cleanModelParts = true) {
        if ($this->_treeObject) {
            return $this->_treeObject;
        }

        if (!$this->_tree) {
            return null;
        }

        if (is_dir(WWW_PATH.'/admin/media/js/dhtmlx/')){
            Goweb::loadClass('Goweb_Js_Dhtmlx_Tree');
            $tree = new Goweb_Js_Dhtmlx_Tree($this->_tree['name']);
            $tree->setTitle($this->_tree['title']);
            goweb::loadCss('/admin/media/js/dhtmlx/dhtmlx.css');
            goweb::loadCss('/admin/media/css/dhtmlx.css');
            goweb::loadJavascript('/admin/media/js/dhtmlx/dhtmlx.js');
            goweb::loadJavascript('/admin/media/js/dhtmlx/dhtmlx_goweb.js');
            goweb::loadJavascript('/admin/media/js/dhtmlx/connector.js');
            return $tree;
        }
        return $this->_getJsTree($cleanModelParts);
    }

    /**
     * Cria e retorna um objecto do tipo Goweb_Js_Tree
     *
     * @param   bool  $cleanModelParts  TRUE se for para remover as partes do objecto Goweb_Db_Select
     * @throws  Goweb_Exception  No caso dos par&acirc;metros 'id', 'pid' e 'name' n&atilde;o existirem
     * @return  Goweb_Js_Tree
     */
    protected function _getJsTree($cleanModelParts = true) {

        // cria o objecto Goweb_Js_Tree
        Goweb::loadClass('Goweb_Js_Tree');
        $tree = new Goweb_Js_Tree($this->_tree['name']);
        $tree->setTitle($this->_tree['title']);

        // adiciona os par&acirc;metros
        foreach ($this->_tree['treeParams'] as $param => $value) {
            $tree->{$param} = $value;
        }

        $nodesParams = $this->_tree['nodesParams'];
        // valida se foram passados os campos necess&aacute;rio para o node
        if (!array_key_exists('id', $nodesParams)) {
            throw new Goweb_Exception("O campo  'id' e necess&aacute;rio nos par&acirc;metros dos nodes.");
        }
        if (!array_key_exists('pid', $nodesParams)) {
            throw new Goweb_Exception("O campo  'pid' e necess&aacute;rio nos par&acirc;metros dos nodes.");
        }
        if (!array_key_exists('name', $nodesParams)) {
            throw new Goweb_Exception("O campo  'name' e necess&aacute;rio nos par&acirc;metros dos nodes.");
        }
        if (!array_key_exists('urlId', $nodesParams)) {
            throw new Goweb_Exception("O campo  'urlId' e necess&aacute;rio nos par&acirc;metros dos nodes.");
        }

        $idCol = $nodesParams['id'];
        $pidCol = $nodesParams['pid'];
        $nameCol = $nodesParams['name'];
        if ($this->_tree['ordering']) {
            $this->_tree['model']->order($this->_tree['ordering']);
        }

        // n&atilde;o carregar as chaves estrangeiras
        $this->_tree['model']->loadForeigns(false);
        // limpa todas as partes do SELECT
        if ($cleanModelParts) {
            $this->_tree['model']->cleanParts();
        }
        $fetchCols = array($idCol, $pidCol, $nameCol);
        if ($this->_tree['model']->colExists('activo')){
            $fetchCols[] = 'activo';
        }

        $rowset = $this->_tree['model']->fetchAll($fetchCols);
        $urlBase = Zend_Controller_Front::getInstance()->getRequest()->getBaseUrl();
        $urlBase = str_replace('/index.php', '', $urlBase);
        unset($nodesParams['id']);
        unset($nodesParams['pid']);
        unset($nodesParams['name']);
        if (strpos($nodesParams['url'], $urlBase) === false) $nodesParams['url'] = $urlBase . $nodesParams['url'];
        $node = new Goweb_Js_Tree_Node('0', '-1', '', $nodesParams['url'] . '0/');
        if (array_key_exists('rootName', $nodesParams)) {
            $node->name = $nodesParams['rootName'];
            unset($nodesParams['rootName']);
        }

        if ($this->_tree['rootNodeParams']) {
            $rootNodeParams = $this->_tree['rootNodeParams'];
        } else {
            $rootNodeParams = $nodesParams;
        }

        if (strpos($rootNodeParams['url'], $urlBase) === false) $rootNodeParams['url'] = $urlBase . $rootNodeParams['url'];

        // configura os links e icons extra
        for ($i=1; $i<=5; $i++) {
            $param = 'url_' . $i;

            if  (array_key_exists($param, $rootNodeParams) && $rootNodeParams[$param]) {
                if (strpos($rootNodeParams[$param], $urlBase) === false) $rootNodeParams[$param] = $urlBase . $rootNodeParams[$param];
                $node->{$param} = $rootNodeParams[$param] . '0/';

                $param = 'url_' . $i . '_icon';
                if (array_key_exists($param, $rootNodeParams) && $rootNodeParams[$param]) {
                    if (strpos($rootNodeParams[$param], $urlBase) === false) $rootNodeParams[$param] = $urlBase . $rootNodeParams[$param];
                    $node->{$param} = $rootNodeParams[$param];
                }
            }
        }


        $tree->addNode($node);

        if ($rowset) {
            if (strpos($nodesParams['url'], $urlBase) === false) $nodesParams['url'] = $urlBase . $nodesParams['url'];
            for ($i=1; $i<=5; $i++) {
                $param = 'url_' . $i;
                if  (array_key_exists($param, $nodesParams) && $nodesParams[$param]) {
                    if (strpos($nodesParams[$param], $urlBase) === false) $nodesParams[$param] = $urlBase . $nodesParams[$param];
                    $node->{$param} = $nodesParams[$param];
                    $param = 'url_' . $i . '_icon';

                    if (array_key_exists($param, $nodesParams) && $nodesParams[$param]) {
                        if (strpos($nodesParams[$param], $urlBase) === false) $nodesParams[$param] = $urlBase . $nodesParams[$param];
                    }
                }
            }
            foreach ($rowset as $row) {
                $titulo = $row->{$nameCol};
                if ($this->_tree['model']->colExists('activo') && !$row->activo){
                    $titulo = '<span class="disabled_page">' . $titulo . '</span>';
                }
                $node = new Goweb_Js_Tree_Node($row->{$idCol},
                $row->{$pidCol},
                $titulo,
                //$nodesParams['url'] . $row->{$nodesParams['urlId']} . '/',
                $nodesParams['url'] . $row->{$idCol} . '/',
                $nodesParams['urlId'],
                $nodesParams['target'],
                $nodesParams['title'],
                $nodesParams['icon'],
                $nodesParams['iconOpen'],
                $nodesParams['open']);

                // configura os links e icons extra
                for ($i=1; $i<=5; $i++) {
                    $param = 'url_' . $i . '_icon';
                    if  (array_key_exists($param, $nodesParams) && $nodesParams[$param]) {
                        $node->{$param} = $nodesParams[$param];
                        $param = 'url_' . $i;
                        if (array_key_exists($param, $nodesParams) && $nodesParams[$param]) {
                            $node->{$param} = $nodesParams[$param] . $row->{$idCol} . '/';
                        }
                    }
                }

                // adiciona o node
                $tree->addNode($node);
            }
        }

        if ($this->_treeOpenTo) {
            $tree->setOpenTo($this->_treeOpenTo);
        }

        // retorna o Goweb_Js_Tree
        return $tree;
    }

    /**
     * Formata uma string para se mostrada como label.
     *
     * Substitui palavras separadas por "-", "_", ou "." por maiusculas e
     * remove todos caracteres que n&atilde;o sejam alfanemericos.
     *
     * @param   string  $unformatted  Nome n&atilde;o formatado
     * @return  string
     */
    protected function _formatNameForLabel($unformatted) {
        $unformatted = str_replace(array('-', '_', '.'), ' ', strtolower($unformatted));
        $unformatted = preg_replace('[^a-z0-9 ]', '', $unformatted);
        return ucwords($unformatted);
    }

    /**
     * Retorna o array com as condições de pesquisa.
     *
     * @return  array
     */
    public function getWhere() {
        return $this->_where;
    }

    /**
     * Retorna o titulo da secção
     *
     * @return string
     */
    public function __toString(){
        return $this->_title;
    }

    /**
     * Retorna se os elementos desta secção só podem ser actualizados (nota: não podem ser apagados, mas podem inserir novos), compatibilidade com nova versão, usar canDelete
     *
     * @return bool
     */
    public function getUpdateOnly(){
        return !$this->_canDelete;
    }

    /**
     * Coloca a opção se os elementos desta secção podem ser actualizados (nota: se podem ser apagados), compatibilidade com nova versão, usar canDelete
     *
     * @param bool $flag
     */
    public function setUpdateOnly($flag = true){
        $this->_canDelete = (bool) !$flag;
    }

    /**
     * Obter as acções da secção 
     *
     * @return array
     */
    public function getActions(){
        return $this->_actions;
    }

    /**
     * Adicionar uma acção à seccção 
     *
     * @param string $link link a que irá ser adicionado o id da acção
     * @param string $imagem link da imagem da acção
     * @param string $alt alt/texto descritivo da imagem da acção
     */
    public function addAction($link = null, $imagem = null, $alt = null){
        if (is_null($link) || is_null($imagem)) return false;
        if (!is_null($alt)) $alt = html_entity_decode($alt, ENT_NOQUOTES, CHARSET);
        $this->_actions[$link] = array('link' => $link,
                                  'img' => $imagem,
                                  'alt' => $alt);

    }

    /**
     * Pode adicionar novos elementos nesta secção
     *
     * @param bool $flag
     */
    public function canAdd($flag = true){
        $this->_canAdd = (bool) $flag;
    }

    /**
     * Pode apagar elementos nesta secção. NOTA: opcçao global, nenhum elemento pode ser apagado nesta secção
     *
     * @param bool $flag
     */
    public function canDelete($flag = true){
        $this->_canDelete = (bool) $flag;
    }

    /**
     * Pode apagar elementos na listagem nesta secção. NOTA: opcçao global, nenhum elemento pode ser apagado nesta secção
     *
     * @param bool $flag
     */
    public function canDeleteList($flag = true){
        $this->_canDeleteList = (bool) $flag;
    }

    /**
     * Pode editar elementos nesta secção. NOTA: opcçao global, nenhum elemento pode ser editado nesta secção
     *
     * @param bool $flag
     */
    public function canEdit($flag = true){
        $this->_canEdit = (bool) $flag;
    }

    /**
     * Elementos que não podem ser apagados nesta secção
     *
     * @param bool $flag
     */
    public function cantDelete($elements = array()){
        if (!is_array($elements)){
            throw new Goweb_Exception('Os elementos que não podem ser apagados devem ser passados como um array com os respectivos IDs');
        }
        $this->_cantDelete = $elements;
    }

    /**
     * Elementos que não podem ser editados nesta secção
     *
     * @param bool $flag
     */
    public function cantEdit($elements = array()){
        if (!is_array($elements)){
            throw new Goweb_Exception('Os elementos que não podem ser editados devem ser passados como um array com os respectivos IDs');
        }
        $this->_cantEdit = $elements;
    }

    /**
     * Obter os elementos que não podem ser editados
     *
     * @return bool|array
     */
    public function getCantEdit(){
        if (!$this->_canEdit) return false;
        return $this->_cantEdit;
    }

    /**
     * Obter os elementos que não podem ser apagados
     *
     * @return bool|array
     */
    public function getCantDelete(){
        if (!$this->_canDelete) return false;
        return $this->_cantDelete;
    }

    /**
     * Podem ser apagados vários elementos na listagem?
     *
     * @return bool
     */
    public function getCantDeleteList(){
        return $this->_canDeleteList;
    }

    /**
     * Podem ser adicionados novos elementos?
     *
     * @return bool
     */
    public function getCanAdd(){
        return $this->_canAdd;
    }

}