<?php
/**
 * @package			No Boss Extensions
 * @subpackage  	No Boss Calendar
 * @author			No Boss Technology <contact@nobosstechnology.com>
 * @copyright		Copyright (C) 2022 No Boss Technology. All rights reserved.
 * @license			GNU Lesser General Public License version 3 or later; see <https://www.gnu.org/licenses/lgpl-3.0.en.html>
 */

defined('_JEXEC') or die();

jimport('noboss.util.url');
jimport('noboss.util.fonts');

require_once "helpers/supports.php";
require_once "helpers/categories.php";
require_once "helpers/apis.php";

class ModNobosscalendarHelper {
    use ModNobosscalendarHelperSupports;
    use ModNobosscalendarHelperCategories;
    use ModNobosscalendarHelperApis;

	/**
	 * Método ajax que atualiza calendário conforme navegação entre meses (nao inclui modelo de cards)
	 *
	 * @return void.
	 */
	public static function showCalendarMonthAjax() {
        header('Access-Control-Allow-Origin: *');
        
        // Pega dados da requisição.
		$app = JFactory::getApplication();
		$input = $app->input;
		$isAjax = true;

		// Pega o id do módulo
		$idModule = $input->get("idModule");

		// Pega os parametros de estilo do mes do calendario
		$calendarParams = ModNobosscalendarHelper::getCalendarParams($idModule);
		$moduleParams = $calendarParams->moduleParams;
		$extensionName = $moduleParams->extension_name;	
		$moduleName = $moduleParams->module_name;	
		$theme = json_decode($moduleParams->theme)->theme;
		$custom = $calendarParams->itemsCustomization;
        $externalArea = $calendarParams->externalArea;		

        // Cria datas para o calendário.
        $currentDate = JDate::getInstance($input->get("currentDate"));
		$slide = $input->get("slide");
		$date = static::getFirstDayMonth($currentDate, $slide);

		// Clona o primeiro dia para ser percorrido na montagem do calendario
		$dateFor = clone $date;

        $today = new JDate(date('Y-m-d'));

		// Pega o filtro caso haja
		$filter = $input->get("filter", "", "STRING");

		$isOpen = $input->get("isOpen", false) == 'true' ? true : false;
        
		// Dados para o calendário.
		$dataCalendar = self::getDataToCalendar($idModule, $moduleParams->id_calendar, $date, $custom, $filter);
        
        $lastDate = clone $date;
        $lastDate->modify('last day of this month');

		$app = JFactory::getApplication();
		// pega o template vinculado a pagina
		$tmpl = $app->getTemplate(true);
		// pega os parametros do template
		$tmplParams = $tmpl->params;
		// pega a cor primaria setada nos parametros do template
        $tmplPrimaryColor = $tmplParams->get('primary_color');

        // Obtem parametros da modal
        $modalCustomization = ModNobosscalendarHelper::getModalParams($idModule);
		
		// Renderiza o html
		exit(require JModuleHelper::getLayoutPath('mod_nobosscalendar', 'common/calendar_inner'));
	}

	/**
	 * Método ajax que atualiza calendário conforme navegação entre meses (especifico do modelo de cards)
	 *
	 * @return void.
	 */
	public static function showCalendarMonthEventsCardsAjax() {
        header('Access-Control-Allow-Origin: *');
		// Pega dados da requisição.
		$app = JFactory::getApplication();
		$input = $app->input;

		// Pega o id do módulo
        $idModule = $input->get("idModule");

        // Indice do evento para navegacao
        $firstEventIndex = $input->get("firstEventIndex", 0);        

		// Pega os parametros do calendario
		$calendarParams = ModNobosscalendarHelper::getCalendarParams($idModule);
		$moduleParams = $calendarParams->moduleParams;
		$extensionName = $moduleParams->extension_name;	
		$moduleName = $moduleParams->module_name;	
		$theme = json_decode($moduleParams->theme)->theme;
		$custom = $calendarParams->itemsCustomization;
		$externalArea = $calendarParams->externalArea;

		// Obtem info de qual botao foi clicado (prev / next)
		$slide = $input->get("slide");
        
		// Navegacao normal (NAO EH POR MES)
		if((isset($custom->browsing_month)) && ($custom->browsing_month == 0)){
			// Obtem eventos utilizando funcao que obtem eventos proximos da data atual
            $returnEventsCards = ModNobosscalendarHelper::getRunningEventsCards($idModule, $custom->events_to_display, $custom->interval_days, $slide, $firstEventIndex);
			
			// Ha resultados a exibir
			if (!empty($returnEventsCards->events)){
                $eventsSearch = $returnEventsCards->events;
                unset($returnEventsCards->events);
                // Seta data inicial do primeiro evento como $date
				$date = new JDate($eventsSearch[0]->initial_date);
			}else{
                // retorna 0 para nao mudar nada na pagina
                exit(0);
            }
		}
		// Navegacao por mes
		else{
            // Setado para exibir meses sem eventos na navegacao
            if($custom->display_empty_month == 1){
                $monthSkipEmpty = 1;
            }
            // Setado para pular meses sem eventos: fixa um limite de tentativas de meses a buscar com evento
            else{
                $monthSkipEmpty = 24;
            }

            // Contador de meses pulados (sem eventos)
            $skippedMonths = 0;

            $eventsSearch = array();

            // Data do ultimo mes consultado
            $date = JDate::getInstance($input->get("currentDate"));

            // Navega bucando meses ate achar mes com eventos, respeitando limite da variavel $monthSkipEmpty
            while((empty((array)$eventsSearch)) && ($skippedMonths < $monthSkipEmpty)){
                // Clicado para avancar um mes
                if($slide == 'next'){
                    $date->modify("+1 month");
                }
                // Clicado para retroceder um mes
                else{
                    $date->modify("-1 month");
                }
                
                // Clona a data, colocando ponteiro para ultimo dia do mes
                $finalDate = clone $date;
                $finalDate->modify('last day of this month');
                
                // Obtem os eventos do mes navegado
                $eventsSearch = ModNobosscalendarHelper::getEvents($date, $finalDate, $idModule);

                $skippedMonths++;

                // Setado para NAO exibir meses sem eventos e ja tentou o limite de meses: retorna vazio para que fique no ultimo mes com evento e bloqueie seta
                if(($custom->display_empty_month == 0) && ($skippedMonths == $monthSkipEmpty)){
                    exit(0);
                }
            }
        }

        // Obtem parametros da modal
        $modalCustomization = ModNobosscalendarHelper::getModalParams($idModule);

		exit(require JModuleHelper::getLayoutPath('mod_nobosscalendar', 'theme/model2_body'));
    }
    
    /**
	 * Metodo que obtem os eventos a exibir no modelo 2 (cards) qnd setado para exibir eventos corridos (sem ser mes a mes)
	 *
	 * @param 	Integer		$idModule 	 			Id do modulo
	 * @param 	Integer  	$eventsToDisplay		Limite de eventos a exibir (eh um parametro admin, mas qnd mobile vem como '1')
     * @param 	array  	    $intervalDays		    Intervalo de dias a exibir os eventos antes e depois da data atual
	 * @param 	String 		$navigation 	 		Sentido da navegacao do usuario (prev / next) (opcional)
     * @param 	Integer 	$firstEventIndex 	    Indice do evento mais proximo da navegacao clicada
	 *
	 * @return 	object 		Array com eventos a exibir e array com infos sobre setas de navegacao
	 */
	public static function getRunningEventsCards($idModule, $eventsToDisplay, $intervalDays, $navigation = '', $firstEventIndex = false){      
        $session = JFactory::getSession();

        // Monta objeto para retorno dos dados (vamos retornar os eventos e info de exibicao das setas)
        $return = new StdClass();
        $return->events = array();

        // Converte data atual para objeto sem horas
        $dateNow = new DateTime();		
        $dateNow = JDate::getInstance($dateNow->format('Y-m-d'));

        // Carrega library do cache, especificando o nome do arquivo de cache
        $cache = JFactory::getCache('nobosscalendar', '');

        // Habilita cache mesmo estando desabilitado nas configuracoes globais do Joomla
        $cache->setCaching(1);
        
        // Define o tempo do cache
        $cache->setLifeTime(86400);
        
        // Carregamento da pagina (nao eh navegacao) ou cache nao definido: busca os eventos e salva no cache
        if(($navigation == '') || (empty($cache->get("cards-events-{$idModule}")))){
            $initialDate = clone $dateNow;
            $lastDate = clone $dateNow;

            // Se nao estiver definido, coloca default de 15 dias antes e 180 dias depois
            if(empty($intervalDays)){
                $intervalDays = array('15', '180');
            }

            // Periodo que permitiremos o usuario navegar
            $initialDate->modify("-{$intervalDays[0]} days");
            $lastDate->modify("+{$intervalDays[1]} days");

            // Obtem eventos conforme criterio de pesquisa
            $events = ModNobosscalendarHelper::getEvents($initialDate, $lastDate, $idModule);

            // Converte resultado para array
            $events = (array)$events;

            // Recria os indices do array para ficar na mesma ordem de exibicao
            $events = array_values($events);
            
            // echo '<pre>';
            // var_dump($events);
            // exit;

            // Percorre todos os eventos para armazenar indice
            foreach ($events as $i => $event) {
                $event->indice = $i;

                // Indice do evento ainda nao definido e data do evento corrente eh igual ou maior que o dia de hoje
                if($firstEventIndex === false && ((JDate::getInstance($event->initial_date) >= $dateNow))){
                    // Seta como indice do primeiro evento a exibir
                    $firstEventIndex = $event->indice;
                }
            }

            // Salva valor no cache
            $cache->store($events, "cards-events-{$idModule}");
        }
        // Obtem eventos do cache
        else{
            $events = $cache->get("cards-events-{$idModule}");
        }

        // Usuario clicou na seta da ESQUERDA
        if($navigation == 'prev'){
            // Reduz o indice
            $firstEventIndex--;
        }
        // Usuario clicou na seta da DIREITA
        else if($navigation == 'next'){
            // Aumenta o indice
            $firstEventIndex++;
        }

        // Seta por padrao para setas de navegacao serem exibidas
        $return->show_navigation_prev = true;
        $return->show_navigation_next = true;

        // Nao existe evento ANTERIOR a exibir
        if(($firstEventIndex == 0) || (empty($events[$firstEventIndex]))){
            // Desabilita seta de navegacao de anterior
            $return->show_navigation_prev = false;      
        }
        // Nao existe PROXIMO evento a exibir
        if(empty($events[$firstEventIndex + $eventsToDisplay])){
            // Desabilita seta de navegacao de proximo
            $return->show_navigation_next = false;
        }
        
        // echo '<pre>';
        // var_dump($firstEventIndex);
        // exit;

        // Extrai somente os eventos a serem exibidos nesta vez
        $return->events = array_slice($events, $firstEventIndex, $eventsToDisplay);

        return $return;
	}

	/**
	 * Método ajax que atualiza calendário conforme navegação entre meses.
	 * Recebe reqiosições ajax 
	 *
	 * @return void.
	 */
	public static function showCalendarYearMonthsEventsAjax() {
        header('Access-Control-Allow-Origin: *');
		// Pega dados da requisição.
		$app = JFactory::getApplication();
		$input = $app->input;
		// Pega o id do módulo
		$idModule = $input->get("idModule");
		// Pega os parametros de estilo do mes do calendario
		$calendarParams = ModNobosscalendarHelper::getCalendarParams($idModule);
		$custom = $calendarParams->itemsCustomization;
		$externalArea = $calendarParams->externalArea;
        
		// Cria datas para o calendário.
		$currentDate = JDate::getInstance($input->get("currentDate"));
		// Pega a informação se avança ou retrocede
		$slide = $input->get("slide");
		// pega o primeiro dia do ano
		$date = self::getFirstDayOfYear($currentDate, $slide);
		// pega um array com todos os meses do ano e seu respectivo numero de eventos
		$yearMonths = self::getEventsYear($currentDate, $idModule, $slide);
		// Clona o primeiro dia para ser percorrido na montagem do calendario
		$dateFor = clone $date;
		exit(require JModuleHelper::getLayoutPath('mod_nobosscalendar', 'theme/model1_sidebar'));
	}
	
