<?php

//
// Open Web Analytics - An Open Source Web Analytics Framework
//
// Copyright 2006 Peter Adams. All rights reserved.
//
// Licensed under GPL v2.0 http://www.gnu.org/copyleft/gpl.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// $Id$
//

require_once(OWA_BASE_DIR.'/owa_lib.php');

/**
 * OWA Core API
 * 
 * @author      Peter Adams <peter@openwebanalytics.com>
 * @copyright   Copyright &copy; 2006 Peter Adams <peter@openwebanalytics.com>
 * @license     http://www.gnu.org/copyleft/gpl.html GPL v2.0
 * @category    owa
 * @package     owa
 * @version        $Revision$
 * @since        owa 1.0.0
 */

class owa_coreAPI {


    // @depricated
    // @todo remove
    public static function singleton($params = array()) {

        static $api;

        if(!isset($api)):
            $api = new owa_coreAPI();
        endif;

        if(!empty($params)):
            $api->params = $params;
        endif;

        return $api;
    }

    public static function setupStorageEngine($type) {

        if ( ! class_exists( 'owa_db' ) ) {
	        
            require_once(OWA_BASE_CLASSES_DIR.'owa_db.php');
        }
		
		if ( $type ) {
        	$connection_class = "owa_db_" . $type;

            if ( ! class_exists( $connection_class ) ) {
	            
                $connection_class_path = OWA_PLUGIN_DIR.'db/' . $connection_class . ".php";
				
				if ( file_exists( $connection_class_path ) ) {
					
	                 if ( ! require_once( $connection_class_path ) ) {
	                     
	                     owa_coreAPI::error(sprintf('Cannot locate proper db class at %s.', $connection_class_path));
	                     
	                     return false;
	                }
	                
				} else {
					
					owa_coreAPI::error("$type database connection class file not found.");
				}
			}

        } else {
	        
	        owa_coreAPI::error("$type is not a supported database.");
        }

         return true;

    }
    /**
     * @return owa_db
     */
    public static function dbSingleton() {

        static $db;

        if (!isset($db)) {

            $db = owa_coreAPI::dbFactory();
        }

        return $db;
    }

    public static function dbFactory() {

        $db_type = owa_coreAPI::getSetting('base', 'db_type');
        $ret = owa_coreAPI::setupStorageEngine($db_type);

         if (!$ret) {
             owa_coreAPI::error(sprintf('Failed to initialize db type %s. Exiting.', $db_type));
             return;
        } else {
            $connection_class = 'owa_db_'.$db_type;
            $db = new $connection_class(
                owa_coreAPI::getSetting('base','db_host'),
                owa_coreAPI::getSetting('base','db_port'),
                owa_coreAPI::getSetting('base','db_name'),
                owa_coreAPI::getSetting('base','db_user'),
                owa_coreAPI::getSetting('base','db_password'),
                owa_coreAPI::getSetting('base','db_force_new_connections'),
                owa_coreAPI::getSetting('base','db_make_persistant_connections')
            );

            return $db;
        }
    }

    /**
     * @return owa_settings
     */
    public static function configSingleton() {

        static $config;

        if( ! isset( $config ) ) {

            if ( ! class_exists( 'owa_settings' ) ) {
                require_once( OWA_BASE_CLASS_DIR.'settings.php' );
            }

            $config = owa_coreAPI::supportClassFactory( 'base', 'settings' );
        }

        return $config;
    }

    public static function errorSingleton() {

        static $e;

        if( ! $e ) {

            if ( ! class_exists( 'owa_error' ) ) {
                require_once( OWA_BASE_CLASS_DIR.'error.php' );
            }

            $e = owa_coreAPI::supportClassFactory( 'base', 'error' );

        }

        return $e;
    }

    public static function getSetting($module, $name) {

        $s = owa_coreAPI::configSingleton();
        return $s->get($module, $name);
    }

    public static function setSetting($module, $name, $value, $persist = false) {

        $s = owa_coreAPI::configSingleton();

        if ($persist === true) {
            $s->persistSetting($module, $name, $value);
        } else {
            $s->setSetting($module, $name, $value);
        }

    }

    public static function persistSetting($module, $name, $value) {

        $s = owa_coreAPI::configSingleton();
        $s->persistSetting($module, $name, $value);

    }

    public static function getSiteSetting($site_id, $name) {

        $site = owa_coreAPI::entityFactory('base.site');
        $site->load( $site->generateId( $site_id ) );

        if ( $site->wasPersisted() ) {

            return $site->getSiteSetting($name);
        }
    }

    public static function getRegisteredDomain( $full_domain ) {

        static $psl;

        if ( ! $psl ) {
            $psl = owa_coreAPI::supportClassFactory( 'base', 'pslReader' );
        }

        return $psl->getRegisteredDomain( $full_domain );
    }

    public static function persistSiteSetting($site_id, $name, $value) {

        $site = owa_coreAPI::entityFactory('base.site');
        $site->load( $site->generateId( $site_id ) );
        if ( $site->wasPersisted() ) {
            $settings = $site->get('settings');
            if ( ! $settings ) {
                $settings = array();
            }
            $settings[$name] = $value;
            $site->set('settings', $settings);
            $site->update();
        }
    }

    public static function getSiteSettings($site_id) {

        $site = owa_coreAPI::entityFactory('base.site');
        $site->load( $site->generateId( $site_id ) );
        if ( $site->wasPersisted() ) {

            $settings = $site->get('settings');

            if ( $settings ) {
                return $settings;
            } else {
                return array();
            }
        }

    }

    public static function getAllRoles() {

        $caps = owa_coreAPI::getSetting('base', 'capabilities');
        return array_keys($caps);
    }

    public static function getCapabilities($role) {
        $caps = owa_coreAPI::getSetting('base', 'capabilities');
        if (array_key_exists($role, $caps)) {
            return $caps[$role];
        } else {
            return array();
        }
    }

    /**
     * @return owa_serviceUser
     */
    public static function getCurrentUser() {
        $s = owa_coreAPI::serviceSingleton();
        return $s->getCurrentUser();
    }

