<?php

/**
 * Basic extension controller.
 *
 * @author     Time.ly Network Inc.
 * @since      2.0
 *
 * @package    AI1EC
 * @subpackage AI1EC.Controller
 */
abstract class Ai1ec_Base_Extension_Controller {

    /**
     * @var Ai1ec_Registry_Object
     */
    protected $_registry;

    /**
     * @var array
     */
    protected $_settings;

    /**
     * @var Ai1ec_Registry_Object
     */
    protected static $_registry_static;

    /**
     * @var array
     */
    protected static $_settings_static;

    /**
     * @var array
     */
    protected static $_schema;

    /**
     * Get the long name of the extension
     */
    abstract public function get_name();

    /**
     * Get the machine name of the extension
     */
    abstract public function get_machine_name();

    /**
     * Get the version of the extension
     */
    abstract public function get_version();

    /**
     * Get the name of the main plugin file
     */
    abstract public function get_file();

    /**
     * Add extension specific settings
     */
    abstract protected function _get_settings();

    /**
     * Register action/filters/shortcodes for the extension
     *
     * @param Ai1ec_Event_Dispatcher $dispatcher
     */
    abstract protected function _register_actions(
        Ai1ec_Event_Dispatcher $dispatcher
    );

    /**
     * Perform the basic compatibility check.
     *
     * @param string $ai1ec_version
     *
     * @return boolean
     */
    public function check_compatibility( $ai1ec_version ) {
        return version_compare(
            $ai1ec_version,
            $this->minimum_core_required(),
            '>='
        );
    }

    /**
     * @return string
     */
    public function minimum_core_required() {
        return '2.0.7';
    }
    /**
     * Removes options when uninstalling the plugin.
     */
    public static function on_uninstall() {
        global $wpdb;
        if ( ! current_user_can( 'activate_plugins' ) ) {
            return;
        }
        $settings = self::$_registry_static->get( 'model.settings' );
        foreach ( self::$_settings_static as $name => $params ) {
            $settings->remove_option( $name );
        }
        $schema = self::$_schema;
        foreach ( $schema['tables'] as $table_name ) {
            // Delete table events
            $wpdb->query( 'DROP TABLE IF EXISTS ' . $table_name );
        }
    }

    /**
     * Public constructor
     */
    public function __construct() {
        global $wpdb;
        self::$_schema          = $this->_get_schema( $wpdb->prefix );
        $settings               = $this->_get_settings();
        $this->_settings        = $settings;
        self::$_settings_static = $settings;
    }

    /**
     * initialize the extension.
     */
    public function init( Ai1ec_Registry_Object $registry ) {
        $this->_registry        = $registry;
        // static properties are needed as uninstall hook must be static
        // http://wpseek.com/register_uninstall_hook/
        self::$_registry_static = $registry;
        register_deactivation_hook(
            $this->get_file(),
            array( $this, 'on_deactivation' )
        );

        $this->_install_schema( $registry );
        $this->_register_actions( $registry->get( 'event.dispatcher' ) );
        $this->_add_settings( $registry->get( 'model.settings' ) );
        $this->_perform_upgrade( $registry );
        if ( method_exists( $this, 'initialize_licence_actions' ) ) {
            $this->initialize_licence_actions();
        }
    }

    /**
     * Hides settings on deactivation.
     */
    public function on_deactivation() {
        if ( ! current_user_can( 'activate_plugins' ) ) {
            return;
        }
        $plugin        = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
        $referer       = 'deactivate-plugin_' . $plugin;
        // if we are disabling the plugin in the exception handler, this can't be done.
        // but i want to disable options
        if ( function_exists( '_get_list_table' ) ) {
            $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
            $action        = $wp_list_table->current_action();
            if ( 'deactivate-selected' === $action ) {
                $referer = 'bulk-plugins';
            }
            check_admin_referer( $referer );
        }
        $settings = $this->_registry->get( 'model.settings' );
        foreach ( $this->_settings as $name => $params ) {
            $settings->hide_option( $name );
        }
    }

    /**
     * Show the settings
     */
    public function show_settings( Ai1ec_Registry_Object $registry ) {
        $settings = $registry->get( 'model.settings' );
        foreach ( $this->_settings as $name => $params ) {
            if ( isset( $params['renderer'] ) ) {
                $settings->show_option( $name, $params['renderer'] );
            }
        }
        $settings->set( 'allow_statistics', true );
    }

    /**
     * If extensions need to add tables, they will need to override the function to add a schema.
     *
     * @param string $prefix Database prefix to use for table names.
     *
     * @return array An array with two keys, schema and tables which are used
     *               for installing and dropping the table.
     */
    protected static function _get_schema( $prefix ) {
        return array(
            'tables' => array(),
            'schema' => '',
        );
    }

    /**
     * Performe upgarde actions based on extension version
     *
     * @param Ai1ec_Registry_Object $registry
     */
    protected function _perform_upgrade( Ai1ec_Registry_Object $registry ) {
        $version_variable = 'ai1ec_' . $this->get_machine_name() .
            '_version';
        $option  = $registry->get( 'model.option' );
        $version = $option->get( $version_variable );
        if ( $version !== $this->get_version() ) {
            $registry->get( 'model.settings' )->perform_upgrade_actions();
            $this->_perform_upgrade_actions();
            $option->set( $version_variable, $this->get_version(), true );
        }
    }

    /**
     * Function called on add on upgrade.
     * Can be overridden by add ons for extra behaviour
     */
    protected function _perform_upgrade_actions() {

    }

    /**
     * Since the call the to the uninstall hook it's static, if a different behaviour
     * is needed also this call must be overridden.
     */
    protected function _register_uninstall_hook() {
        register_uninstall_hook(
            $this->get_file(),
            array( get_class( $this ), 'on_uninstall' )
        );
    }

    /**
     * Adds extension settings
     *
     * @param Ai1ec_Settings $settings
     */
    protected function _add_settings( Ai1ec_Settings $settings ) {
        foreach ( $this->_settings as $name => $params ) {
            $renderer = null;
            if ( isset( $params['renderer'] ) ) {
                $renderer = $params['renderer'];
            }
            $settings->register(
                $name,
                $params['value'],
                $params['type'],
                $renderer,
                $this->get_version()
            );
        }
    }

    /**
     * Check if the schema needs to be updated
     *
     * @param Ai1ec_Registry_Object $registry
     * @throws Ai1ec_Database_Update_Exception
     */
    protected function _install_schema( Ai1ec_Registry_Object $registry ) {
        $option = $registry->get( 'model.option' );
        $schema = self::$_schema;
        if (
            is_admin() &&
            ! empty( $schema['schema'] )
        ) {
            $db_version_variable = 'ai1ec_' . $this->get_machine_name() .
                '_db_version';
            $version = sha1( $schema['schema'] );
            if (
                $option->get( $db_version_variable ) !== $version
            ) {
                if (
                    $registry->get( 'database.helper' )->apply_delta(
                        $schema['schema']
                )
                ) {
                    $option->set( $db_version_variable, $version );
                } else {
                    throw new Ai1ec_Database_Update_Exception(
                        'Database upgrade for ' . $this->get_name() .
                        ' failed'
                    );
                }
            }
        }
    }

}