	/**
	 * Método que pega todas as parametrizações do calendário que são usadas por partes recarregáveis por ajax.
	 * Pega parametros da parte do mês do calendario e da listagem de evantos 
	 *
	 * @return Object Objeto com todos os parametros
	 */
	public static function getCalendarParams($idModule){
        jimport('noboss.util.modules');

        $app = JFactory::getApplication();
        // pega o template vinculado a pagina
        $tmpl = $app->getTemplate(true);
        // pega os parametros do template
        $tmplParams = $tmpl->params;
        
        // pega a cor primaria setada nos parametros do template
        $tmplPrimaryColor = $tmplParams->get('primary_color');
        
        // pega a cor secundaria setada nos parametros do template
        $tmplSecondaryColor = $tmplParams->get('secondary_color');

        $dataModule = NoBossUtilModules::getDataModule($idModule);

		$custom = (object) self::getItemsCustomization($idModule);	

		//variaveis necessarias para os arquivos de estilo
		$moduleParams = new StdClass();
		$moduleParams->extension_name = $dataModule->module;
		$moduleParams->module_name = str_replace("mod_", "", $dataModule->module);
        $moduleParams->id_module = $idModule;
        $moduleParams->id_calendar = $dataModule->params->id_calendar;
		$moduleParams->theme = $dataModule->params->theme;
		$theme = json_decode($moduleParams->theme)->theme;
        
        // Largura maxima permitida no calendario
        if (!empty($custom->calendar_maximum_width)){
            $custom->calendar_maximum_width = "max-width: {$custom->calendar_maximum_width}px";
        }
        else{
            $custom->calendar_maximum_width = "";
        }

        if(!empty($custom->calendar_font)){
			$custom->calendar_font_family = NoBossUtilFonts::importNobossfontlist($custom->calendar_font);
		}

		if(isset($custom->sidebar_background_color)){
			$custom->sidebar_background_color = !empty($custom->sidebar_background_color) ? $custom->sidebar_background_color : $tmplPrimaryColor;
		}
		
        // Cor de fundo da listagem de eventos
        $custom->calendar_events_background_color = !empty($custom->calendar_events_background_color) ? $custom->calendar_events_background_color : $tmplPrimaryColor;
        $custom->calendar_events_background_color = !empty($custom->calendar_events_background_color) ? "background-color: {$custom->calendar_events_background_color};" : "";

        // pega cor de fundo do cabecalho no mobile (caso nao esteja definido, pega a cor de fundo definida para lista de eventos)
        $custom->month_header_mobile_background_color = !empty($custom->month_header_mobile_background_color) ? "background-color: {$custom->month_header_mobile_background_color};" : $custom->calendar_events_background_color;
        
        // pega a cor definida no modulo ou a cor primaria do template
        $custom->calendar_texts_color = !empty($custom->calendar_texts_color) ? $custom->calendar_texts_color : $tmplPrimaryColor;
        $custom->calendar_texts_color = !empty($custom->calendar_texts_color) ? "color: {$custom->calendar_texts_color};" : "";

        // Cor de fundo da area do body do calendario
        if (!empty($custom->calendar_background_color)){
            $custom->calendar_background_color = "background-color: {$custom->calendar_background_color};";
        }
        else{
            // Se nao estiver definido, pega cor da area de listagem
            $custom->calendar_background_color = $custom->calendar_events_background_color;
        }
        
        if(($theme == "model1") || ($theme == "model3")){
            // Font size para dias da semana no body
            if(empty($custom->days_week_size)){
                $custom->days_week_size = '16';
            }
            $custom->days_week_size_em = $custom->days_week_size/16;
            $custom->days_week_size = "font-size: {$custom->days_week_size}px; font-size: {$custom->days_week_size_em}em;";

            // Font size mobile para dias da semana no body
            if(empty($custom->days_week_size_mobile)){
                $custom->days_week_size_mobile = '12';
            }
            $custom->days_week_size_mobile_em = $custom->days_week_size_mobile/16;
            $custom->days_week_size_mobile = "font-size: {$custom->days_week_size_mobile}px !important; font-size: {$custom->days_week_size_mobile_em}em !important;";

            // Font size para dias do mês no body
            if(!empty($custom->days_month_size)){
                $custom->days_month_size_em = $custom->days_month_size/16;
                $custom->days_month_size = "font-size: {$custom->days_month_size}px; font-size: {$custom->days_month_size_em}em;";
            }else{
                $custom->days_month_size = '';
            }

            // Font size mobile para dias do mês no body
            if(empty($custom->days_month_size_mobile)){
                $custom->days_month_size_mobile = '16';
            }
            $custom->days_month_size_mobile_em = $custom->days_month_size_mobile/16;
            $custom->days_month_size_mobile = "font-size: {$custom->days_month_size_mobile}px !important; font-size: {$custom->days_month_size_mobile_em}em !important;";

            // Font size para filtro de categorias
            if(!empty($custom->categories_size)){
                $custom->categories_size_em = $custom->categories_size/16;
                $custom->categories_size = "font-size: {$custom->categories_size}px; font-size: {$custom->categories_size_em}em;";
            }
            else{
                $custom->categories_size = '';
            }

            // Font size mobile para filtro de categorias
            if(!empty($custom->categories_size_mobile)){
                $custom->categories_size_mobile_em = $custom->categories_size_mobile/16;
                $custom->categories_size_mobile = "font-size: {$custom->categories_size_mobile}px !important; font-size: {$custom->categories_size_mobile_em}em !important;";
            }else{
                $custom->categories_size_mobile = '';
            }
            
            if(empty($custom->back_button_position_mobile)){
                $custom->back_button_position_mobile = 'top';
            }
        }

		if($theme == "model1"){
            // Define valor default para tag html do ano no sidebar
            if (empty($custom->year_header_tag_html)){
                $custom->year_header_tag_html = 'h3';
            }

            // Font size para ano do sidebar
            if(empty($custom->year_header_size)){
                $custom->year_header_size = '29';
            }
            $custom->year_header_size_em = $custom->year_header_size/16;
            $custom->year_header_size = "font-size: {$custom->year_header_size}px; font-size: {$custom->year_header_size_em}em;";

            // Font size mobile para ano do sidebar
            if(empty($custom->year_header_size_mobile)){
                $custom->year_header_size_mobile = '29';
            }
            $custom->year_header_size_mobile = "font-size: {$custom->year_header_size_mobile}px !important;";
            
            // Espacamento para ano no sidebar
            if (empty($custom->year_header_spacing)){
                $custom->year_header_spacing = "padding: 1.5em 1em 1em 1em;";
            }
            else{
                $custom->year_header_spacing = "padding: " . implode(' ', $custom->year_header_spacing) . ";";
            }

            // Espacamento para ano no sidebar para mobile
             if (empty($custom->year_header_spacing_mobile)){
                $custom->year_header_spacing_mobile = "padding: 0 1em 0 !important;";
            }
            else{
                $custom->year_header_spacing_mobile = "padding: " . implode(' ', $custom->year_header_spacing_mobile) . " !important;";
            }

            // Font size para meses do sidebar
            if(empty($custom->sidebar_months_size)){
                $custom->sidebar_months_size = '15';
            }
            $custom->sidebar_months_size_em = $custom->sidebar_months_size/16;
            $custom->sidebar_months_size = "font-size: {$custom->sidebar_months_size}px; font-size: {$custom->sidebar_months_size_em}em;";
            
            // Font size mobile para meses do sidebar
            if(empty($custom->sidebar_months_size_mobile)){
                $custom->sidebar_months_size_mobile = '19';
            }
            $custom->sidebar_months_size_mobile_em = $custom->sidebar_months_size_mobile/16;
            $custom->sidebar_months_size_mobile = "font-size: {$custom->sidebar_months_size_mobile}px !important;; font-size: {$custom->sidebar_months_size_mobile_em}em !important;";
            
            // Espacamento para meses no sidebar
            if (empty($custom->sidebar_months_spacing)){
                $custom->sidebar_months_spacing = "padding: 9px 72px 10px 16px;";
            }
            else{
                $custom->sidebar_months_spacing = "padding: " . implode(' ', $custom->sidebar_months_spacing) . ";";
            }

            // Espacamento para meses no sidebar para mobile
            if (empty($custom->sidebar_months_spacing_mobile)){
                $custom->sidebar_months_spacing_mobile = "padding: 10px 15px !important;";
            }
            else{
                $custom->sidebar_months_spacing_mobile = "padding: " . implode(' ', $custom->sidebar_months_spacing_mobile) . " !important;";
            }

            // Tamanho da fonte da bolinha que indica qnt de eventos do mes na navegacao
            $custom->sidebar_months_bullet_size = (!empty($custom->sidebar_months_bullet_size)) ? "font-size: {$custom->sidebar_months_bullet_size}px;" : "font-size: 13px;";

            // Tamanho da fonte da bolinha que indica qnt de eventos do mes na navegacao (mobile)
            $custom->sidebar_months_bullet_size_mobile = (!empty($custom->sidebar_months_bullet_size_mobile)) ? "font-size: {$custom->sidebar_months_bullet_size_mobile}px !important;" : "font-size: 14px !important;";

            // Largura da bolinha que indica qnt de eventos do mes na navegacao
            $custom->sidebar_months_bullet_width = (!empty($custom->sidebar_months_bullet_width)) ? "width: {$custom->sidebar_months_bullet_width}px; height: {$custom->sidebar_months_bullet_width}px;" : "width: 18px; height: 18px;";

            // Largura da bolinha que indica qnt de eventos do mes na navegacao (mobile)
            $custom->sidebar_months_bullet_width_mobile = (!empty($custom->sidebar_months_bullet_width_mobile)) ? "width: {$custom->sidebar_months_bullet_width_mobile}px !important; height: {$custom->sidebar_months_bullet_width_mobile}px !important;" : "width: 19px !important; height: 19px !important;";
		}		
		
		// Setas
		$custom->arrows_style = "";
		$custom->arrows_style_hover = "";
		$custom->arrows_style_mobile = "";
		if(!empty($custom->arrows_icon_size)){
			$arrowsIconSizeEm = $custom->arrows_icon_size/16;
			$custom->arrows_style = "font-size: {$custom->arrows_icon_size}px; font-size: {$arrowsIconSizeEm}em;";
			if(isset($custom->arrows_icon_size_mobile)){
				$arrowIconSizeMobile = $custom->arrows_icon_size_mobile;
				$arrowIconSizeMobileEm = $arrowIconSizeMobile/16;
				$custom->arrows_style_mobile =  "font-size: {$arrowIconSizeMobile}px !important; font-size: {$arrowIconSizeMobileEm}em !important;";
			}
		}
		
        // Cor das setas de navegacao (se nao estiver definido, pega cor primaria do template)
        $custom->arrows_color = !empty($custom->arrows_color) ? $custom->arrows_color : $tmplPrimaryColor;
        $custom->arrows_style .= " color: {$custom->arrows_color};";

        // Cor das setas de navegacao no mobile (se nao estiver definido, pega do desktop)
        $custom->arrows_color_mobile = !empty($custom->arrows_color_mobile) ? $custom->arrows_color_mobile : $custom->arrows_color;
        $custom->arrows_style_mobile .= " color: {$custom->arrows_color_mobile} !important;";

        if ($custom->show_arrows_border && !empty($custom->arrows_border_color)){
            $custom->arrows_style .= "border: 1px solid {$custom->arrows_border_color};";
        }
        if ($custom->show_arrows_border_radius && !empty($custom->arrows_border_radius)){
            $custom->arrows_style .= "border-radius: {$custom->arrows_border_radius}px;";
        }
        if ($custom->show_arrows_background && !empty($custom->arrows_background)){
            // pega a cor definida no modulo ou a cor primaria do template
            $custom->arrows_background = !empty($custom->arrows_background) ? $custom->arrows_background : $tmplPrimaryColor;
            $custom->arrows_style .= "background-color: {$custom->arrows_background}; ";
            // pega a cor definida no modulo ou a cor secundaria do template
            $custom->arrows_background_hover = !empty($custom->arrows_background_hover) ? $custom->arrows_background_hover : $tmplSecondaryColor;
            $custom->arrows_style_hover .= "background-color: {$custom->arrows_background_hover} !important; ";
        }
        $custom->arrows_style .= "padding: " . implode(' ', $custom->arrows_spacing) . ";";
		
		$custom->description = "";
		$custom->month_header_style = "";

        // Modelo de cards
        if($theme == 'model2'){           
            // Campo 'definir largura maxima para calendario' (sim / nao)
            if (empty($custom->limit_maximum_width)){
                $custom->limit_maximum_width = '0';
            }

            // Campo Limite de dias 'antes' e 'depois' da data atual
            if (empty($custom->interval_days)){
                $custom->interval_days = array();
            }

            // Campo 'Exibir meses sem eventos' (0/1)
            if(!isset($custom->display_empty_month)){
                $custom->display_empty_month = 1;
            }

            // Nao eh navegacao por meses e acesso eh mobile: forca para exibir apenas um item por vez
            if (($custom->browsing_month == 0) && ((strpos($_SERVER['HTTP_USER_AGENT'],"iPhone") == true) || (strpos($_SERVER['HTTP_USER_AGENT'],"Android") == true))){
                $custom->events_to_display = 1;
            }
            // Acesso tablet: exibe itens por linha conforme parametro proprio do tablet
            else if ((strpos($_SERVER['HTTP_USER_AGENT'],"iPad") == true) || (strpos($_SERVER['HTTP_USER_AGENT'],"Kindle") == true) || (strpos($_SERVER['HTTP_USER_AGENT'],"wOSBrowser") == true)){ 
                if (empty($custom->events_per_line_tablet)){
                    $custom->events_per_line_tablet = '2';
                }
                $custom->events_per_line = $custom->events_per_line_tablet;
            }

            // Campo que define se link eh em todo card ou em botao
            $custom->events_link_display = (!empty($custom->events_link_display)) ? $custom->events_link_display : 'card';

            $custom->button_style = "";
            $custom->button_style_mobile = "";
            $custom->button_style_hover = "";

            // Exibicao eh por botao: pega texto e estilos do botao
            if($custom->events_link_display == 'button'){
                // Texto do botao (se usuario nao definir, pega de constante de traducao)
                $custom->events_bt_text = (!empty($custom->events_bt_text)) ? $custom->events_bt_text : JText::_('MOD_NOBOSSCALENDAR_LINK_EVENT_TEXT_DEFAULT');
                
                // Cor do texto do botao
                $custom->events_bt_text_color = !empty($custom->events_bt_text_color) ? $custom->events_bt_text_color : 'rgba(255, 255, 255, 1)';
                // Cor do texto no hover do botao
                $custom->events_bt_hover_text_color = !empty($custom->events_bt_hover_text_color) ? $custom->events_bt_hover_text_color : 'rgba(255, 255, 255, 1)';
                // Cor de fundo do botao
                $custom->events_bt_background_color = !empty($custom->events_bt_background_color) ? $custom->events_bt_background_color : 'rgba(100, 100, 100, 1)';
                // Cor da borda do botao
                $custom->events_bt_border_color = !empty($custom->events_bt_border_color) ? $custom->events_bt_border_color : 'rgba(100, 100, 100, 1)';
                // Cor de fundo no hover
                $custom->events_bt_background_hover_color = !empty($custom->events_bt_background_hover_color) ? $custom->events_bt_background_hover_color : 'rgba(100, 100, 100, 0.8)';
                // Cor da borda no hover
                $custom->events_bt_hover_border_color = !empty($custom->events_bt_hover_border_color) ? $custom->events_bt_hover_border_color : 'rgba(100, 100, 100, 0.8)';
                
                // Modelo do botao
                $custom->events_bt_style = !empty($custom->events_bt_style) ? $custom->events_bt_style : 'squared-button';                

                // Monta css do botao conforme modelo escolhido
                switch ($custom->events_bt_style) {
                    // Botao quadrado
                    case 'squared-button':
                        $custom->button_style = "color: {$custom->events_bt_text_color}; background-color: {$custom->events_bt_background_color};";
                        $custom->button_style_hover = "color: {$custom->events_bt_hover_text_color} !important; background-color: {$custom->events_bt_background_hover_color} !important;";
                        break;
                    // Botao arredondado
                    case 'rounded-button':
                        $custom->button_style = "color: {$custom->events_bt_text_color}; background-color: {$custom->events_bt_background_color}; border-radius: {$custom->events_bt_border_radius_size}px;";
                        $custom->button_style_hover = "color: {$custom->events_bt_hover_text_color} !important; background-color: {$custom->events_bt_background_hover_color} !important;";
                        break;
                    // Botao transparente quadrado
                    case 'ghost-squared-button':
                        $custom->button_style = "background-color: transparent; border: 2px solid; color: {$custom->events_bt_text_color}; border-color: {$custom->events_bt_border_color};";
                        $custom->button_style_hover = "color: {$custom->events_bt_hover_text_color} !important; border-color: {$custom->events_bt_hover_border_color} !important; background-color: {$custom->events_bt_background_hover_color} !important;";
                        break;
                    // Botao transparente arredondado
                    case 'ghost-rounded-button':
                        $custom->button_style = "background-color: transparent; border: 2px solid; color: {$custom->events_bt_text_color}; border-color: {$custom->events_bt_border_color}; border-radius: {$custom->events_bt_border_radius_size}px;";
                        $custom->button_style_hover = "color: {$custom->events_bt_hover_text_color} !important; border-color: {$custom->events_bt_hover_border_color} !important; background-color: {$custom->events_bt_background_hover_color} !important;";
                        break;
                    default:
                        $custom->button_style = "color: {$custom->events_bt_text_color};";
                        break;
                }

                // Tipo de fonte para o texto
                $custom->events_bt_font = (!empty($custom->events_bt_font)) ? $custom->events_bt_font : '{"fontfamily":"Roboto_Regular.ttf","externalLinked":"","fontStyle":"Regular"}';
                $custom->button_style .= NoBossUtilFonts::importNobossfontlist($custom->events_bt_font); 

                // Transformacao do texto (maiusculo, minusculo, etc)
                $custom->events_bt_text_transform = (!empty($custom->events_bt_text_transform)) ? $custom->events_bt_text_transform : 'uppercase';
                $custom->button_style .= " text-transform: " . $custom->events_bt_text_transform . ";";                
                
                // Font size do botao
                $custom->events_bt_text_size = (!empty($custom->events_bt_text_size)) ? $custom->events_bt_text_size : '14';
                $buttonsSize = $custom->events_bt_text_size;
                if(!empty($buttonsSize)){
                    $buttonsSizeEm = $buttonsSize/16;
                    $custom->button_style .= " font-size: {$buttonsSize}px; font-size: {$buttonsSizeEm}em;";
                }

                // Font size do botao no mobile
                $custom->events_bt_text_size_mobile = (!empty($custom->events_bt_text_size_mobile)) ? $custom->events_bt_text_size_mobile : '13';
                $buttonsSizeEmMobile = $custom->events_bt_text_size_mobile/16;
                $custom->button_style_mobile .= " font-size: {$custom->events_bt_text_size_mobile}px !important; font-size: {$buttonsSizeEmMobile}em !important;";

                // Espacamento interno do botao
                $custom->button_style .= isset($custom->events_bt_inner_space) ? ' padding: '.implode(' ', $custom->events_bt_inner_space).';' : ' padding: 10px 12px;';
                
                // Espacamento interno do botao no mobile
                $custom->button_style_mobile .= isset($custom->events_bt_inner_space_mobile) ? ' padding: '.implode(' ', $custom->events_bt_inner_space_mobile).' !important;' : 'padding: 10px 12px !important;';

                // Espacamento externo do botao
                $custom->button_style .= isset($custom->events_bt_outer_space) ? ' margin: '.implode(' ', $custom->events_bt_outer_space).';' : 'margin: 15px 0 0 0;';
                
                // Espacamento externo do botao no mobile
                $custom->button_style_mobile .= isset($custom->events_bt_outer_space_mobile) ? ' margin: '.implode(' ', $custom->events_bt_outer_space_mobile).' !important;' : 'margin: 15px 0 0 0 !important;';
            }
		}

		// string com o nome da modal da área externa concatenado com o modelo selecionado 
		$externalAreaXml = "external_area";
		// variável que armazena o objeto com os parâmetros da modal de área externa do tema selecionado  
		$externalArea = json_decode($dataModule->params->$externalAreaXml);
		// //pega o valor da cor de fundo da area externa
		// $externalArea = new StdClass();
		// if(!empty($externalArea->external_area_background_color)){
        //     // pega a cor definida no modulo ou a cor primaria do template
        //     $externalArea->external_area_background_color = !empty($externalArea->external_area_background_color) ? $externalArea->external_area_background_color : $tmplPrimaryColor;
		// 	$externalArea->external_area_background_color = $externalArea->external_area_background_color;
		// }
		
        // Parametros para o mes no cabecalho
		$custom->month_header_style .= !empty($custom->month_header_color) ? "color: {$custom->month_header_color};" : "";
		$custom->month_header_style .= !empty($custom->month_header_alignment) ? "text-align: {$custom->month_header_alignment};" : "text-alignment: left;";
        $custom->month_header_style .= !empty($custom->month_header_transform) ? "text-transform: {$custom->month_header_transform}; " : "text-transform: none; ";
        $custom->month_header_style .= !empty($custom->month_header_font) ? str_replace("\"", "'", NoBossUtilFonts::importNobossfontlist($custom->month_header_font)) : "";

        // Espacamento do header de mes
        if(!empty($custom->month_header_space)){
			$custom->month_header_space = " padding:".implode(" ", $custom->month_header_space).";";
        }else{
            $custom->month_header_spac = '';
        }

        // Espacamento do header de mes no mobile
        if(!empty($custom->month_header_space_mobile)){
			$custom->month_header_space_mobile = "padding:".implode(" ", $custom->month_header_space_mobile)." !important;";
        }else{
            $custom->month_header_space_mobile = '';
        }

        // Font size para tamanho do mes no body
		if(!empty($custom->month_header_size)){
			$custom->month_header_size_em = $custom->month_header_size/16;
			$custom->month_header_size = "font-size: {$custom->month_header_size}px; font-size: {$custom->month_header_size_em}em;";
			$custom->month_header_style .= $custom->month_header_size;
		}
        
        // Font size mobile para tamanho do mes no body
		$custom->month_header_style_mobile = "";
		if(!empty($custom->month_header_size_mobile)){
			$monthHeaderSizeMobile = $custom->month_header_size_mobile;
			$monthHeaderSizeMobileEm = $monthHeaderSizeMobile/16;
			$custom->month_header_style_mobile .= "font-size: {$monthHeaderSizeMobile}px !important;";
		}

        $custom->month_header_style_mobile .= (!empty($custom->month_header_color_mobile)) ? "color: {$custom->month_header_color_mobile};" : "";

		if($theme == 'model2'){
            // Valores default para eventos por linha
            $custom->events_per_line_tablet = (!empty($custom->events_per_line_tablet)) ? $custom->events_per_line_tablet : '2';
            $custom->events_per_line_mobile = (!empty($custom->events_per_line_mobile)) ? $custom->events_per_line_mobile : '1';

            // Monta estilo da <ul> para definicao de quantidade de colunas
            $custom->gridList = "grid-template-columns: repeat({$custom->events_per_line}, 1fr);";
            $custom->gridListTablet = "grid-template-columns: repeat({$custom->events_per_line_tablet}, 1fr);";
            $custom->gridListMobile = "grid-template-columns: repeat({$custom->events_per_line_mobile}, 1fr);";

            // Valores default para espacamento entre videos
            $custom->grid_gap = !empty($custom->grid_gap) ? $custom->grid_gap : '2';
            $custom->grid_gap_tablet = !empty($custom->grid_gap_tablet) ? $custom->grid_gap_tablet : '2';
            $custom->grid_gap_mobile = !empty($custom->grid_gap_mobile) ? $custom->grid_gap_mobile : '2';

            // Monta estilo da <ul> para espacamento entre videos no grid
            $custom->gridList .= "grid-gap: {$custom->grid_gap}px;";
            $custom->gridListTablet .=" grid-gap: {$custom->grid_gap_tablet}px;";
            $custom->gridListMobile .=" grid-gap: {$custom->grid_gap_mobile}px;";
		}

		// Parâmetros para título
		$custom->events_title_style = "";
		$custom->events_title_font_family = NoBossUtilFonts::importNobossfontlist($custom->events_title_font); 
		$custom->events_title_style .= $custom->events_title_font_family;
		$custom->events_title_style .= !empty($custom->events_title_alignment) ? "text-align: {$custom->events_title_alignment}; " : "";
		$custom->events_title_style .= !empty($custom->events_title_transform) ? "text-transform: {$custom->events_title_transform}; " : "";
		
        // Tamanho da fonte titulo
        $custom->events_title_style .= (!empty($custom->events_title_size)) ? "font-size: {$custom->events_title_size}px;" : "";

        // Tamanho da fonte titulo no mobile
        $custom->events_title_style_mobile = (!empty($custom->events_title_size_mobile)) ? "font-size: {$custom->events_title_size_mobile}px !important;" : "";

        // Espacamento titulo
        $custom->events_title_style .= (empty($custom->events_title_space)) ? " padding: 7px 0px 5px;" : " padding:".implode(" ", $custom->events_title_space).";";

		// Parâmetros para texto de apoio
		$custom->events_subtitle_style = "";
		$custom->events_subtitle_font_family = NoBossUtilFonts::importNobossfontlist($custom->events_subtitle_font);
		$custom->events_subtitle_style .= NoBossUtilFonts::importNobossfontlist($custom->events_subtitle_font);
		$custom->events_subtitle_style .= !empty($custom->events_subtitle_alignment) ? "text-align: {$custom->events_subtitle_alignment}; " : "";
		$custom->events_subtitle_style .= !empty($custom->events_subtitle_transform) ? "text-transform: {$custom->events_subtitle_transform}; " : "";
		
        // Tamanho da fonte descricao curta
        $custom->events_subtitle_style .= (!empty($custom->events_subtitle_size)) ? "font-size: {$custom->events_subtitle_size}px;" : "";

        // Tamanho da fonte descricao curta no mobile
        $custom->events_subtitle_style_mobile = (!empty($custom->events_subtitle_size_mobile)) ? "font-size: {$custom->events_subtitle_size_mobile}px !important;" : "";

        // Espacamento descricao curta
        $custom->events_subtitle_style .= (empty($custom->events_subtitle_space)) ? "" : " padding:".implode(" ", $custom->events_subtitle_space).";";

        // Tamanho da fonte das datas do evento
        $custom->events_date_style = (!empty($custom->events_date_size)) ? "font-size: {$custom->events_date_size}px;" : "font-size: 12px;";

        // Tamanho da fonte das datas no mobile
        $custom->events_date_style_mobile = (!empty($custom->events_date_size_mobile)) ? "font-size: {$custom->events_date_size_mobile}px !important;" : "";

        // Espacamento das datas
        $custom->events_date_style .= (empty($custom->events_date_space)) ? " padding: 0px 10px 0px 0px;" : " padding:".implode(" ", $custom->events_date_space).";";
        
        // Formato da hora
        $custom->events_schedule_format = (empty($custom->events_schedule_format)) ? 'complete' : $custom->events_schedule_format;

        // Tamanho da fonte de horario
        $custom->events_schedule_style = (!empty($custom->events_schedule_size)) ? "font-size: {$custom->events_schedule_size}px;" : "font-size: 12px;";

        // Tamanho da fonte de horario no mobile
        $custom->events_schedule_style_mobile = (!empty($custom->events_schedule_size_mobile)) ? "font-size: {$custom->events_schedule_size_mobile}px !important;" : "";

        // Espacamento do horario
        $custom->events_schedule_style .= (empty($custom->events_schedule_space)) ? " padding: 0px 0px 0px 0px;" : " padding:".implode(" ", $custom->events_schedule_space).";";
        
        // Tamanho da fonte de endereço
        $custom->events_address_style = (!empty($custom->events_address_size)) ? "font-size: {$custom->events_address_size}px;" : "font-size: 12px;";
  
        // Tamanho da fonte de endereço no mobile
        $custom->events_address_style_mobile = (!empty($custom->events_address_size_mobile)) ? "font-size: {$custom->events_address_size_mobile}px !important;" : "";

        // Espacamento do endereço
        $custom->events_address_style .= (empty($custom->events_address_space)) ? " padding: 0px 0px 5px 0px;" : " padding:".implode(" ", $custom->events_address_space).";";

        // Cor para mensagem 'sem eventos a exibir'
        $custom->color_message_without_events = (!empty($custom->color_message_without_events)) ? "color: {$custom->color_message_without_events};" : "";
        
        // Parametro que define se barra lateral com lista de eventos pode ser exibida no desktop
        $custom->display_month_events = (empty($custom->display_month_events)) ? '1': $custom->display_month_events;

        // Largura minima a reservar para body do calendario (utilizado para saber se ha espaco para exibir barra lateral)
        if (!empty($custom->bar_list_minimum_width)){
            // Divide por 100 para colocar em formato que permite calculos (Ex: 50% fica como 0.5)
            $custom->bar_list_minimum_width = $custom->bar_list_minimum_width / 100;
        }else{
            $custom->bar_list_minimum_width = '0.5';
        }
        // Eventos a exibir por padrao na listagem lateral
        if (!isset($custom->events_to_display_default)){
            $custom->events_to_display_default = 'month';
        }

		$params = new StdClass();
		$params->moduleParams = $moduleParams;
		$params->itemsCustomization = $custom;
		$params->externalArea = $externalArea;

		// Retorna um objeto com todos estes parametros
		return $params;
	}