    /**
     * check to see if the current user has a capability
     * always returns a bool
     * @return boolean
     */
    public static function isCurrentUserCapable($capability, $site_id = null) {

        $cu = owa_coreAPI::getCurrentUser();
        owa_coreAPI::debug("Current User Role: ".$cu->getRole());
        owa_coreAPI::debug("Current User Authentication: ".$cu->isAuthenticated());
        $ret = $cu->isCapable($capability, $site_id);
        owa_coreAPI::debug("Is current User capable: ".$ret);
        return $ret;
    }

    public static function isCurrentUserAuthenticated() {

        $cu = owa_coreAPI::getCurrentUser();
        return $cu->isAuthenticated();
    }
    
    public static function getCurrentUserApiKey() {

        $cu = owa_coreAPI::getCurrentUser();
        return $cu->getApiKey();
    }
    
    /**
     * @return owa_service
     */
    public static function serviceSingleton() {

        static $s;

        if(empty($s)) {

            if (!class_exists('owa_service')) {
                require_once(OWA_BASE_CLASS_DIR.'service.php');
            }

            $s = owa_coreAPI::supportClassFactory('base', 'service');

        }

        return $s;
    }

    public static function cacheSingleton( $params = [] ) {

        static $cache;

        if ( empty ( $cache ) ) {
	        
            $cache = owa_lib::simpleFactory( 'owa_cache', OWA_BASE_CLASS_DIR.'cache.php', $params );
        }

        return $cache;
    }
    
    public static function implementationFactory( $group, $implementation_name ) {
      
        // get implementation
        $s = owa_coreAPI::serviceSingleton();
    
        $mapValue = $s->getMapValue( $group, $implementation_name );
		
        if ( $mapValue && is_array( $mapValue ) ) {
	        
	        // new style map format
	        if ( array_key_exists('class_name', $mapValue ) ) {
		        
		        $class_name = $mapValue['class_name'];
		       		            
		        if ( array_key_exists('file', $mapValue ) ) {
			        
			        $file = $mapValue['file'];
		        }
		        
		        if ( array_key_exists('params', $mapValue ) ) {
			        
			        $params = $mapValue['params'];
		        }
		        
	        } else {
				// old style compatability
	        	list( $class_name, $file, $params ) = $mapValue;
            }

            //owa_coreAPI::debug(print_r($implementation, true));
            return owa_lib::simpleFactory( $class_name, $file, $params );

        } else {

            throw new Exception("No implementation by the name $implementation_name found in group $group.");
        }
        
    }


    public static function requestContainerSingleton() {

        static $request;

        if(!isset($request)):

            if (!class_exists('owa_requestContainer')):
                require_once(OWA_DIR.'owa_requestContainer.php');
            endif;

            $request = owa_lib::factory(OWA_DIR, '', 'owa_requestContainer');

        endif;

        return $request;

    }

    public static function moduleRequireOnce($module, $class_dir, $file) {

        if (!empty($class_dir)) {

            $class_dir .= '/';

        }

        $full_file_path = OWA_BASE_DIR.'/modules/'.$module.'/'.$class_dir.$file.'.php';

        if (file_exists($full_file_path)) {
            return require_once($full_file_path);
        } else {
            owa_coreAPI::debug("moduleRequireOnce says no file found at: $full_file_path");
            return false;
        }
    }

    public static function moduleFactory($modulefile, $class_suffix = null, $params = '', $class_ns = 'owa_') {
        list($module, $file) = explode(".", $modulefile);
        $class = $class_ns.$file.$class_suffix;
        //print $class;
        // Require class file if class does not already exist
        if(!class_exists($class)):
            owa_coreAPI::moduleRequireOnce($module, '', $file);
        endif;

        $obj = owa_lib::factory(OWA_BASE_DIR.'/modules/'.$module, '', $class, $params);

        //if (isset($obj->module)):
            $obj->module = $module;
        //endif;

        return $obj;
    }

    public static function moduleGenericFactory($module, $sub_directory, $file, $class_suffix = null, $params = '', $class_ns = 'owa_') {

        $class = $class_ns.$file.$class_suffix;

        // Require class file if class does not already exist
        if(!class_exists($class)):
            owa_coreAPI::moduleRequireOnce($module, $sub_directory, $file);
        endif;

        $obj = owa_lib::factory(OWA_DIR.'modules'.'/'.$module.'/'.$sub_directory, '', $class, $params);

        return $obj;
    }

    /**
     * Produces Module Classes (module.php)
     *
     * @return Object module class object
     */
    public static function moduleClassFactory($module) {

        if (!class_exists('owa_module')):
            require_once(OWA_BASE_CLASSES_DIR.'owa_module.php');
        endif;

        require_once(OWA_BASE_DIR.'/modules/'.$module.'/module.php');

        return owa_lib::factory(OWA_BASE_CLASSES_DIR.$module, 'owa_', $module.'Module');

    }


    public static function updateFactory($module, $filename, $class_ns = 'owa_') {

        require_once(OWA_BASE_CLASS_DIR.'update.php');

        //$obj = owa_coreAPI::moduleGenericFactory($module, 'updates', $filename, '_update');
        $class = $class_ns.$module.'_'.$filename.'_update';

        // Require class file if class does not already exist
        if(!class_exists($class)):
            owa_coreAPI::moduleRequireOnce($module, 'updates', $filename);
        endif;

        $obj = owa_lib::factory(OWA_DIR.'modules'.'/'.$module.'/'.'updates', '', $class);

        $obj->module_name = $module;
        if (!$obj->schema_version) {
            $obj->schema_version = $filename;
        }
        return $obj;
    }

    public static function subViewFactory($subview, $params = array()) {

        list($module, $class) = explode(".", $subview);
        //print_r($module.' ' . $class);
        //owa_lib::moduleRequireOnce($module, $class);

        $subview =  owa_lib::moduleFactory($subview, 'View', $params);
        $subview->is_subview = true;

        return $subview;
    }

    public static function supportClassFactory($module, $class, $params = array(),$class_ns = 'owa_') {

        $obj = owa_lib::factory(OWA_BASE_DIR.'/'.'modules'.'/'.$module.'/'.'classes'.'/', $class_ns, $class, $params);
        //$obj->module = $module;

        return $obj;
    }

    /**
     * Convienence method for generating entities
     *
     * @param unknown_type $entity_name
     * @return unknown
     */
    public static function entityFactory($entity_name) {

        /* SETUP STORAGE ENGINE */

        // Must be called before any entities are created

        if (!defined('OWA_DTD_INT')) {
            if (defined('OWA_DB_TYPE')) {
                owa_coreAPI::setupStorageEngine(OWA_DB_TYPE);
            } else {
                //owa_coreAPI::setupStorageEngine('mysql');
                self::error("OWA_DB_TYPE constant has not been set for some reason.");
            }

        }



        if (!class_exists('owa_entity')):
            require_once(OWA_BASE_CLASSES_DIR.'owa_entity.php');
        endif;

        $entity = owa_coreAPI::moduleSpecificFactory($entity_name, 'entities', '', '', false);
        $entity->name = $entity_name;
        return $entity;
        //return owa_coreAPI::supportClassFactory('base', 'entityManager', $entity_name);

    }

    /**
     * Convienence method for generating entities
     *
     * @param unknown_type $entity_name
     * @return unknown
     * @depricated
     * @todo REMOVE
     */
    public static function rawEntityFactory($entity_name) {

        return owa_coreAPI::entityFactory($entity_name);

    }

    /**
     * Factory for generating module specific classes
     *
     * @param string $modulefile
     * @param string $class_dir
     * @param string $class_suffix
     * @param array $params
     * @return unknown
     */
    public static function moduleSpecificFactory($modulefile, $class_dir, $class_suffix = null, $params = '', $add_module_name = true, $class_ns = 'owa_') {

        list($module, $file) = explode(".", $modulefile);
        $class = $class_ns.$file.$class_suffix;

        // Require class file if class does not already exist
        if(!class_exists($class)):
            owa_coreAPI::moduleRequireOnce($module, $class_dir, $file);
        endif;

        $obj = owa_lib::factory(OWA_BASE_DIR.'/'.'modules'.'/'.$class_dir.'/'.$module, '', $class, $params);

        if ($add_module_name == true):
            $obj->module = $module;
        endif;

        return $obj;


    }

    public static function executeApiCommand($map) {
		
		// carve out for REST API backwards compatability during migration
		if ( array_key_exists('version', $map) ) {
			
			$route = self::lookupRestRoute( $map['request_method'], $map['module'], $map['version'], $map['do']);
			
			if ( $route ) {
				
				//$params['rest_route'] = $route;
				owa_coreAPI::debug('API params: ');
				owa_coreAPI::debug($map);
				owa_coreAPI::debug('API route: ');
				owa_coreAPI::debug($route);
				$controller = owa_lib::simpleFactory( $route['class_name'], $route['file'], $map );					
				$response = self::runController( $controller );
				
				$response = json_decode($response);
				
				return $response->data;
			}
		}
		
        if (!array_key_exists('do', $map)) {
            echo ("API Command missing from request.");
            owa_coreAPI::debug('API Command missing from request. Aborting.');
            exit;
        } else {
            // load service
            $s = owa_coreAPI::serviceSingleton();
            // lookup method class
            $do = $s->getApiMethodClass($map['do']);

        }

        // if exists, pass to OWA as a request
        if ($do) {

            if (array_key_exists('args', $do)) {

                $passed_args = array();

                foreach ($do['args'] as $arg) {

                    if (isset($map[$arg])) {
                        $passed_args[] = $map[$arg];
                    } else {
                        $passed_args[] = '';
                    }
                }

                if (!empty($do['file'])) {

                    if (!class_exists($do['callback'][0])) {
                        require_once($file);
                    }
                }

                $something = call_user_func_array($do['callback'], $passed_args);
            }

            return $something;
        } else {
            echo "No API Method Found.";
        }

    }

    /**
     * Convienence method for generating metrics
     *
     * @param unknown_type $entity_name
     * @return unknown
     */
    public static function metricFactory($metric_name, $params = array()) {

        if (!strpos($metric_name, '.')) {
            $s = owa_coreAPI::serviceSingleton();
            $metric_name = $s->getMetricClasses($metric_name);
        }

        if (!class_exists('owa_metric')) {
            require_once(OWA_BASE_CLASSES_DIR.'owa_metric.php');
        }

        return owa_coreAPI::moduleSpecificFactory($metric_name, 'metrics', '', $params, false);
    }

    /**
     * Returns a consolidated list of admin/options panels from all active modules
     *
     * @return array
     */
    public static function getAdminPanels() {

        $panels = array();

        $service = owa_coreAPI::serviceSingleton();

        foreach ($service->modules as $k => $v) {
            $v->registerAdminPanels();
            $module_panels = $v->getAdminPanels();
            if ($module_panels) {
                foreach ($module_panels as $key => $value) {

                    $panels[$value['group']][] = $value;
                }
            }
        }

        return $panels;
    }

    /**
     * Returns a consolidated list of nav links from all active modules for a particular view
     * and named navigation element.
     *
     * @param string nav_name the name of the navigation element that you want links for
     * @param string sortby the array value to sort the navigation array by
     * @return array
     */
    public static function getNavigation($view, $nav_name, $sortby ='order') {

        $links = array();

        $service = owa_coreAPI::serviceSingleton();

        foreach ($service->modules as $k => $v) {

            // If the module does not have nav links, register them. needed in case this function is called twice on
            // same view.
            if (empty($v->nav_links)):
                $v->registerNavigation();
            endif;

            $module_nav = $v->getNavigationLinks();


            if (!empty($module_nav)) {
                // assemble the navigation for a specific view's named navigation element'
                foreach ($module_nav as $key => $value) {

                    $links[$value['view']][$value['nav_name']][] = $value;
                }
            }

        }

        //print_r($links[$view][$nav_name]);
        if (!empty($links[$view][$nav_name])):
               // sort the array
               usort($links[$view][$nav_name], function($a, $b) use ($sortby) {
                return strnatcmp($a[$sortby], $b[$sortby]);
            });

            return $links[$view][$nav_name];
        else:
            return false;
        endif;

    }