	/**
	 * Pega os todos os meses do ano com o seu respectivo numero de eventos
	 *
	 * @param array $data			 Objeto data de referencia para saber o ano de busca
	 * @param JDate $idModule 	 	 id do múdulo para busca dos parametros
	 *
	 * @return array Retorna lista com os meses e o numero de eventos
	 */
	public static function getEventsYear($date, $idModule, $slide = 'current') {
		// Array de retorno
		$months = array();
		// Pega o primeiro mes do ano
		$date = self::getFirstDayOfYear($date, $slide);

		// Define o ano atual como limite
        $limitYear = $date->year;
        
		// Enquanto dentro do limite do ano da busca.
		while ($date->year == $limitYear) {
			// Data inicial do mes.
			$startDate = clone $date;
			$startDate->modify('first day of this month');
			$startDate->modify('midnight');
			// Data final do mes.
			$finalDate = clone $date;
			$finalDate->modify('last day of this month');
			$finalDate->modify('midnight');

            // Pega os eventos.
			$events = self::getEvents($startDate, $finalDate, $idModule);
			// Salva no array o mes como a chave e o numero de eventos como valor
			$months[$date->__toString()] = count((array)$events);
			// Avança uma mes
			$date = $date->getInstance($date->toSql() . " +1 month");
		}
		// Retorna o array
 		return $months;
	}

	/**
	 * Pega dias do calendario que possuem eventos ou períodos importantes.
	 *
	 * @param array $events				 Lista de eventos para extrair os dias e datas importantes.
	 * @param JDate $referenceDate 	 Data referência para buscar os dias de eventos.
	 *
	 * @return array Retorna lista com dias de eventos.
	 */
	public static function getEventsDays($events, $referenceDate = false) {

		$eventDays = array();

        $jToday = new JDate(date('Y-m-d'));

		// Se é um calendário com o mesmo mês da data de hoje.
		if($referenceDate->month == $jToday->month) {
			/* Adiciona legenda de "hoje". */
			$legend['description'] = JText::_('MOD_NOBOSSCALENDAR_LEGEND_TODAY');
			$legend['class'] = 'square-hoje';
        }

        // Percorre todos os eventos.
		foreach ($events as $key => $event) {
            // Pega o primeiro dia do mes e define o mes de corte
			$date = self::getFirstDayMonth($referenceDate);
			$edgeMonth = $date->month;

			// Percorre todos os dias do mes do evento para saber em quais ele deve ser exibido
			while ($date->month == $edgeMonth) {
				// Pega a data inicial e final do evento/periodo
				$eventInitialDate = self::createJDateFromString($event->initial_date, true);
                $eventFinalDate = self::createJDateFromString($event->final_date, true);

                // Dia corrente esta nos dias do evento: adiciona ao array de retorno
                if (isset($event->days) && in_array($date->day, $event->days)){
                    $eventDays['days'][$date->day][] = $event;
                }
               
				// Avança um dia
				$date = $date->getInstance($date->toSql() . " +1 day");
            }
         }
        
 		return $eventDays;
	}

	/**
	 * Metodo que ordena eventos de forma crescente atraves da data e hora
	 *
	 * @param array 	$calendarItem1		Evento 1 a comparar
	 * @param array 	$calendarItem2 		Evento 2 a comparar
	 *
	 * @return int Se a reunião 1 tiver data maior que a reunião 2 retorna 1, se a reunião 2
	 * tiver data maior que a reunião 1 retorna -1, se as duas datas forem iguais retorna 0.
	 */
	private static function sortAscendingEvents($calendarItem1, $calendarItem2) {
        $date1 = $calendarItem1->initial_date;
        $date2 = $calendarItem2->initial_date;

        if(!empty($calendarItem1->hours[0])){
            $date1 .= ' '.$calendarItem1->hours[0]->initial.':00';
        }
        else{
            $date1 .= ' 00:00:00';
        }
        if(!empty($calendarItem2->hours[0])){
            $date2 .= ' '.$calendarItem2->hours[0]->initial.':00';
        }
        else{
            $date2 .= ' 00:00:00';
        }

        // Converte para objeto data
		$date1 = JDate::getInstance($date1);
		$date2 = JDate::getInstance($date2);

		if ($date1 == $date2) {
			return 0;
		} elseif ($date1 > $date2) {
			return 1;
		} else {
			return -1;
		}
	}