    public static function getGroupNavigation($group_name, $sortby ='order') {

        $links = array();

        $service = owa_coreAPI::serviceSingleton();

        foreach ($service->modules as $k => $v) {

            // If the module does not have nav links, register them. needed in case this function is called twice on
            // same view.
            if ( empty( $v->nav_links ) ) {

                $v->registerNavigation();
            }

            $module_nav = $v->getNavigationLinks();

            if ( $module_nav ) {

                //loop through returned nav array
                foreach ( $module_nav as $group => $nav_links ) {

                    foreach ( $nav_links as $subgroup => $link ) {

                        // check to see if group exists
                        if ( array_key_exists( $group, $links ) ) {

                            // check to see if subgroup is already present in the main array
                            if ( array_key_exists( $subgroup, $links[ $group ] ) ) {
                                // merge various elements?? not now.

                                //check to see if there is an existing set of subgroup links
                                if ( array_key_exists( 'subgroup', $links[ $group ][ $subgroup ] ) ) {
                                    // if so, merge the subgroups
                                    $links[ $group ][ $subgroup ][ 'subgroup' ] = array_merge( $links[ $group ][ $subgroup ][ 'subgroup' ], $link[ 'subgroup' ] );
                                } else {

                                }
                            } else {
                                // else populate the link
                                $links[$group][$subgroup] = $link;
                            }

                        } else {
                            $links[$group][$subgroup] = $link;
                        }
                    }

                }
            }
        }

        if ( isset( $links[$group_name] ) ) {

            return $links[$group_name];
        }
    }

    /**
     * @Todo REMOVE
     */
    public static function getNavSort($a, $b) {

        return strnatcmp($a['order'], $b['order']);
    }


    public static function getActiveModules() {

        $c = owa_coreAPI::configSingleton();

        $config = $c->config->get('settings');


        $active_modules = array();

        foreach ($config as $k => $module) {

            if ( isset($module['is_active']) && $module['is_active'] == true) {
                $active_modules[] = $k;
            }
        }

        return $active_modules;

    }
    
    public static function getPresentModules() {
	    $path = OWA_DIR.'modules';
	    // Check directory exists or not
		if( file_exists($path) && is_dir($path)) {
        	// Scan the files in this directory
			$result = scandir($path);
        
			// Filter out the current (.) and parent (..) directories
			$files = array_diff($result, array('.', '..', 'index.php'));
			owa_coreAPI::debug('Modules present are: ');
			owa_coreAPI::debug( $files );
			
			return $files;
		}
    }

    public static function getModulesNeedingUpdates() {

        $service = owa_coreAPI::serviceSingleton();

        return $service->getModulesNeedingUpdates();
    }

    /**
     * Invokes controller to perform controller
     *
     * @param $action string
     *
     */
    public static function performAction( $action, $params = array() ) {

        $service = owa_coreAPI::serviceSingleton();
			
		// Load action controller from service map which uses the 'module.action' convention	
		$action_map = $service->getMapValue('actions', $action );
			
		// create the controller object
        if ( $action_map ) {
	    
            $controller = owa_lib::simpleFactory( $action_map['class_name'], $action_map['file'], $params );
        
        } else {
        
            // attempt to use old style convention
            $controller = owa_coreAPI::moduleFactory($action, 'Controller', $params);
        }
		
		return owa_coreAPI::runController( $controller );
    }
    
    public static function runController( $controller ) {
	    
	    if ( ! $controller || ! method_exists( $controller, 'doAction' ) ) {

            owa_coreAPI::debug("Class is not a controller. no doAction method found.");
            return;
        }

        // call the doAction method which is part of the abstract controller class
        // inherited by all other controller classes
        $data = $controller->doAction();

        // Display view if controller calls for one.
        if ( ! empty( $data['view'] ) || ! empty( $data['action'] ) ) {

            // Redirect to a view
            if ( $data['view_method'] == 'redirect' ) {

                return owa_lib::redirectToView( $data );

            // return an image . Will output headers and binary data.
            } elseif ( $data['view_method'] == 'image' ) {

                return owa_coreAPI::displayImage( $data );

            } else {

                return owa_coreAPI::displayView( $data );
            }

        } elseif( ! empty( $data['do'] ) ) {

            return owa_lib::redirectToView( $data );
        }
    }

    /**
     * Logs an event to the event queue
     *
     * take an owa_event object as a message.
     *
     * @param string $event_type
     * @param object $message
     * @return boolean
     */
    public static function logEvent( $event_type, $message = '') {

        owa_coreAPI::debug("Logging new event $event_type");
		
        // Check to see if named users should be logged
        if (owa_coreAPI::getSetting('base', 'log_named_users') != true) {
            $cu = owa_coreAPI::getCurrentUser();
            $cu_user_id = $cu->getUserData('user_id');

            if( ! empty( $cu_user_id ) ) {
				owa_coreAPI::debug("Not logging named user.");
                return false;
            }
        }

		// backwads compatability with old style messages
		// @todo is this needed anymore?
        $class= 'owa_event';
        
        if ( ! ( $message instanceof $class ) ) {
	        
            $event = owa_coreAPI::supportClassFactory( 'base', 'event' );
            $event->setProperties( $message );
            $event->setEventType( $event_type );
            
        } else {
	        
            $event = $message;
        }
        
        $service = owa_coreAPI::serviceSingleton();
        
        // Tracking Event processing STAGE 1
        // sets any necessary environmental properties from SERVER global
        $teh = owa_coreAPI::getInstance( 'owa_trackingEventHelpers', OWA_BASE_CLASS_DIR.'trackingEventHelpers.php');
        $environmentals = $service->getMap( 'tracking_properties_environmental' );
        $teh->setTrackerProperties( $event, $environmentals );
		
        // do not log if the do not log property is set on the event.
        if ($event->get('do_not_log')) {
            return false;
        }
        
        // do not log if the request is robotic
        owa_coreAPI::debug("Testing to see if event was generated by a robot");
        owa_coreAPI::debug("User Agent: ". $message->get('HTTP_USER_AGENT') );
        
        $bcap = $service->getBrowscap( $message->get('HTTP_USER_AGENT') );
       
        if ( ! owa_coreAPI::getSetting('base', 'log_robots') ) {

            if ( $bcap->robotCheck() ) {
	            
                owa_coreAPI::debug("ABORTING: request appears to be from a robot");
                owa_coreAPI::setRequestParam('is_robot', true);
                
                return;
            }
        }

        // check to see if IP should be excluded
        if ( owa_coreAPI::isIpAddressExcluded( $event->get('ip_address') ) ) {
	        
            owa_coreAPI::debug("Not logging event. IP address found in exclusion list.");
            
            return false;
        }
        
        // queue for later or process event straight away
        if ( owa_coreAPI::getSetting( 'base', 'queue_events' ) ||
             owa_coreAPI::getSetting( 'base', 'queue_incoming_tracking_events' ) ) {

            $q = owa_coreAPI::getEventQueue( 'incoming_tracking_events' );
            owa_coreAPI::debug('Queuing '.$event->getEventType().' event with properties: '.print_r($event->getProperties(), true ) );
            $q->sendMessage( $event );

        } else {

            // lookup which event processor to use to process this event type
            $processor_action = owa_coreAPI::getEventProcessor( $event->getEventType() );
           
			owa_coreAPI::debug('About to perform action: '.$processor_action);
			owa_coreAPI::debug($event);
			
			return owa_coreAPI::performAction( $processor_action, array( 'event' => $event ) );
        }
    }