	/**
	 * Pega dias de eventos e períodos importantes de uma faixa de data.
	 *
	 * @param  JDate 	$searchInicialData      Filtro de data incial
	 * @param  JDate 	$searchFinalDate        Filtro de data final
	 * @param  int		$idModule               Id do módulo.
	 *
	 * @return array 	Retorna uma lista de dados de objetos.
	 */
	public static function getEvents($searchInicialData, $searchFinalDate, $idModule, $filterCategory = false) {
        jimport('noboss.util.modules');

        $app = JFactory::getApplication();
        // pega o template vinculado a pagina
        $tmpl = $app->getTemplate(true);
        // pega os parametros do template
        $tmplParams = $tmpl->params;
        // pega a cor primaria setada nos parametros do template
        $tmplPrimaryColor = $tmplParams->get('primary_color');
        // pega a cor secundaria setada nos parametros do template
        $tmplSecondaryColor = $tmplParams->get('secondary_color');
        
        // Pega dados do módulo.
		$dataModule = NoBossUtilModules::getDataModule($idModule);		
		$moduleParams = $dataModule->params;
        
		// Pega os eventos do subform
		$custom = self::getItemsCustomization($idModule);
		$theme = json_decode($moduleParams->theme)->theme;

        // Id do calendario definido
        if (isset($moduleParams->id_calendar)){
            $events = self::getEventsDataBase($moduleParams->id_calendar, $idModule, 1);
        }

        // Novo array com eventos
        $newEvents = array();

		if(!empty($events)){
            // variavel que fica incrementando a cada evento sem categoria, para que os ids fiquem unicos
            $noneCounter = 1;

            // Converte resultado para array
            $events = (array)$events;

            // Total de eventos antes de percorre-los
            $totalEvents = count($events);

            // Contador do proximo item a criar de evento (se necessario criar novo item de evento) 
            $indexNextEvent = ($totalEvents+1);

            // echo '<pre>';
            // var_dump($events);
            // exit;

            // Percorre todas as datas configuradas no módulo.
            foreach ($events as $key => $event) {
                
                // Decoda dados do evento (se for possivel)
                // TODO: qnd tirarmos a coluna data_json, esse decode nao eh mais necessario
                if(@json_decode($event)){
                    $event = json_decode($event);
                }
                // Ocorreu erro ao decodar (provavelmente algum caracter invalido na string)
                else{
                    // Exibe erro no console do navegador para ajudar a debugar
                    echo "<script>console.log('Ocorreu erro para realizar json_decode de um evento na função getEvents. String do evento que deu erro: {$event}');</script>";
                    continue;
                }

                // Seta valores default para campos que podem nao estar definidos
                if (!isset($event->recurrence_type)){
                    // echo '<pre>';
                    // var_dump($event);
                    // exit;
                    $event->recurrence_type = '';
                }
                if (!isset($event->link_target)){
                    $event->link_target = '';
                }
                if (!isset($event->event_subtitle)){
                    $event->event_subtitle = '';
                }
                if (!isset($event->event_place)){
                    $event->event_place = '';
                }
                if (!isset($event->event_place_link)){
                    $event->event_place_link = 'none';
                }
                if (!isset($event->event_place_link_href)){
                    $event->event_place_link_href = '';
                }
				if(!empty($event->recurrent_days)){
                    $event->recurrent_days = json_decode($event->recurrent_days);
                    // Converte para array e garante que as posicoes do array sejam int
                    $event->recurrent_days = array_values((array) $event->recurrent_days);
				}
				if(!empty($event->specific_dates)){
					$event->specific_dates = json_decode($event->specific_dates);
				}
				if(!empty($event->recurrent_days_ignore)){
					$event->recurrent_days_ignore = json_decode($event->recurrent_days_ignore);
				}
				else{
					$event->recurrent_days_ignore = array();
				}

                // Obtem data e hora atual
                //$nowDate = date('Y-m-d H:i:s');

                // Data inicial do evento definida e evento nao eh recorrente do tipo datas especificadas
                if(!empty($event->initial_date) && (!isset($event->is_recurrent) || (($event->recurrence_type != 'specific-dates') && ($event->recurrence_type != 'specific-dates-hours')))){
                    
                    // Data final nao definida
                    if (empty($event->final_date)){
                        // Evento eh recorrente do tipo dia da semana: coloca data final como data atual somada em 5 anos (valor simbolico)
                        if ((isset($event->is_recurrent)) && ($event->is_recurrent)){
                            $event->final_date = (date('Y')+5).date('-m-d');
                        }
                        // Evento nao recorrente: seta data final igual a data inicial
                        else{
                            $event->final_date = $event->initial_date;
                        }
                    }

                    // Pega o objeto date a partir da string
					$eventInitialDate = self::createJDateFromString($event->initial_date, true);
					$eventFinalDate = self::createJDateFromString($event->final_date, true);

                    // ((data inicial evento < data inicial pesquisada) && (data final do evento < data inicial pesquisada) || (data inicial evento > data final pesquisada))
                    if((($eventInitialDate < $searchInicialData) && ($eventFinalDate < $searchInicialData)) || ($eventInitialDate > $searchFinalDate)){
                        continue;
                    }
                }

                // Decoda json das horas
                $event->hours = json_decode($event->hours);

                $category = new StdClass();

                // Categoria nao esta definida
                if (empty($event->event_category) || ($event->event_category == "none")){
                    $category->id = "none{$noneCounter}";
                    $category->legend = JText::_('MOD_NOBOSSCALENDAR_CATEGORY_COLOR');

                    // Cor ainda nao esta definida
                    if (empty($category->category_color)){
                        // Existe cor primaria do template nemesis
                        if(!empty($tmplPrimaryColor)){
                            $category->category_color = $tmplPrimaryColor;
                        }
                        // Cor manual
                        if (!empty($event->event_manual_color)){
                            $category->category_color = $event->event_manual_color;
                        }
                        // Seta uma cor default
                        else{
                            $category->category_color = '#333';
                        }
                    }

                    if($theme != "model2"){
                        $category->show_in_legends = $custom->show_legends;
                    }
                    $event->event_category = $category;

                    // incrementa o contador de eventos sem categorias
                    $noneCounter++;
                }

                // Evento da categoria feriado
				else if($event->event_category == "holidays"){
 					$category = new StdClass();
					$category->id = $event->event_category;
                    $category->legend = JText::_('MOD_NOBOSSCALENDAR_CATEGORY_HOLIDAYS');
                    // pega a cor definida no modulo ou a cor primaria do template
                    $custom->holidays_background_color = !empty($custom->holidays_background_color) ? $custom->holidays_background_color : $tmplPrimaryColor;
					$category->category_color = $custom->holidays_background_color;
					
                    if($theme != "model2"){
                        $category->show_in_legends = $custom->show_legends;
                    }
					
                    $event->event_category = $category;
                }
                // Obtem dados da categoria no banco
                else {
                    $event->event_category = ModNobosscalendarHelper::getCategories($moduleParams->id_calendar, $event->event_category);
                }

                // Usuario filtrou uma categoria e evento nao pertence a ela: exclui do array
				if (!empty($filterCategory) && ($event->event_category->id != $filterCategory)) {
					continue;
                }

                // Converte ocorrencias de '##' (utilizado para nao ocorrer problemas com aspas ao salvar no admin) por aspas simples
				$event->event_title 	= str_replace("##", "'", $event->event_title);
				$event->event_subtitle 	= str_replace("##", "'", $event->event_subtitle);
                $event->event_place 	= str_replace("##", "'", $event->event_place);
                
                // Link para o local
                switch ($event->event_place_link) {
                    case 'googlemaps':
                        $event->event_place_link =  ModNobosscalendarHelper::addGoogleMapLink($event->event_place);
                        break;

                    case 'manual':
                        $event->event_place_link = $event->event_place_link_href;
                        break;
                    
                    case 'none':
                    default:
                        $event->event_place_link = '';
                        # code...
                        break;
                }
                unset($event->event_place_link_href);

                // Link para o evento
                switch ($event->link_option){
                    // Abrir item de menu do site
                    case 'internal':
                        if(!empty($event->link_internal)){
                            $event->link = NoBossUtilUrl::getUrlItemMenu($event->link_internal);
                        }else{
                            $event->link_option = 'none';
                        }
                        break;
                    // Abrir url externa
                    case 'external':
                        // Valida se a "URL externa" não é vazia e se é uma URL válida.
                        if(!empty($event->link_external) && filter_var($event->link_external, FILTER_VALIDATE_URL)){
                            $event->link = $event->link_external;
                        }else{
                            $event->link_option = 'none';
                        }
                        break;
                    // Abrir modal de conteudo
                    case 'modal':
						if(isset($event->modal_option)){
							// Tipo de modal
							switch ($event->modal_option){
								// Artigo Joomla
								case 'article':
									if(!empty($event->modal_article)){
										$db = JFactory::getDBO();
										$sql = "SELECT introtext FROM #__content WHERE id = ".intval($event->modal_article);
										$db->setQuery($sql);
										$event->modal_content = $db->loadResult();
									}
									break;
								// Modulo
								case 'module':
									//$event->modal_module
									if(!empty($event->modal_module)){
                                        // Versao do Joomla eh menor que 3.9: carrega o modulo pela posicao
                                        if (JVERSION < '3.9') {
                                            // Obtem o conteudo a partir da posicao de modulo
                                            $db = JFactory::getDBO();
                                            $sql = "SELECT position FROM #__modules WHERE id = ".intval($event->modal_module);
                                            $db->setQuery($sql);
                                            $position = $db->loadResult();
                                            $event->modal_content = JHTML::_('content.prepare', '{loadposition '.$position.'}');
                                        }
                                        else{
                                            // Obtem o conteudo a partir do id
                                            $event->modal_content = JHTML::_('content.prepare', '{loadmoduleid '.$event->modal_module.'}');
                                        }
									}
									break;
							}
						}
                        break;
                }
                
                $event->link_target = $event->link_target ? "_blank" : "_self";	

                // Array com os dias que ocorre o evento
                $event->days = array();

                // Evento recorrente do tipo especificado por data: cria um novo evento para cada data
                if((isset($event->is_recurrent)) && ($event->is_recurrent) && (($event->recurrence_type == 'specific-dates') || ($event->recurrence_type == 'specific-dates-hours'))){
                    
                    // Datas nao estao especificadas
                    if (empty($event->specific_dates)){
                        continue;
                    }

                    // Percorre cada data especificada e gera um novo evento para cada uma
                    foreach($event->specific_dates as $keyDateEspecific => $dateEventEspecific){
                        // Converte data de string para objeto Date
                        $dateEventEspecific = self::createJDateFromString(($dateEventEspecific), true);
                        
                        // Data corrente eh menor que data inicial de pesquisa ou mais que data final de pesquisa
                        if(($dateEventEspecific < $searchInicialData) || ($dateEventEspecific > $searchFinalDate)){
                            continue;
                        }

                        // Clona objeto para modificar ele
                        $newEvent = clone $event;
                        // Coloca o dia atual como dia do evento
                        $newEvent->initial_date = $dateEventEspecific->format('Y-m-d');
                        // Zera data final
                        $newEvent->final_date = $newEvent->initial_date;
                        // Remove informacao de que eh recorrente
                        $newEvent->is_recurrent = 0;
                        // Remove informacoes de dias a ignorar
                        $newEvent->recurrent_days_ignore = null;
                        // Adiciona o dia do evento no seu array de dias
                        $newEvent->days[] = (int)$dateEventEspecific->day;

                        /* Evento com datas e horas especificas: reseta o array de horas
                         * OBS: esse tipo de recorrencia utiliza os campos 'datas especificas' e 'horas' da tabela do banco onde
                         * as posicoes do array de cada campo desses corresponde a um conjunto de data e hora a exibir
                         */
                        if($event->recurrence_type == 'specific-dates-hours'){
                            $hour = new stdClass();

                            // Pega hora na mesma posicao da data percorrida
                            $hour->initial = $event->hours[$keyDateEspecific]->initial;
                            $hour->final = $event->hours[$keyDateEspecific]->final;

                            // Recria o array de horas deixando apenas a hora da data percorrida
                            $newEvent->hours = array();
                            $newEvent->hours[] = $hour;
                        }

                        // Adiciona como novo evento
                        $newEvents[] = $newEvent;
                        // Incrementa contador do proximo item a criar de evento
                        $indexNextEvent++;
                    }
                }

                // Evento ocorre em mais de um dia: percorre cada dia do evento para saber se eh um dia valido a exibir
                else if ($eventInitialDate != $eventFinalDate){
                    // Data inicial do evento eh menor que data inicial da pesquisa: seta data da pesquisa como data inicial a percorrer
                    if ($eventInitialDate < $searchInicialData){
                        $periodMonthInitial = clone $searchInicialData;
                    }else{
                        $periodMonthInitial = clone $eventInitialDate;
                    }
                    // Data final do evento eh maior que data final da pesquisa: seta data da pesquisa como data final a percorrer
                    if ($eventFinalDate > $searchFinalDate){
                        $periodMonthFinal = clone $searchFinalDate;
                    }else{
                        $periodMonthFinal = clone $eventFinalDate;
                    }
                    
                    // Percorre cada dia entre inicio e fim do periodo do evento no mes
                    while ($periodMonthInitial <= $periodMonthFinal){
                        $dateCurrent = clone $periodMonthInitial;
                        $dayWeekCurrent = $dateCurrent->format('w');                      

                        $event->recurrent_days = (array)$event->recurrent_days;

                        // ((Evento é recorrente) && (tipo de recorrencia eh por dia de semana) && (dia da semana atual nao esta marcado para o evento))
                        if ((isset($event->is_recurrent)) && ($event->is_recurrent) && ($event->recurrence_type == 'week-days') && (!empty($event->recurrent_days)) && ($event->recurrent_days[$dayWeekCurrent] === 0)){
                            // NAO ADICIONA DIA NO ARRAYS DE DIAS DO EVENTO
                        }
                        // Dia atual esta no array de 'recurrent_days_ignore'
                        else if(in_array($dateCurrent->format('Y-m-d'), (array) $event->recurrent_days_ignore)){
                            // NAO ADICIONA EVENTO PARA EXIBICAO
                        }
                        // Dia valido
                        else{
                            // Evento recorrente e modelo 2 (cards): forca que seja criado um evento novo para cada data
                            // TODO: se quisermos um dia deixar o evento recorrente de dia da semana ser duplicado para todos os modelos, basta remover a verificacao do campo theme abaixo que se aplicará a todos. A principal vantagem de mudar isso será o fato de que no modelo 1 marca ao lado do mes que tem somente um evento
                            if (((isset($event->is_recurrent)) && ($event->is_recurrent)) && ($theme == 'model2')){
                                // Clona objeto para modificar ele
                                $newEvent = clone $event;
                                // Coloca o dia atual como dia do evento
                                $newEvent->initial_date = $dateCurrent->format('Y-m-d');
                                // Zera data final
                                $newEvent->final_date = $newEvent->initial_date;
                                // Remove informacao de que eh recorrente
                                $newEvent->is_recurrent = 0;
                                // Remove informacoes de dias a ignorar
								$newEvent->recurrent_days_ignore = null;
                                // Adiciona o dia do evento no seu array de dias (nao eh usado no modelo de cards)
								$newEvent->days[] = (int)$dateCurrent->day;
                                // Adiciona como novo evento
                                $newEvents[] = $newEvent;
                                // Incrementa contador do proximo item a criar de evento
                                $indexNextEvent++;
                            }
                            // Mantem um evento unico para todas datas (nao eh usado no modelo de cards)
                            else{
                                $event->days[] = (int)$periodMonthInitial->day;
                            }                            
                        }

                        // Soma um dia
                        $periodMonthInitial->modify('+1 day');
                    }
                }
                // Evento de um dia: adiciona no array de dias do evento (nao eh usado no modelo de cards)
                else{
                    $event->days[] = (int)$eventInitialDate->day;
                }

                // Evento nao possui dias no mes atual: excluir do array
                if (empty($event->days)){
					continue;
                }
	
				// Caso as duas datas estejam vazias, remove o evento do array e segue o foreach para o proximo
				if(empty($event->initial_date) && empty($event->final_date)){
					continue;
                }
                $newEvents[] = $event;
            }
            
        }

		// Ordena eventos pelas suas datas.
        uasort($newEvents, array('ModNobosscalendarHelper', 'sortAscendingEvents'));

		// Retorna os eventos como objeto
		return (object) $newEvents;
	}

	/**
	 * Pega data com o primeiro dia do mês.
	 *
	 * @param JDate $currentData Data atual referência.
	 * @param string $navigation (opcional) 'next' para pega o valor do mês seguinte
	 * ou 'prev' para o valor do mês anterior.
	 *
	 * @return JDate.
	 */
	public static function getFirstDayMonth($currentDate, $navigation = 'current') {

		$month = $currentDate->month;
		$year = $currentDate->year;

		// Verifica navegação do mês.
		switch ($navigation) {
			// Se mês seguinte.
			case 'next':
				$month = $currentDate->month + 1;
				// Se o próximo mês excedeu o mês 12(dezembro).
				if ($month == 13) {
					// Vai para janeiro do próximo ano.
					$month = 1;
					$year = $currentDate->year + 1;
				}
				break;

			// Se mês anterior.
			case 'prev':
				$month = $currentDate->month - 1;
				// Se o próximo mês limitou o mês 1(janeiro).
				if ($month == 0) {
					// Volta para dezembro do próximo passado.
					$month = 12;
					$year = $currentDate->year - 1;
				}
				break;

			default:
				break;
		}
		// Cria a data de retorno.
		return JDate::getInstance("{$year}-{$month}-01");
	}

	/**
	 * Retorna o ano da data passada, e de acordo com 
	 * o segundo parametro retorna o ano seguinte ou anterior
	 *
	 * @param JDate $currentData Data atual referência.
	 * @param string $navigation (opcional) 'next' para pega o valor do ano seguinte
	 * ou 'prev' para o valor do ano anterior.
	 *
	 * @return JDate.
	 */
	public static function getFirstDayOfYear($currentDate, $navigation = 'current') {
		$year = $currentDate->year;

		// Verifica navegação do ano.
		switch ($navigation) {
			// Se ano seguinte.
			case 'next':
				$year = $currentDate->year + 1;
				break;

			// Se ano anterior.
			case 'prev':
				$year = $currentDate->year - 1;
				break;

			default:
				break;
		}
		// Cria a data de retorno.
		return JDate::getInstance("{$year}-01-01");
	}


	/**
	 * Monta os dados para o calendario, retornando os dias do mes com seus respectivos eventos
	 *
	 * @param   int         $idModule 					Id do módulo atual
     * @param   Sreing      $idCalendar 		  	    Id do calendario
	 * @param   JDate       $dateCalendarReference		Objeto jdate com uma data do mes atual para referencia
     * @param   boolean     $custom			Objeto com dados da modal de customizacao dos itens
	 * @param   boolean     $filter					    Filtro para os eventos
	 * @return  ArraY 	    Retorna os dias do mês com os eventos de cada um
	 */
	public static function getDataToCalendar($idModule, $idCalendar, JDate $dateCalendarReference, $custom, $filter = false) {
        jimport('noboss.util.modules');

		$eventsDays = array();
		// Pega dados do módulo.
		$dataModule = NoBossUtilModules::getDataModule($idModule);
		// Data inicial do calendário.
		$startDate = clone $dateCalendarReference;
		$startDate->modify('first day of this month');
		$startDate->modify('midnight');
		// Data final do calendário.
		$finalDate = clone $dateCalendarReference;
		$finalDate->modify('last day of this month');
		$finalDate->modify('midnight');

		// Pega os eventos.
        $events = self::getEvents($startDate, $finalDate, $idModule);

		// Caso exista ao menos um evento
		if(empty($events)){
			return false;
		}
		// Cria a chave de legendas
        $eventsDays = array("legends" => array());

        // Monta o array com as legendas que existem nos eventos deste mes
        $eventsDays['legends'] = array_values(array_filter(array_map(function($a){
            if(!empty($a->event_category->id) && $a->event_category->id != 'none')
            return $a->event_category;
        }, (array)$events)));

        // Setado para exibir todos os filtros de categorias, mesmo que nao tenha eventos vinculados
        if (!empty($custom->show_legends) && ($custom->show_legends == 2)){
            // Obtem todas legendas do calendario
            $categories = ModNobosscalendarHelper::getCategories($event->id_calendar);

            // Concatena com as legendas do mes apenas para pegar feriados, caso tenha (depois eh removidos duplicados)
            $eventsDays['legends'] = array_merge($categories, $eventsDays['legends']);
        }

        // Limpa legendas repetidas
        $eventsDays['legends'] = array_filter($eventsDays['legends'],function($obj){
            static $idList = array();
            if(in_array($obj->id, $idList)) {
                return false;
            }
            $idList[] = $obj->id;
            return true;
        });

		// Filtra os eventos
		if(!empty($filter)){
			$events = array_filter((array)$events, function($a) use ($filter){ return $a->event_category->id == $filter;});
		}
		
        // Pega dia de cada evento ou periodo importante.
		$eventsDays = array_merge($eventsDays, self::getEventsDays($events, $dateCalendarReference));

		return $eventsDays;
    }

    /**
     * Metodo que pega as parametrizacoes para modais de conteudo
     *
     * @return Object Objeto com todos os parametros da modal de customizacao
     */
    public static function getModalParams($idModule, $modalCustomization = false){
        jimport('noboss.util.modules');

        // Parametros da modal precisam ser buscados no banco (requisicao ajax)
        if (!$modalCustomization){
            // Obtem dados do modulo
            $dataModule = NoBossUtilModules::getDataModule($idModule);
            // Extrai parametros do modulo
            $params = $dataModule->params;
            $theme = json_decode($params->theme)->theme;
            // Obtem os parametros da modal de cusotomizacao
            $modalCustomization = json_decode($params->modal_customization);
        }

        // Parametros da modal nao definidos
        if (empty($modalCustomization)){
            return false;
        }

        // Titulo da modal
        $modalCustomization->titleStyle = "";
        $modalCustomization->titleStyle .= NoBossUtilFonts::importNobossfontlist($modalCustomization->modal_title_font);
        $modalCustomization->titleStyle .= " text-transform: {$modalCustomization->modal_title_transform};";
        $modalCustomization->titleStyle .= " color: {$modalCustomization->modal_title_color};";
        $modalTitleSize = $modalCustomization->modal_title_size;
        if(!empty($modalTitleSize)){
            $modalTitleSizeEm = $modalTitleSize/16;
            $modalCustomization->titleStyle .= " font-size: {$modalTitleSize}px; font-size: {$modalTitleSizeEm}em;";
        }
        
        // Titulo da modal para mobile
        $modalCustomization->titleStyleMobile = "";
        if(isset($modalCustomization->modal_title_size_mobile)){
            $modalTitleSizeMobile  = $modalCustomization->modal_title_size_mobile;
            $modalTitleSizeMobileEm = $modalTitleSizeMobile/16;
            $modalCustomization->titleStyleMobile = " font-size: {$modalTitleSizeMobile}px; font-size: {$modalTitleSizeMobileEm}em !important;";
        }

        // Categoria
        if (!isset($modalCustomization->show_category)) { $modalCustomization->show_category = 1; }
        $modalCustomization->categoryStyle = "";
        if (!empty($modalCustomization->modal_category_font)){ $modalCustomization->categoryStyle .= NoBossUtilFonts::importNobossfontlist($modalCustomization->modal_category_font); }
        if (!empty($modalCustomization->modal_category_transform)){ $modalCustomization->categoryStyle .= " text-transform: {$modalCustomization->modal_category_transform};"; }
        if (!empty($modalCustomization->modal_category_color)){ $modalCustomization->categoryStyle .= " color: {$modalCustomization->modal_category_color};"; }
        if (!empty($modalCustomization->modal_category_size)){ $modalCategorySize = $modalCustomization->modal_category_size; }
        
        if(!empty($modalCategorySize)){
            $modalCategorySizeEm = $modalCategorySize/16;
            $modalCustomization->categoryStyle .= " font-size: {$modalCategorySize}px; font-size: {$modalCategorySizeEm}em;";
        }
        
        // Categoria para mobile
        $modalCustomization->categoryStyleMobile = "";
        if(isset($modalCustomization->modal_category_size_mobile)){
            $modalCategorySizeMobile  = $modalCustomization->modal_category_size_mobile;
            $modalCategorySizeMobileEm = $modalCategorySizeMobile/16;
            $modalCustomization->categoryStyleMobile = " font-size: {$modalCategorySizeMobile}px; font-size: {$modalCategorySizeMobileEm}em !important;";
        }
        
        // Detalhes do conteudo
        if (!isset($modalCustomization->show_date)) { $modalCustomization->show_date = 1; }
        if (!isset($modalCustomization->show_hour)) { $modalCustomization->show_hour = 1; }
        if (!isset($modalCustomization->show_address)) { $modalCustomization->show_address = 1; }
        $modalCustomization->detailsStyle = "";
        $modalCustomization->detailsStyle .= NoBossUtilFonts::importNobossfontlist($modalCustomization->modal_details_font);
        $modalCustomization->detailsStyle .= " text-transform: {$modalCustomization->modal_details_transform};";
        $modalCustomization->detailsStyle .= " color: {$modalCustomization->modal_details_color};";
        $modalDetailsSize = $modalCustomization->modal_details_size;
        if(!empty($modalDetailsSize)){
            $modalCustomization->detailsStyle .= " font-size: {$modalDetailsSize}px;";
        }
        
        // Detalhes do conteudo para mobile
        $modalCustomization->detailsStyleMobile = "";
        if(isset($modalCustomization->modal_details_size_mobile)){
            $modalDetailsSizeMobile  = $modalCustomization->modal_details_size_mobile;
            $modalCustomization->detailsStyleMobile = " font-size: {$modalDetailsSizeMobile}px !important;";
        }

        if (empty($modalCustomization->show_display_background_color)) {
            $modalCustomization->show_display_background_color = 'rgba(255, 255, 255, 1)';
        }

        if (empty($modalCustomization->show_address)) {
            $modalCustomization->show_address = 1;
        }
    
        return $modalCustomization;
    }

	/**
	 * Cria um objeto JDate dado um formato e uma string.
     * 
	 * @param string $stringDate 	String com a data a ser criada.
	 *
	 * @return JDate Retorna objeto criado.
	 */
	public static function createJDateFromString($stringDate, $clearTime = false) {
        // Obtem configuracoes globais
        $config     = JFactory::getConfig();
        
        // Obtem offset das configuracoes globais
        // OBS: fixado timezone de sao paulo para evitar problemas na conversao como ja ocorreu com fuso da finlandia que tem 6hrs a menos que o brasil
        $dateOffSet = 'America/Sao_Paulo'; 
        //$config->get('offset', 'America/Sao_Paulo');

        // Obtem objeto da data e hora
        $auxDate = JFactory::getDate($stringDate, $dateOffSet);
        // Converte a data para o formato desejado
        $auxDate->format('Y-m-d');

        $jDate = JDate::getInstance($auxDate->format('Y-m-d'));
		if($clearTime){
			$jDate->modify('midnight');
        }

		return $jDate;
	}

	/**
	 * Busca e extrai o objeto com a parametrização de itens
	 *
	 * @param int $idModule 		Id do módulo
	 *
	 * @return stdClass 			Retorna objeto
	 */
	public static function getItemsCustomization($idModule){
        jimport('noboss.util.modules');

		// Pega os dados do módulo
		$module = NoBossUtilModules::getDataModule($idModule);
		$params = $module->params;
		$theme = json_decode($params->theme)->theme;
		
		// variável que armazena o objeto com os parâmetros da modal de itens do tema selecionado  
		$custom = json_decode($params->layout_calendar);

		return $custom;
    }