    public static function getInstance( $class, $path ) {

        if ( ! class_exists( $class ) ) {

            require_once( $path );
        }

        return $class::getInstance();
    }

    public static function displayImage($data) {

        header('Content-type: image/gif');
        header('P3P: CP="'.owa_coreAPI::getSetting('base', 'p3p_policy').'"');
        header('Expires: Sat, 22 Apr 1978 02:19:00 GMT');
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Pragma: no-cache');

        echo owa_coreAPI::displayView($data);
    }


    /**
     * Displays a View without user authentication. Takes array of data as input
     *
     * @param array $data
     * @param string $viewfile a specific view file to use
     * @return string
     *
     */
    public static function displayView($data, $viewfile = '') {

        if (empty($viewfile)):
            $viewfile = $data['view'];
        endif;

        $view = owa_coreAPI::moduleFactory($viewfile, 'View');
      
        $view->setData($data);
        return $view->assembleView($data);

    }

    public static function displaySubView($data, $viewfile = '') {

        if (empty($viewfile)):
            $viewfile = $data['view'];
        endif;

        $view =  owa_coreAPI::subViewFactory($viewfile);

        return $view->assembleView($data);

    }

    /**
     * Strip a URL of certain GET params
     * @depricated
     * @return string
     * @todo REMOVE
     */
    function stripDocumentUrl($url) {

        if (owa_coreAPI::getSetting('base', 'clean_query_string')):

            if (owa_coreAPI::getSetting('base', 'query_string_filters')):
                $filters = str_replace(' ', '', owa_coreAPI::getSetting('base', 'query_string_filters'));
                $filters = explode(',', $filters);
            else:
                $filters = array();
            endif;

            // OWA specific params to filter
            array_push($filters, owa_coreAPI::getSetting('base', 'source_param'));
            array_push($filters, owa_coreAPI::getSetting('base', 'ns').owa_coreAPI::getSetting('base', 'feed_subscription_param'));

            //print_r($filters);

            foreach ($filters as $filter => $value) {

              $url = preg_replace(
                '#\?' .
                $value .
                '=.*$|&' .
                $value .
                '=.*$|' .
                $value .
                '=.*&#msiU',
                '',
                $url
              );

            }

        endif;
         //print $url;

         return $url;

    }

    public static function getRequestParam($name) {

        $service = owa_coreAPI::serviceSingleton();

        return $service->request->getParam($name);

    }

    public static function getRequest() {
        $service = owa_coreAPI::serviceSingleton();
        return $service->request;
    }

    public static function setRequestParam($name, $value) {

        $service = owa_coreAPI::serviceSingleton();
        return $service->request->setParam($name, $value);

    }

    public static function makeTimePeriod($time_period, $params = array()) {

        $period = owa_coreAPI::supportClassFactory('base', 'timePeriod');

        if ( ! array_key_exists('period', $params)) {
            $params['period'] = $time_period;
        }
        $period->setFromMap( $params );
        return $period;
    }

    /**
     * Factory method for producing validation objects
     *
     * @return Object
     */
    public static function validationFactory($class_file, $conf = array()) {

        if (!class_exists('owa_validation')):
            require_once(OWA_BASE_CLASS_DIR.'validation.php');
        endif;

        return owa_lib::factory(OWA_PLUGIN_DIR.'validations', 'owa_', $class_file, $conf, 'Validation');

    }

    public static function debug($msg) {

        $e = owa_coreAPI::errorSingleton();
        $e->debug($msg);
        return;
    }

    public static function error($msg) {

        $e = owa_coreAPI::errorSingleton();
        $e->err($msg);
        return;
    }

    public static function notice($msg) {

        $e = owa_coreAPI::errorSingleton();
        $e->notice($msg);
    }

    public static function createCookie($cookie_name, $cookie_value, $expires = 0, $path = '/; samesite=Lax', $domain = '', $secure = false) {

        if ( $domain ) {
            // sanitizes the domain
            $domain = owa_lib::sanitizeCookieDomain( $domain );
            
        } else {
	        
            $domain = owa_coreAPI::getSetting('base', 'cookie_domain');
        }
        if (is_array($cookie_value)) {

            $cookie_value = owa_lib::implode_assoc('=>', '|||', $cookie_value);
        }

        // add namespace
        $cookie_name = sprintf('%s%s', owa_coreAPI::getSetting('base', 'ns'), $cookie_name);

        // debug
        owa_coreAPI::debug(sprintf('Setting cookie %s with values: %s under domain: %s', $cookie_name, $cookie_value, $domain));

        // makes cookie to session cookie only
        if (!owa_coreAPI::getSetting('base', 'cookie_persistence')) {
	        
            $expires = 0;
        }
		
		$secure = owa_lib::isHttps();
	
        // PHP 7.3 has a different function signature.
        // @todo refactor usage to clean up once php 7.3 is min requirment.
        if (PHP_VERSION_ID < 70300) {
	        
	        setcookie($cookie_name, $cookie_value, $expires, $path, $domain, $secure);
	        
	    } else {
		    	
			$options = [
		        
		        'expires' 	=> $expires,
                'path' 		=> '/',
                'samesite' 	=> 'Lax',
                'domain' 	=> $domain,
                'secure' 	=> $secure
	        ];
	        
	        setcookie($cookie_name, $cookie_value, $options);
	    }
    }

    public static function deleteCookie($cookie_name, $path = '/', $domain = '') {

        return owa_coreAPI::createCookie($cookie_name, false, time()-3600*25, $path, $domain);
    }

    public static function registerStateStore($name, $expiration, $length = '', $format = '', $type = 'cookie', $cdh_required = '') {

        $service = owa_coreAPI::serviceSingleton();
        return $service->request->state->registerStore( $name, $expiration, $length, $format, $type, $cdh_required );
    }

    public static function setState($store, $name, $value, $store_type = '', $is_perminent = '') {

        $service = owa_coreAPI::serviceSingleton();
        return $service->request->state->set($store, $name, $value, $store_type, $is_perminent);
    }

    public static function getState($store, $name = '') {

        $service = owa_coreAPI::serviceSingleton();
        return $service->request->state->get($store, $name);
    }

    // depricated
    public static function getStateParam($store, $name = '') {

        return owa_coreAPI::getState($store, $name);
    }

    public static function getServerParam($name = '') {

        $service = owa_coreAPI::serviceSingleton();
        return $service->request->getServerParam($name);
    }

    public static function clearState($store, $name = '') {

        $service = owa_coreAPI::serviceSingleton();
        $service->request->state->clear($store, $name);

    }

    public static function getEventProcessor($event_type) {

        $service = owa_coreAPI::serviceSingleton();
        $processor = $service->getMapValue('event_processors', $event_type);

        if (empty($processor)) {

            $processor = 'base.processEvent';
        }

        return $processor;
    }

    /**
     * Retrieves any registered build packages
     *
     * @return array
     */
    public static function getBuildPackages() {

        $service = owa_coreAPI::serviceSingleton();
        $map = $service->getMap('build_packages');

        if ( ! $map ) {
            $map = array();
        }

        return $map;
    }

    /**
     * Handles OWA internal page/action requests
     *
     * @return unknown
     */
    public static function handleRequest($caller_params = null, $action = '') {

        static $init;

        $service = owa_coreAPI::serviceSingleton();
        // Override request parsms with those passed by caller
        if (!empty($caller_params)) {
            $service->request->mergeParams($caller_params);
        };

        $params = $service->request->getAllOwaParams();

        if ($init != true) {
            owa_coreAPI::debug('Handling request with params: '. print_r($params, true));
        }

        // backwards compatability with old style view/controler scheme
        // still needed??
        if (array_key_exists('view', $params)) {
            // its a view request so the only data is in whats in the params
            $init = true;
            return owa_coreAPI::displayView($params);
        }

        if (empty($action)) {
            $action = owa_coreAPI::getRequestParam('action');
            if (empty($action)) {
                $action = owa_coreAPI::getRequestParam('do');

                if (empty($action)) {
                    $action = owa_coreAPI::getSetting('base', 'start_page');
                    $params['do'] = $action;
                }
            }
        }
		
		
		// REST API Requests
		// Lookup controler for REST API route.
		if ( owa_coreAPI::getSetting( 'base', 'request_mode' ) === 'rest_api' ) {
			
			// get request method
			$request_method = $service->request->getRequestType();
			
			// check to see if this is a CORS pre-flight Request
			if ($request_method == 'OPTIONS') {
				
				$controller = owa_lib::simpleFactory( 'owa_corsPreflightController', 'controllers/corsPreflightController.php', [] );					
				return owa_coreAPI::runController( $controller );
			}
			
			// check for rewriten rest params and set module, version, and do params from that
			$rest_params = self::getRequestParam('rest_params');
			
			if ( $rest_params ) {
			
				$rest_params = explode('/', $rest_params);
				self::debug( 'exploding raw REST params:');
				self::debug( $rest_params );
			
				if ( count( $rest_params ) >= 3 ) {
					
					$params['module'] = $rest_params[0];
					$params['version'] = $rest_params[1];
					$params['do'] = $rest_params[2];
					$action = $params['do'];
				}				
			}
			
			
			owa_coreAPI::debug('Generating REST API route controller...');
			
			if ( owa_lib::keyExistsNotEmpty( 'module', $params ) && owa_lib::keyExistsNotEmpty( 'version', $params ) ) {
			
				$route = self::lookupRestRoute( $request_method, $params['module'], $params['version'], $action );
				
				if ( $route ) {
					
					// set the remainer of the rewritten rest params
					
					if ( $rest_params ) {
						
						// slice off the first three params which have already been set
						$rest_params = array_slice($rest_params, 3);
						
						foreach ( $rest_params as $k => $v) {
							
							$params[ $route['conf'][ 'params_order' ][$k] ] = $rest_params[ $k ];
						}
					}
					
					$params['rest_route'] = $route;
					$controller = owa_lib::simpleFactory( $route['class_name'], $route['file'], $params );					
					return owa_coreAPI::runController( $controller );
				
				} else {
					
					owa_coreAPI::debug('No REST API route found');
					return;	
				}

			} else {
				
				owa_coreAPI::debug('Could not generate controller because no version param was on request.');
				return;
			}
			
		}
		
		
		
        $init = true;
        owa_coreAPI::debug('About to perform action: '.$action);
        return owa_coreAPI::performAction($action, $params);

    }
    
    public static function lookupRestRoute( $request_method, $module, $version, $do ) {
	    
	    if ( ! empty( $request_method )
	    	&& ! empty( $version )
	    	&& ! empty( $do )
	    	&& ! empty( $module )
	    ){
		    
		    $service = owa_coreAPI::serviceSingleton();
		    $route = $service->getRestApiRoute($module, $version, $do, $request_method );
			owa_coreAPI::debug($route);
		    return $route;
	    }
    }

    public static function isUpdateRequired() {

        $service = owa_coreAPI::serviceSingleton();
        return $service->isUpdateRequired();
    }