    /**
	 * Salva um evento no banco de dados
	 *
	 * @param   object      $event 		    Dados do evento a salvar
     * @param   String      $idCalendar 	Id do calendario
     * @param   Module      $idModule 		Id do modulo (opcional, se definido idCalendar)
	 *
	 * @return  boolean     True ou false
	 */
    public static function saveEvent($event, $idCalendar, $idModule = 0){
        if(empty($idCalendar) && empty($idModule)){
            return false;
        }
        
        $db = JFactory::getDBO();
        $query = $db->getQuery(true);

        $idEvent = $event->id_event;
        $columns = array();
        $values = array();

        $columns[] = 'id_calendar';
        $values[] = $db->quote("$idCalendar");

        // Titulo
        $columns[] = 'event_title';
        // Limpa quebra de pagina utilizando funcao 'chr' que identifica via ascii
        $event->event_title = str_replace(chr(012), "", $event->event_title);
        // Limpa '  ' (tab) utilizando funcao 'chr' que identifica via ascii
        $event->event_title = str_replace(chr(011), "", $event->event_title);
        // Coloca como json_encode e depois remove primeiro e ultimo caracter que serao aspas (gambi para tratar problemas de html) - necessario para funcionar caracteres especiais
        $event->event_title = substr(substr(json_encode($event->event_title), 1), 0, -1);

        $values[] = $db->quote("$event->event_title");

        // Flag se eh recorrente
        $columns[] = 'is_recurrent'; 
        $event->is_recurrent = (empty($event->is_recurrent)) ? 0 : $event->is_recurrent;
        $values[] = $db->quote("$event->is_recurrent");

        // Tipo de recorrencia
        if(!empty($event->recurrence_type)){
            $columns[] = 'recurrence_type';
            $values[] = $db->quote("$event->recurrence_type");      
        }
        
        // Dias da semana da recorrencia
        if (!empty($event->recurrent_days)){
            $columns[] = 'recurrent_days';
            // Converte valores para int (se estiver como string, nao funciona)
            $event->recurrent_days = array_map('intval', $event->recurrent_days);
            // Encoda em json
            $event->recurrent_days = addslashes(json_encode($event->recurrent_days));
            $values[] = $db->quote($event->recurrent_days);
        }
             
        // Datas especificas da recorrencia
        $columns[] = 'specific_dates';
        if (!empty($event->specific_dates)){
            // Ordena array pelas datas
            usort($event->specific_dates, function($d1, $d2){
                $t1 = strtotime($d1);
                $t2 = strtotime($d2);
                if ($t1 === $t2) return 0;
                return ($t1 < $t2) ? -1 : 1;
            });

            // Confirma que eh um evento de datas espeficicas
            if($event->recurrence_type == 'specific-dates'){
                // Obtem ultima data do array 
                $lastPosition = count($event->specific_dates)-1;
                $lastDate = $event->specific_dates[$lastPosition];
                
                // Coloca a ultima data como data fim do evento para sabermos qnd encerra
                $event->final_date = str_replace('/', '-', $lastDate);
            }

            // Encoda em json as datas definidas
            $event->specific_dates = addslashes(json_encode($event->specific_dates));
        }
        else{
            $event->specific_dates = '';
        }
        $values[] = $db->quote($event->specific_dates);

        // Datas a ignorar do evento
        $columns[] = 'recurrent_days_ignore';
        if (!empty($event->recurrent_days_ignore)){
            // Encoda em json as datas a ignorar
            $event->recurrent_days_ignore = addslashes(json_encode($event->recurrent_days_ignore));
        }
        else{
            $event->recurrent_days_ignore = '';
        }   
        $values[] = $db->quote($event->recurrent_days_ignore);

        // Data inicial
        // FIXME: qnd eh evento recorrente de datas especificas cai aqui por nao ter data. Assim passa a salvar como 'null'. se der problema precisaremos mudar para salvar como '0000-00-00'
        if(!empty($event->initial_date)){
            $columns[] = 'initial_date';
            $values[] = $db->quote("$event->initial_date");
        }

        // Data final
        if(!empty($event->final_date)){
            $columns[] = 'final_date';
            $values[] = $db->quote("$event->final_date");
        }

        // Horarios do evento
        if(!empty($event->hours)){
            $columns[] = 'hours';
            // Array de horas por algum motivo esta sem escape: adiciona escape
            if(strpos($event->hours, '\\') === false){
                $event->hours = addslashes($event->hours);
            }
            $values[] = $db->quote("$event->hours");
        }

        // Tipo de cadastro (manual, googlecalendar)
        $columns[] = 'input_type';
        $event->input_type = (empty($event->input_type)) ? 'manual' : $event->input_type;
        $values[] = $db->quote("$event->input_type");

        // Status
        if(($event->published == 1) || ($event->published == 0)){
            $columns[] = 'published';
            $values[] = $db->quote("$event->published");
        }

        // Categoria vinculada ao evento
        $event->event_category = (empty($event->event_category)) ? 'none' : $event->event_category;
        $columns[] = 'event_category';
        $values[] = $db->quote("$event->event_category");

        // Subtitulo
        // TODO: esse campo ainda nao possui coluna propria no banco
        $event->event_subtitle = (empty($event->event_subtitle)) ? '' : $event->event_subtitle;
        // Limpa quebra de pagina utilizando funcao 'chr' que identifica via ascii
        //$event->event_subtitle = str_replace(chr(012), "", $event->event_subtitle);
        // Limpa '  ' (tab) utilizando funcao 'chr' que identifica via ascii
        $event->event_subtitle = str_replace(chr(011), "", $event->event_subtitle);

        // Conteudo para modal
        if(!empty($event->modal_content)){
            $columns[] = 'modal_content';
            // Substitui aspas duplas por simples no conteudo (aspas duplas faz com que json quebre na hora de carregar os dados)
            $event->modal_content = str_replace("\"", "'", $event->modal_content);
            // Limpa quebra de pagina utilizando funcao 'chr' que identifica via ascii
            $event->modal_content = str_replace(chr(012), "", $event->modal_content);
            // Limpa '  ' (tab) utilizando funcao 'chr' que identifica via ascii
            $event->modal_content = str_replace(chr(011), "", $event->modal_content);
            // Coloca como json_encode e depois remove primeiro e ultimo caracter que serao aspas (gambi para tratar problemas de html) - necessario para funcionar caracteres especiais
            $event->modal_content = substr(substr(json_encode($event->modal_content), 1), 0, -1);

            $values[] = $db->quote("$event->modal_content");
        }

        // echo '<pre>';
        // var_dump($event);
        // exit;

        // Local do evento
        // TODO: esse campo ainda nao possui coluna propria no banco
        $event->event_place = (empty($event->event_place)) ? '' : $event->event_place;
        // Limpa quebra de pagina utilizando funcao 'chr' que identifica via ascii
        //$event->event_place = str_replace(chr(012), "", $event->event_place);
        // Limpa '  ' (tab) utilizando funcao 'chr' que identifica via ascii
        $event->event_place = str_replace(chr(011), "", $event->event_place);

        // Link para local do evento
        // TODO: esse campo ainda nao possui coluna propria no banco
        $event->event_place_link = (empty($event->event_place_link)) ? 'none' : $event->event_place_link;

        // Url do local do evento
        // TODO: esse campo ainda nao possui coluna propria no banco
        $event->event_place_link_href = (!empty($event->event_place_link_href) && (!empty($event->event_place_link)) && ($event->event_place_link == 'manual')) ? $event->event_place_link_href : '';

        // Cor de identificacao do evento (qnd nao tem categoria vinculada)
        // TODO: esse campo ainda nao possui coluna propria no banco
        if(!empty($event->event_manual_color)){
            $event->event_manual_color = $event->event_manual_color;
        }
        
        // Cor especifica para o texto do evento
        // TODO: esse campo ainda nao possui coluna propria no banco
        $event->event_text_color = (empty($event->event_text_color)) ? '' : $event->event_text_color;

        // Flag que diz se deve ser exibido um evento para cada horario (somente qnd nao eh recorrente)
        // TODO: esse campo ainda nao possui coluna propria no banco
        $event->one_event_schedule = (empty($event->one_event_schedule)) ? 0 : $event->one_event_schedule;

        // Tipo de link no evento
        // TODO: esse campo ainda nao possui coluna propria no banco
        $event->link_option = (empty($event->link_option)) ? 'none' : $event->link_option;

        // Tipo de modal de conteudo
        // TODO: esse campo ainda nao possui coluna propria no banco
        if(!empty($event->modal_option)){
            $event->modal_option = $event->modal_option;
        }

        // Id do artigo vinculado a modal de conteudo
        // TODO: esse campo ainda nao possui coluna propria no banco
        if(!empty($event->modal_article)){
            $event->modal_article = $event->modal_article;
        }

        // TODO: Remove valores que serao inseridos em colunas proprias (demais campos ainda sao encodados em uma unica coluna)
        unset($event->id_event);
        unset($event->event_title);
        unset($event->is_recurrent);
        unset($event->recurrence_type);
        unset($event->recurrent_days);
        unset($event->specific_dates);
        unset($event->recurrent_days_ignore);
        unset($event->initial_date);
        unset($event->final_date);
        unset($event->hours);
        unset($event->published);
        unset($event->event_category);
        unset($event->modal_content);
        unset($event->input_type);
        
        // Salva os dados restantes do evento em formato json em coluna propria
        // TODO: a ideia eh passarmos a ter todos campos em coluna propria, eliminando esse data_json
        $columns[] = 'data_json';
        // TODO: avaliar utilizar a funcao json_last_error ou json_last_error_msg apos o json_encode para verificar eventuais erros (https://www.php.net/manual/pt_BR/function.json-last-error.php)
        $values[] = $db->quote(json_encode($event));

        // Id do evento nao esta definido: eh novo evento
        if (empty($idEvent) || ($idEvent == 0)){
            // Realiza insert no banco
            $query->insert($db->quoteName('#__noboss_calendar'))
            ->columns($db->quoteName($columns))
            ->values(implode(',', $values));

            //echo str_replace('#__', 'ext_', $query); exit;
        }
        // Id do modulo definido: edicao de modulo
        else{
            $fields = array();
            // colunas a serem usadas na query de update
            foreach ($columns as $key => $column) {
                $fields[] = $db->quoteName($column) . ' = ' . $values[$key];
            }
            // condicoes a serem usadas no where da query
            $conditions = array(
                $db->quoteName('id_calendar') . ' = ' . $db->quote($idCalendar),
                $db->quoteName('id_event') . ' = ' . (int) $idEvent
            );
            // Realiza update no banco
            $query->update($db->quoteName('#__noboss_calendar'))->set($fields)->where($conditions);
        }
        
        //echo str_replace('#__', 'ext_', $query); exit;

        // Seta query
        $db->setQuery($query);
            
        return $db->execute();
    }
    
    /**
	 * Requisicao para salvar evento a partir da area admin
	 *
	 * @return  void
	 */
    public static function saveEventsAjax(){
        header('Access-Control-Allow-Origin: *');
        error_reporting(0);

        // Carrega arquivo de traducao da extensao
        $lang = JFactory::getLanguage();
        $lang->load('mod_nobosscalendar', JPATH_SITE.'/modules/mod_nobosscalendar');

		$app = JFactory::getApplication();
        $input = $app->input;

		// Obtem parametros da requisicao
        $idModule = $input->get("id_module");
        $idCalendar = $input->get("id_calendar");
        $events = $input->get("data_json", '', 'RAW');
        $eventsToRemove = $input->get("events_to_remove");
        
        $response = array();
		
        // Id do calendario nao definido: retorna mensagem de erro
        if (empty($idCalendar)){
			$response['success'] = 0;
            $response['message'] = JText::_("MOD_NOBOSSCALENDAR_ID_CALENDAR_NOT_FOUND");
            exit(json_encode($response));
        }

        if (count($events) > 0){
            // Percorre todos os eventos para salvar
            foreach ($events as $event){
                $event = (object)$event;

                try{
                    // Executa funcao para salvar evento
                    self::saveEvent($event, $idCalendar, $idModule);
                }catch(Exception $e){
                    // seta flag de sem sucesso em caso de erro
                    $response['success'] = 0;
                    $response['message'] = JText::sprintf('MOD_NOBOSSCALENDAR_SAVE_EVENT_DB_ERROR', $event->event_title).' <br /><br /><b>Details:</b> <br />'.$e->getMessage();
                    exit(json_encode($response));
                }
            }
        }
        // Ha eventos para remover
        if (!empty($eventsToRemove)){
            try{
                self::removeEventsFromSpecifiedIds($eventsToRemove);
            }catch(Exception $e){
                // seta flag de sem sucesso em caso de erro
                $response['success'] = 0;
                $response['message'] = JText::sprintf('MOD_NOBOSSCALENDAR_REMOVE_EVENTS_DB_ERROR', implode($eventsToRemove, ',')).' <br /><br /><b>Details:</b> <br />'.$e->getMessage();
                exit(json_encode($response));
            }
            
        }

        // Executa funcao de limpeza que remove eventos que nao possuem mais modulos
        //self::removeEventsWithoutModule();

        if (!isset($response['success']) || $response['success'] == false){
            $response['success'] = 1;
        }

        // Retorna a resposta em json
        exit(json_encode($response));
    }

    /**
	 * Obtem os eventos para edicao na area admin
	 *
	 * @return  void
	 */
    public static function getEventsAdminAjax(){
        header('Access-Control-Allow-Origin: *');

        // Carrega arquivo de traducao da extensao
        $lang = JFactory::getLanguage();
        $lang->load('mod_nobosscalendar', JPATH_SITE.'/modules/mod_nobosscalendar');

        error_reporting(0);

        $app = JFactory::getApplication();
        $input = $app->input;

		// Obtem parametros da requisicao
        $idCalendar = $input->get("id_calendar", "", "STRING");
        $idCalendarOriginal = $input->get("id_calendar_original", "", "STRING");
        $year = $input->get("year", '');
        $month = $input->get("month", '');

        try{
            $response['events'] = self::getEventsDataBase($idCalendar, 0, 0, $year, $month, array('manual'), $idCalendarOriginal);
            $response['success'] = 1;
            // Seta a mensagem para caso ocorra erro no JSON.parse realizado no JS apos o retorno desta funcao
            $response['error_json'] = JText::_('MOD_NOBOSSCALENDAR_GET_EVENT_JSON_PARSE_ERROR');
            // Seta a mensagem para caso ocorra algum outro erro no JS apos o retorno desta funcao
            $response['error_js'] = JText::_('MOD_NOBOSSCALENDAR_GET_EVENT_JAVASCRIPT_ERROR');
        }
        catch(Exception $e){
            // seta flag de sem sucesso em caso de erro
            $response['success'] = 0;
            $response['error_database'] = $e->getMessage();
        }
        
        // Retorna a resposta em json
        exit(json_encode($response));
    }

   /**
	 * Atualiza id calendar de eventos e categorias existentes na base (usuario alterou o id_calendar na area admin)
	 *
	 * @return  void
	 */
    public static function updateIdCalendarAjax(){
        header('Access-Control-Allow-Origin: *');
        error_reporting(0);

		$app = JFactory::getApplication();
        $input = $app->input;

		// Obtem parametros da requisicao
        $idCalendar = $input->get("id_calendar", "", "STRING");
        $idCalendarOriginal = $input->get("id_calendar_original", "", "STRING");

        $db = JFactory::getDBO();

        try{
            // Atualiza os eventos
            $query = $db->getQuery(true);
            $query->update($db->quoteName('#__noboss_calendar'))
                ->set($db->quoteName('id_calendar') . ' = ' . $db->quote($idCalendar))
                ->where($db->quoteName('id_calendar') . ' = ' . $db->quote($idCalendarOriginal));
            $db->setQuery($query);
            $db->execute();

            // Atualiza as categorias
            $query = $db->getQuery(true);
            $query->update($db->quoteName('#__noboss_calendar_categories'))
                ->set($db->quoteName('id_calendar') . ' = ' . $db->quote($idCalendar))
                ->where($db->quoteName('id_calendar') . ' = ' . $db->quote($idCalendarOriginal));
            $db->setQuery($query);
            $db->execute();

            $response['success'] = 1;
        }
        catch(Exception $e){
            // seta flag de sem sucesso em caso de erro
            $response['success'] = 0;
            $response['error'] = $e->getMessage();
        }
        
        // Retorna a resposta em json
        exit(json_encode($response));
    }