    /**
     * @return array
     */
    public static function getSitesList() {
        $db = owa_coreAPI::dbSingleton();
        $db->selectFrom('owa_site');
        $db->selectColumn('*');
        $sites = $db->getAllRows();

        if ( ! $sites ) {
            $sites = array();
        }
        return $sites;
    }
	
	
    public static function profile($that = '', $function = '', $line = '', $msg = '') {

        return;
    }

    public static function profileDisplay() {
        
		return;
    }

    public static function getEventDispatch() {

        if (! class_exists( 'owa_eventDispatch' ) ) {
            require_once(OWA_BASE_CLASS_DIR.'eventDispatch.php');
        }

        return owa_eventDispatch::get_instance();

    }

    public static function getEventQueue( $name ) {

        static $queues;

        // make queue if needed
        if ( ! isset( $queues[ $name ] ) ) {

            // get queue config
            $s = owa_coreAPI::serviceSingleton();
            $map = $s->getMapValue('event_queues', $name);

            if ( $map ) {

                $implementation = $s->getMapValue( 'event_queue_types', $map['queue_type'] );

                if ( $implementation
                     && isset( $implementation[0] )
                     && isset( $implementation[1] )
                ) {
                    owa_coreAPI::debug(print_r($implementation, true));
                    $queues[ $name ] = owa_lib::simpleFactory( $implementation[0], $implementation[1], $map );

                } else {

                    throw new Exception("No event queue by that type found.");
                }

            } else {

                throw new Exception("No configuration found for event queue $name.");
            }
        }
            // return queue
        return $queues[ $name ];
    }

    public static function getCliCommandClass($command) {

        $s = owa_coreAPI::serviceSingleton();
        return $s->getCliCommandClass($command);
    }

    public static function getGeolocationFromIpAddress($ip_address) {

        $s = owa_coreAPI::serviceSingleton();
        $s->geolocation->getGeolocationFromIp($ip_address);
        return $s->geolocation;
    }

    public static function getNonceTimeInterval() {

        return  ceil( time() / owa_coreAPI::getSetting( 'base', 'nonce_expiration_period') );
    }

    public static function createNonce($action) {

        $time = owa_coreAPI::getNonceTimeInterval();
        $cu = owa_coreAPI::getCurrentUser();
        $user_id = $cu->getUserData( 'user_id' );

        $full_nonce = $time . $action . $user_id . 'owa_nonce';

        $nonce = substr( owa_coreAPI::saltedHash($full_nonce, 'nonce'), -12, 10);

        return $nonce;
    }
    
    public static function createRestApiNonce( $version, $module, $do ) {
        
        return self::createNonce( $version . $module . $do );
    }

    public static function saltedHash( $data, $scheme, $hash_type = 'md5' ) {

        $salt = owa_coreAPI::getSalt( $scheme );
        return owa_lib::hash( $hash_type, $data, $salt );
    }



    public static function getSalt( $scheme ) {

        static $cached_salts;

        $scheme = strtoupper($scheme);

        if ( ! $cached_salts ) {

            $cached_salts = array();
            $ns = strtoupper( owa_coreAPI::getSetting('base', 'ns') );

            foreach (array('NONCE', 'SECRET', 'AUTH') as $f ) {

                foreach (array('KEY', 'SALT') as $s ) {

                    $const = sprintf("%s%s_%s", $ns, $f, $s);

                    if ( ! defined ( "$const" ) ) {
                        continue;
                    } else {

                        $cached_salts[ $f.'_'.$s ] = constant("$const");
                    }
                }
            }
        }


        $key = '';
        $salt = '';

        if (array_key_exists( $scheme.'_KEY', $cached_salts ) ) {

            $key = $cached_salts[ $scheme.'_KEY' ];
        }

        if (array_key_exists( $scheme.'_SALT', $cached_salts ) ) {

            $salt = $cached_salts[ $scheme.'_SALT' ];
        }

        return $key . $salt;
    }

    public static function secureRandomString( $length, $special_chars = true, $more_special_chars = true ) {

        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        if ( $special_chars )
            $chars .= '!@#$%^&*()';
        if ( $more_special_chars )
            $chars .= '-_ []{}<>~`+=,.;:/?|';

        $password = '';
        for ( $i = 0; $i < $length; $i++ ) {
            $password .= substr($chars, owa_coreAPI::random(0, strlen($chars) - 1), 1);
        }

        return $password;
    }

    public static function random($min, $max) {

        static $rnd_value;

        if ( strlen($rnd_value) < 8 ) {

            $notrandom = false;

            if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {

                $rnd_value = bin2hex(openssl_random_pseudo_bytes(32, $cstrong));

                if ( ! $cstrong ) {

                    $notrandom = true;
                }

            } else {

                $notrandom = true;
            }

            if ( $notrandom ) {

                $seed = microtime();
                $rnd_value = md5( uniqid(microtime() . mt_rand(), true ) . $seed );
                $rnd_value .= sha1($rnd_value);
                $rnd_value .= sha1($rnd_value . $seed);

            }

            //$seed = md5($seed . $rnd_value);
        }
        // Take the first 8 digits for our value
        $value = substr($rnd_value, 0, 8);

        // Strip the first eight, leaving the remainder for the next call to random.
        $rnd_value = substr($rnd_value, 8);

        $value = abs(hexdec($value));

        // Some misconfigured 32bit environments (Entropy PHP, for example) truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats.
        $max_random_number = 3000000000 === 2147483647 ? (float) "4294967295" : 4294967295; // 4294967295 = 0xffffffff

        // Reduce the value to be within the min - max range
        if ( $max != 0 )
            $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 );