    /**
	 * Obtem os eventos do banco de dados
     * 
     * @param   String    $idCalendar 	        Id do calendario (opcional, se definido o id do modulo)
     * @param   Int       $idModule 	        Id do modulo (opcional, se definido o id do calendario)
	 * @param   int       $onlyPublished 		Somente eventos publicados
     * @param   int       $year 		        Somente eventos de determinado ano
     * @param   int       $month 		        Somente eventos de determinado mes
     * @param   Array     $inputTypes 		    Armazena os tipos de inputs a retornar
     * @param   String    $idCalendarOriginal   Id do calendario origianl (antes do usuario poder ter alterado)
	 *
	 * @return  void
	 */
    public static function getEventsDataBase($idCalendar, $idModule, $onlyPublished = 0, $year = '', $month = '', $inputTypes = array(), $idCalendarOriginal = 0){
        $dateOffSet = 'America/Sao_Paulo'; 

        if(!$idCalendarOriginal){
            $idCalendarOriginal = $idCalendar;
        }

        $db = JFactory::getDBO();
        $query = $db->getQuery(true);

        // Realiza consulta no banco juntando colunas proprias com a coluna 'data_json' ficando tudo em um unico json
        /* 
         * TODO: a limpeza dos caracteres que dao problemas no json sao feitas na consulta sql abaixo e na funcao PHP que salva os dados
         *      - A limpeza esta sendo feita usando codigo ascii.
         *      - A tabela ascii pode ser acessada em http://www.asciitable.com/
         *          - Para consulta SQL utilizar o codigo da primeira coluna da tabela que eh 'Dec' utilizando a funcao CHAR do mysql
         *          - Para o PHP utilizar o codigo da coluna 'Oct' na funcao php chr
         */

         /*
            Campos que ainda nao possuem colunas proprias:
                specific-dates-hours
                one_event_schedule
                event_subtitle
                event_place
                event_place_link
                event_place_link_href
                link_option
                modal_option
                link_internal
                link_external
                link_title
                link_target
                modal_article
                modal_module
                event_manual_color
                event_text_color
         */

         // Os dados sao todos agrupados em um unico json de retorno
        $query
        ->select("concat(\"{\", 
                    '\"id_event\":\"',id_event,'\",',
                    
                    /* Obtem o titulo convertendo aspas duplas para simples e removendo tab e quebra de linha */
                    '\"event_title\":\"', REPLACE(REPLACE(REPLACE(REPLACE(event_title, '\"', \"'\"),CHAR(10),''),CHAR(13),''),CHAR(9),''),'\",',
                    
                    '\"is_recurrent\":\"',is_recurrent,'\",',
                    
                    '\"recurrence_type\":\"',CASE WHEN (recurrence_type IS NULL) THEN '' ELSE TRIM(recurrence_type) END,'\",',
                    
                    '\"recurrent_days\":\"',CASE WHEN (recurrent_days IS NULL) THEN '' ELSE TRIM(recurrent_days) END,'\",',
                    
                    '\"specific_dates\":\"',CASE WHEN (specific_dates IS NULL) THEN '' ELSE TRIM(specific_dates) END,'\",',
                    
                    '\"recurrent_days_ignore\":\"',CASE WHEN (recurrent_days_ignore IS NULL) THEN '' ELSE TRIM(recurrent_days_ignore) END,'\",',

                    '\"initial_date\":\"',CASE WHEN ((initial_date = '0000-00-00') OR (initial_date IS NULL)) THEN '' ELSE initial_date END,'\",',

                    '\"final_date\":\"',CASE WHEN ((final_date = '0000-00-00') OR (final_date IS NULL)) THEN '' ELSE final_date END,'\",',

                    '\"hours\":\"',CASE WHEN (hours IS NULL) THEN '' ELSE TRIM(hours) END,'\",',

                    '\"event_category\":\"',CASE WHEN (event_category IS NULL) THEN '' ELSE event_category END,'\",',

                    '\"published\":\"',published,'\",',
                    
                    /* Obtem o conteudo da modal convertendo aspas duplas para simples e removendo tab e quebra de linha */
                    '\"modal_content\":\"',CASE WHEN (modal_content IS NULL) THEN '' ELSE TRIM(REPLACE(REPLACE(REPLACE(REPLACE(modal_content, '\"', \"'\"),CHAR(10),''),CHAR(13),''),CHAR(9),'')) END,'\",',

                    /* Obtem demais dados do 'data_json' removendo tab, quebra de linha e '}' do final do json deste campo */
                    substring(TRIM(REPLACE(REPLACE(REPLACE(data_json,CHAR(10),''),CHAR(13),''),CHAR(9),'')), 2, length(data_json) - 2),'}') as data_json
                    ")
        ->from('#__noboss_calendar');

        if(!empty($idCalendar)){
            $query->where('(id_calendar =' . $db->quote($idCalendar) . ' OR id_calendar =' . $db->quote($idCalendarOriginal) . ')');
        }
        else{
            return false;
        }

        if (!empty($inputTypes)){
            $query->where("input_type IN ('".implode("','", $inputTypes)."')");
        }

        if($onlyPublished){
            $query->where('published = "1"');
        }

        // Definido filtro de ano
        // TODO: seria bom usar esse filtro e de mes nas consultas do front para ficar menos pesado
        if(!empty($year)){
            // Filtro de mes nao definido: define o ano inteiro como periodo de pesquisa
            if(empty($month)){
                $filterStart = JFactory::getDate("{$year}-01-01", $dateOffSet)->format('Y-m-d');
                $filterEnd = JFactory::getDate("{$year}-12-01", $dateOffSet)->modify('last day of this month')->format('Y-m-d');
            }
            // Filtro de mes definido
            else{
                $filterStart = JFactory::getDate("{$year}-{$month}-01", $dateOffSet)->format('Y-m-d');
                $filterEnd = JFactory::getDate("{$year}-{$month}-01", $dateOffSet)->modify('last day of this month')->format('Y-m-d');
            }

            $whereDate = "(
                /* Evento nao recorrente */
                ((is_recurrent = '0') AND (
                    /* Nao tem data final E data inicial esta dentro do periodo do filtro */
                    (((final_date = '0000-00-00') OR (final_date IS NULL)) AND (initial_date <= '{$filterEnd}') AND (initial_date >= '{$filterStart}'))
                    OR
                    /* Tem data final E filtro esta entre data inicial e final do evento */
                    (((final_date <> '0000-00-00') AND (final_date IS NOT NULL)) AND (initial_date <= '{$filterEnd}') AND (final_date >= '{$filterStart}'))
                ))
                OR
                /* Evento recorrente */
                ((is_recurrent = '1') AND (
                    /* Recorrencia do dia de semana */
                    ((recurrence_type = 'week-days') AND (
                        /* Nao tem data final E data inicial eh igual ou maior que periodo pesquisado */
                        (((final_date = '0000-00-00') OR (final_date IS NULL)) AND (initial_date <= '{$filterEnd}'))
                        OR
                        /* Tem data final E filtro esta entre data inicial e final do evento */
                        (((final_date <> '0000-00-00') AND (final_date IS NOT NULL)) AND (initial_date <= '{$filterEnd}') AND (final_date >= '{$filterStart}'))
                    ))
                    OR
                    /* Recorrencia por datas especificas */
                    (((recurrence_type = 'specific-dates') OR (recurrence_type = 'specific-dates-hours')) AND (
                        ";
                        // Informado mes no filtro E ano/mes existem em alguma das datas especificas
                        if(!empty($month)){
                            $whereDate .= "(specific_dates LIKE '%{$year}-{$month}%')";
                        }
                        // Nao informado mes E ano existe em alguma das datas especificas
                        else{
                            $whereDate .= "(specific_dates LIKE '%{$year}-%')";
                        }
                    $whereDate .= "    
                    ))
                )))";
            
            $query->where($whereDate);
        }

        // Seta query
		$db->setQuery($query);
		
		//echo str_replace('#__', 'ext_', $query); exit;

        try{
            $events = $db->loadColumn();
        }
        // Ocorreu um erro na consulta
        catch(Exception $e){
            // Verifica se tabela existe no banco
            $query2 = $db->getQuery(true);
            $query2 = "SHOW TABLES LIKE '%_noboss_calendar'";
            $db->setQuery($query2);

            // Retorna erro que tabela nao existe
            if(empty($db->loadResult())){
                throw new Exception(JText::_('MOD_NOBOSSCALENDAR_GET_EVENT_DB_ERROR_NOT_FOUND_DATABASE'));
            }
            // Retorna erro da consulta
            else{
                // Obtem configuracoes globais
                $config     = JFactory::getConfig();
                throw new Exception(JText::sprintf('MOD_NOBOSSCALENDAR_GET_EVENT_DB_ERROR', $e->getMessage(),str_replace('#__', $config->get('dbprefix'), $query)));
            }

        }
        
        if (empty($events)){
            return false;
        }

        return $events;
    }

    /**
	 * Remove eventos que nao possuem mais vinculos com modulos (casos de modulos que sao excluidos)
	 *
	 * @return  Boolean     True ou false
	 */
    /*
    FIXME: refatorar! Hj remove eventos que contenham id de modulos nao mais existentes, mas devemos passar a excluir apenas eventos que tenham id_calendar que nao seja mais usado em nenhum modulo de calendario
            - tb mudar o local onde eh chamada a funcao (hj sempre que eventos sao salvos na area admin) p/ nao prejudicar performance da area admin
    */
    // public static function removeEventsWithoutModule(){
    //     $db = JFactory::getDBO();
    //     $query = $db->getQuery(true);

    //     $query
    //         ->delete('#__noboss_calendar')
    //         ->where("id_module IS NOT NULL
    //                     AND NOT EXISTS (
    //                         SELECT * FROM #__modules
    //                         WHERE id = id_module
    //                             AND module = 'mod_nobosscalendar')");

    //     $db->setQuery($query);

    //     // Executa query
    //     $db->execute();
    // }

    /**
	 * Remove eventos mais antigos que a data especificada
	 *
     * @param   REQUEST      $dateLimit   Data limite em que eventos podem ser removidos
     * @param   REQUEST      $idModule    Id do module que eventos devem ser removidos (opcional)
     * 
     * @note    Exemplo de requisicao: http://localhost/extensions/pt/index.php?option=com_ajax&module=nobosscalendar&format=raw&method=removeOldEvents&dateLimit=2020-02-30&idModule=711
     * 
	 * @return  string      'success' ou 'error'
	 */
    public static function removeOldEventsAjax(){
        header('Access-Control-Allow-Origin: *');
        
		$app = JFactory::getApplication();
		$input = $app->input;

        // Pega a data limite
        $dateLimit = $input->get("dateLimit", false);

		// Id do calendario
        $idCalendarOriginal = $input->get("id_calendar_original", "", "STRING");

        if(empty($idCalendarOriginal)){
            exit('Id calendar not defined');
        }
 
        $db = JFactory::getDBO();
        $query = $db->getQuery(true);

        $query
            ->delete('#__noboss_calendar')
            ->where("(
                        # Evento nao eh recorrente e data final esta em branco e data inicial eh menor que data limite
                        ((is_recurrent = '0') AND ((final_date = '0000-00-00') OR (final_date IS NULL)) AND (initial_date < '$dateLimit')) OR 
                    
                        # Evento nao eh recorrente e data final NAO esta em branco e data final eh menor que data limite
                        ((is_recurrent = '0') AND((final_date <> '0000-00-00') AND (final_date IS NOT NULL)) AND (final_date < '$dateLimit')) OR 
                    
                        # Evento eh recorrente e data final nao esta em branco e data final eh menor que data limite
                        ((is_recurrent = '1') AND((final_date <> '0000-00-00') AND (final_date IS NOT NULL)) AND (final_date < '$dateLimit'))
                    )");

        $query->where('id_calendar =' . $db->quote($idCalendarOriginal));

        //echo str_replace('#__', 'ext_', $query); exit;

        try {
            $db->setQuery($query);
            $db->execute();
            exit('success');
        } catch (Exception $e) {
            exit('error');
        }
    }

    /**
	 * Remove eventos de id's especificados
     * 
     * @param   Array      $idsEvents 		Ids dos eventos a remover
	 *
	 * @return  Boolean     True ou false
	 */
    public static function removeEventsFromSpecifiedIds($idsEvents){
        $db = JFactory::getDBO();
        $query = $db->getQuery(true);

        $idsEvents = "'".implode("','", $idsEvents)."'";

        $query
            ->delete('#__noboss_calendar')
            ->where("id_event IN ($idsEvents)");

        $db->setQuery($query);

        // Executa query
        $db->execute();
    }

    /**
	 * Metodo que obtem o mes como numero (ex: '01') e retona como string (ex: 'Janeiro')
     * Usavamos a funcao monthToString, mas havia casos que estava trazendo em ingles, sem ser o idioma acessado
	 *
	 * @return string.
	 */
	public static function getMonthString($day) {
        switch ($day) {
            case '01':
                return ucfirst(JText::_('JANUARY'));
                break;
            case '02':
                return ucfirst(JText::_('FEBRUARY'));
                break;
            case '03':
                return ucfirst(JText::_('MARCH'));
                break;
            case '04':
                return ucfirst(JText::_('APRIL'));
                break;
            case '05':
                return ucfirst(JText::_('MAY'));
                break;
            case '06':
                return ucfirst(JText::_('JUNE'));
                break;
            case '07':
                return ucfirst(JText::_('JULY'));
                break;
            case '08':
                return ucfirst(JText::_('AUGUST'));
                break;
            case '09':
                return ucfirst(JText::_('SEPTEMBER'));
                break;
            case '10':
                return ucfirst(JText::_('OCTOBER'));
                break;
            case '11':
                return ucfirst(JText::_('NOVEMBER'));
                break;
            case '12':
                return ucfirst(JText::_('DECEMBER'));
                break;
        }
	}

    /**
	 * Metodo que adiciona um link do google maps para uma string de local informada
	 *
     * Documentacao do Google para links: https://developers.google.com/maps/documentation/urls/get-started?hl=pt-br
     * 
	 * @return string.
	 */
    public static function addGoogleMapLink($local){       
        // Remove acentos
        $local = preg_replace(array("/(á|à|ã|â|ä)/","/(Á|À|Ã|Â|Ä)/","/(é|è|ê|ë)/","/(É|È|Ê|Ë)/","/(í|ì|î|ï)/","/(Í|Ì|Î|Ï)/","/(ó|ò|õ|ô|ö)/","/(Ó|Ò|Õ|Ô|Ö)/","/(ú|ù|û|ü)/","/(Ú|Ù|Û|Ü)/","/(ñ)/","/(Ñ)/"),explode(" ","a A e E i I o O u U n N"), $local);

        // Monta link de pesquisa do google
        $hrefGoogleMaps = "https://www.google.com/maps/search/?api=1&query=".urlencode($local);


        return $hrefGoogleMaps;
    }
}