        return abs(intval($value));
    }

    public static function summarize($map) {

        $entity = owa_coreAPI::entityFactory($map['entity']);
        $db = owa_coreAPI::dbSingleton();
        $db->selectFrom($entity->getTableName(), $entity->getTableAlias());

        foreach ($map['columns'] as $col => $action) {

            switch ($action) {

                case 'sum':
                    $col_def = sprintf("SUM(%s)", $col);
                    $name = $col.'_sum';
                    break;
                case 'count':
                    $col_def = sprintf("COUNT(%s)", $col);
                    $name = $col.'_count';
                    break;
                case 'count_distinct':
                    $col_def = sprintf("COUNT(distinct %s)", $col);
                    $name = $col.'_dcount';
                    break;
                case 'max':
                    $col_def = sprintf("MAX(%s)", $col);
                    $name = $col.'_max';
                    break;
            }

            $db->selectColumn($col_def, $name);
        }

        foreach ($map['constraints'] as $con_col => $con_value) {

            if ( is_array( $con_value ) ) {
                $db->where($con_col, $con_value['value'], $con_value['operator']);
            } else {
                $db->where($con_col, $con_value);
            }
        }

        $ret = $db->getOneRow();
        return $ret;
    }

    public static function getJsTrackerTag( $site_id, $options = array() ) {

        if ( ! class_exists( 'owa_template' ) ) {
            require_once(OWA_BASE_CLASSES_DIR.'owa_template.php');
        }

        $t = new owa_template();

        $t->set( 'site_id', $site_id );
        $cmds = owa_coreAPI::filter( 'tracker_tag_cmds', array() );
        $t->set( 'cmds', $cmds );
        $t->set('options', $options);
        $t->set_template('js_log_tag.tpl');
        return $t->fetch();
    }

    public static function activateModule( $module_name ) {

        if ( $module_name ) {

            $m = owa_coreAPI::moduleClassFactory($module_name);
            return $m->activate();
        }
    }

    public static function deactivateModule( $module_name ) {

        if ( $module_name ) {

            $s = owa_coreAPI::serviceSingleton();
            $m = $s->getModule($module_name);
            return $m->deactivate();
        }
    }

    public static function installModule( $module_name ) {

        if ($module_name) {

            $m = owa_coreAPI::moduleClassFactory($module_name);
            $status = $m->install();
            return $status;
        }
    }

    public static function generateInstanceSpecificHash() {

        if ( defined( 'OWA_SECRET' ) ) {
            $salt = OWA_SECRET;
        } else {
            $salt = '';
        }

        if ( defined( 'OWA_DB_USER' ) ) {
            $salt .= OWA_DB_USER;
        }

        if ( defined( 'OWA_DB_PASSWORD' ) ) {
            $salt .= OWA_DB_PASSWORD;
        }

        return md5( $salt );
    }

    public static function getAllDimensions() {

        $s = owa_coreAPI::serviceSingleton();

        $dims = $s->dimensions;

        foreach ( $s->denormalizedDimensions as $k => $entity_dims ) {
            foreach ($entity_dims as $entity => $dedim) {
                $dims[$k] = $dedim;
            }
        }

        return $dims;
    }

    public static function getAllMetrics() {

        $s = owa_coreAPI::serviceSingleton();
        return $s->metrics;
    }

    public static function getGoalManager( $siteId ) {

        static $gm;

        if ( ! $gm ) {

            $gm = array();
        }

        if ( ! isset( $gm[$siteId] ) )  {
            $gm[ $siteId ] = owa_coreAPI::supportClassFactory('base', 'goalManager', $siteId);
        }

        return $gm[$siteId];
    }

    public static function getRequestTimestamp() {

        $r = owa_coreAPI::requestContainerSingleton();
        return $r->getTimestamp();
    }

    public static function isEveryoneCapable( $capability ) {

        $caps = owa_coreAPI::getCapabilities('everyone');

        if ( in_array( $capability, $caps ) ) {
            return true;
        } else {
            return false;
        }
    }

    public static function getCurrentUrl() {
	    
        $r = owa_coreAPI::requestContainerSingleton();
        return $r->getCurrentUrl();
    }

    public static function isIpAddressExcluded( $ip_address ) {

        // do not log if ip address is on the do not log list
        $ips = owa_coreAPI::getSetting( 'base', 'excluded_ips' );
        
        if ( $ips ) {
	        
	        owa_coreAPI::debug('Excluded ip address list: '.$ips);

            $ips = trim( $ips );

            if ( strpos( $ips, ',' ) ) {
                $ips = explode( ',', $ips );
            } else {
                $ips = array( $ips );
            }

            foreach( $ips as $ip ) {
                $ip = trim( $ip );
                if ( $ip_address === $ip ) {
                    owa_coreAPI::debug("Request is from excluded ip address: $ip.");
                    return true;
                }
            }
        }
    }
    
    static function loadConf( $file_name, $filter_name = '' ) {
	    
	    $conf_file = OWA_CONF_DIR . $file_name;
	    
	    if ( file_exists( $conf_file ) ) {
	    
	    	$conf = include( $conf_file);
	    }
	    
	    $sup_file = OWA_DATA_DIR .  $file_name;
	    
	    if ( file_exists( $sup_file ) ) {
		    
		    $sup_conf = include( $sup_file );
		    
		    if ( is_array( $sup_conf) ) {
		    
		    	$conf = array_merge( $conf, $sup_conf );
		    }
	    }
	    
	    // see generic filter name for filtering the final conf array
	    if ( ! $filter_name ) {
		    
		    $filter_name = 'conf.' . $file_name;
	    }
	    
	    return owa_coreAPI::filter( $filter_name, $conf );
    }

    /**
     * Attaches an event handler to the event queue
     *
     * @param array $event_name
     * @param string $handler_name
     * @return boolean
     */
    public static function registerFilter( $filter_name, $callback, $priority = 10 ) {

        $ed = owa_coreAPI::getEventDispatch();
        $ed->attachFilter($filter_name, $callback, $priority);
    }

    public static function filter( $filter_name, $value ) {

        $ed = owa_coreAPI::getEventDispatch();
        return $ed->filter( $filter_name, $value );
    }
    
    public static function loadEntitiesFromArray( $items, $entity_name ) {
	    
	    $set = [];
	    
	    if ( $items ) {
		    
		    foreach ($items as $item ) {
			    
			    $entity = owa_coreAPI::entityFactory( $entity_name );
			    $entity->setProperties( $item );
			    $set[] = $entity;
			    
		    }
	    }
	    
	    return $set;
    }
    
    public static function signRequestUrl( $url, $apiKey ) {
	    
	    $auth = owa_auth::get_instance();
	    
	    $signature = $auth->generateSignature( $url, $apiKey );
	    
	    $url .= '&owa_signature=' . $signature;
	    
	    return $url;
    }

}

?>
