<?php

defined( 'ABSPATH' ) or exit;


final class Mobile_Contact_Bar_Option
{


    /**
     * Multidimensional array of the plugin's option, divided into sections: 'settings', 'contacts', 'styles'.
     * @var array
     */
    public static $option = null;



    /**
     * Multidimensional array of settings, divided into sections.
     * @var array
     */
    public static $settings = null;



    /**
     * Hooks WordPress's admin and AJAX actions and filters.
     *
     * @since 0.1.0
     */
    public static function plugins_loaded()
    {
        add_action( 'init'         , array( __CLASS__, 'init' ));
        add_action( 'admin_init'   , array( __CLASS__, 'admin_init' ));
        add_action( 'admin_footer' , array( __CLASS__, 'admin_footer' ));

        add_filter( 'pre_update_option_' . MOBILE_CONTACT_BAR__NAME, array( __CLASS__, 'pre_update_option' ), 10, 2 );

        add_action( 'wp_ajax_ajax_add_contact', array( __CLASS__, 'ajax_add_contact' ));
    }



    /**
     * Initializes option and settings.
     *
     * @since 0.1.0
     */
    public static function init()
    {
        self::$option   = get_option( MOBILE_CONTACT_BAR__NAME );
        self::$settings = Mobile_Contact_Bar_Settings::settings();
    }



    /**
     * Adds sections and fields to the option page.
     *
     * @since 0.1.0
     */
    public static function admin_init()
    {
        register_setting(
            MOBILE_CONTACT_BAR__NAME . '_group',
            MOBILE_CONTACT_BAR__NAME,
            array( __CLASS__, 'callback_sanitize_option' )
        );

        // Settings Sections

        foreach( self::$settings as $section_id => $section )
        {
            if( 'badges' == $section_id && ! class_exists( 'WooCommerce' ))
            {
                continue;
            }

            $title = ucfirst( $section_id );

            add_settings_section(
                'mcb-section-' . $section_id,
                __( $title, 'mobile-contact-bar' ),
                false,
                MOBILE_CONTACT_BAR__NAME
            );

            foreach( $section as $setting_id => $setting )
            {
                $args = array(
                    'section_id' => $section_id,
                    'setting_id' => $setting_id,
                    'setting'    => $setting,
                );

                if( isset( $setting['parent'] ))
                {
                     $args['class'] = 'hidden mcb-child mcb-parent-' . $section_id . '-' . $setting['parent'];
                }
                elseif( isset( $setting['trigger'] ))
                {
                    $args['class'] = 'mcb-parent mcb-parent-' . $section_id . '-' . $setting_id . ' mcb-trigger-' . $setting['trigger'];
                }

                add_settings_field(
                    $setting_id,
                    $setting['title'],
                    array( __CLASS__, 'callback_output_setting' ),
                    MOBILE_CONTACT_BAR__NAME,
                    'mcb-section-' . $section_id,
                    $args
                );
            }
        }



        // Contact List Section

        add_settings_section(
            'mcb-section-contacts',
            __( 'Contact List', 'mobile-contact-bar' ),
            false,
            MOBILE_CONTACT_BAR__NAME
        );

        foreach( self::$option['contacts'] as $contact_id => $contact )
        {
            $checked = ( $contact['checked'] )  ? ' mcb-active' : '';
            $odd     = ( $contact_id % 2 == 1 ) ? ' mcb-odd'    : '';

            $contact_args = array(
                'class'      => 'mcb-contact' . $checked . $odd,
                'contact_id' => $contact_id,
                'contact'    => $contact,
            );

            add_settings_field(
                $contact_id,
                self::output_contact_th( $contact_id, $contact ),
                array( __CLASS__, 'callback_render_contact_td' ),
                MOBILE_CONTACT_BAR__NAME,
                'mcb-section-contacts',
                $contact_args
            );

            if( isset( $contact['parameters'] ))
            {
                foreach( $contact['parameters'] as $parameter_id => $parameter )
                {
                    $parameter_args = array(
                        'class'        => 'mcb-parameter hidden',
                        'parameter_id' => $parameter_id,
                        'parameter'    => $parameter,
                        'contact_id'   => $contact_id,
                        'contact_type' => $contact['type'],
                    );

                    add_settings_field(
                        $contact_id . '-' . $parameter_id,
                        self::output_parameter_th( $parameter_id, $parameter['key'], $contact_id, $contact['type'] ),
                        array( __CLASS__, 'callback_render_parameter_td' ),
                        MOBILE_CONTACT_BAR__NAME,
                        'mcb-section-contacts',
                        $parameter_args
                    );
                }
            }
        }
    }



    /**
     * Renders a meta box.
     *
     * @since 1.2.0
     *
     * @param object $object  null
     * @param array  $section Passed from add_meta_box as sixth parameter.
     */
    public static function callback_render_section( $object, $section )
    {
        $table_id = str_replace( '-section-', '-table-', $section['id'] );

        $out_buttons = '';
        $out_icons   = '';
        if( 'mcb-table-contacts' == $table_id )
        {
            $buttons = apply_filters( 'mcb_admin_add_button', array() );
            ksort( $buttons );

            foreach( $buttons as $button )
            {
                $out_buttons .= sprintf(
                    '<li class="mcb-action wp-ui-text-highlight" data-contact-type="' . $button['type'] . '">
                        <span class="mcb-integration">%s</span>
                    </li>',
                    esc_html( $button['title'] )
                );
            }

            $icons = apply_filters( 'mcb_admin_add_icon', array() );
            ksort( $icons );

            foreach( $icons as $icon )
            {
                $out_icons .= sprintf(
                    '<li class="mcb-action wp-ui-text-highlight" data-contact-type="' . $icon['type'] . '">
                        <i class="mcb-integration %1$s fa-fw" title="%2$s" aria-hidden="true"></i>
                        <span class="screen-reader-text">%2$s</span>
                    </li>',
                    esc_attr( $icon['icon'] ),
                    esc_attr( $icon['title'] )
                );
            }
        }

        ?>
        <table id="<?php esc_attr_e( $table_id ); ?>" class="form-table">
            <?php if( 'mcb-table-contacts' == $table_id ) { ?>
            <thead>
                <tr>
                    <td>
                        <ul id="mcb-integration-buttons">
                            <?php echo $out_buttons; ?>
                        </ul>
                    </td>
                    <td>
                        <ul id="mcb-integration-icons">
                            <?php echo $out_icons; ?>
                        </ul>
                    </td>
                </tr>
            </thead>
            <tfoot>
                <tr>
                    <td colspan="2" class="wp-ui-text-highlight">
                        <i class="fas fa-share fa-fw fa-rotate-270" aria-hidden="true"></i>
                        <span><?php esc_html_e( 'Drag and drop to reorder', 'mobile-contact-bar' ); ?></span>
                    </td>
                </tr>
            </tfoot>
            <?php } ?>
            <tbody>
                <?php do_settings_fields( MOBILE_CONTACT_BAR__NAME, $section['id'] ); ?>
            </tbody>
        </table>
        <?php
    }



    /**
     * Outputs a setting field.
     *
     * @since 0.1.0
     *
     * @param array $args section_id, setting_id, setting
     */
    public static function callback_output_setting( $args )
    {
        switch( $args['setting']['type'] )
        {
            case 'color-picker':
                printf(
                    '<input type="text" id="mcb-%1$s-%2$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" class="cs-wp-color-picker" value="%3$s">',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] ),
                    esc_attr( self::$option['settings'][$args['section_id']][$args['setting_id']] )
                );
                break;

            case 'select':
                printf(
                    '<select id="mcb-%1$s-%2$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" class="mcb-regular-text">',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] )
                );
                foreach( $args['setting']['options'] as $value => $label )
                {
                    printf(
                        '<option value="%s" %s>%s</option>',
                        esc_attr( $value ),
                        selected( $value, self::$option['settings'][$args['section_id']][$args['setting_id']], false ),
                        esc_html( $label )
                    );
                }
                echo '</select>';
                break;

            case 'radio':
                printf( '<fieldset class="mcb-radio-label-wrap" id="mcb-%s-%s">',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] )
                );
                foreach( $args['setting']['options'] as $value => $label )
                {
                    printf(
                        '<label class="mcb-radio-label" for="mcb-%1$s-%2$s--%3$s">
                            <input type="radio" id="mcb-%1$s-%2$s--%3$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" value="%3$s" %4$s>%5$s
                        </label>',
                        esc_attr( $args['section_id'] ),
                        esc_attr( $args['setting_id'] ),
                        esc_attr( $value ),
                        checked( $value, self::$option['settings'][$args['section_id']][$args['setting_id']], false ),
                        esc_html( $label )
                    );
                }
                echo '</fieldset>';
                break;

            case 'radio-image':
                printf( '<fieldset class="mcb-radio-image-wrap" id="mcb-%s-%s">',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] )
                );
                foreach( $args['setting']['options'] as $value => $url )
                {
                    printf(
                        '<label class="mcb-radio-image">
                            <input type="radio" id="mcb-%1$s-%2$s--%3$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" value="%3$s" %4$s>
                            <img src="%5$s">
                        </label>',
                        esc_attr( $args['section_id'] ),
                        esc_attr( $args['setting_id'] ),
                        esc_attr( $value ),
                        checked( $value, self::$option['settings'][$args['section_id']][$args['setting_id']], false ),
                        plugins_url( $url, MOBILE_CONTACT_BAR__PATH )
                    );
                }
                echo '</fieldset>';
                break;

            case 'checkbox':
                printf(
                    '<label for="mcb-%1$s-%2$s">
                        <input type="checkbox" id="mcb-%1$s-%2$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" %3$s value="1">%4$s
                    </label>',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] ),
                    checked( self::$option['settings'][$args['section_id']][$args['setting_id']], 1, false ),
                    esc_html( $args['setting']['label'] )
                );
                break;

            case 'text':
                printf(
                    '<input type="text" id="mcb-%1$s-%2$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" class="mcb-regular-text" value="%3$s">',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] ),
                    esc_attr( self::$option['settings'][$args['section_id']][$args['setting_id']] )
                );
                break;

            case 'number':
                printf(
                    '<input type="number" id="mcb-%1$s-%2$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" class="mcb-regular-text" value="%3$d">
                    <span>%4$s</span>',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] ),
                    esc_attr( self::$option['settings'][$args['section_id']][$args['setting_id']] ),
                    esc_html( $args['setting']['postfix'] )
                );
                break;

            case 'slider':
                printf(
                    '<input type="range" id="mcb-%1$s-%2$s" name="' . MOBILE_CONTACT_BAR__NAME . '[settings][%1$s][%2$s]" class="mcb-slider-input" value="%3$s" min="%4$s" max="%5$s" step="%6$s">
                    <span class="mcb-slider-value">%3$s</span>',
                    esc_attr( $args['section_id'] ),
                    esc_attr( $args['setting_id'] ),
                    esc_attr( self::$option['settings'][$args['section_id']][$args['setting_id']] ),
                    esc_attr( $args['setting']['min'] ),
                    esc_attr( $args['setting']['max'] ),
                    esc_attr( $args['setting']['step'] )
                );
                break;
        }

        if( isset( $args['setting']['desc'] ))
        {
            printf( '<p class="description">%s</p>', esc_html( $args['setting']['desc'] ));
        }
    }



    /**
     * Renders the data part of the contact.
     *
     * @since 0.1.0
     *
     * @param array $args contact_id, contact
     */
    public static function callback_render_contact_td( $args )
    {
        $out = self::output_contact_td( $args );
        echo $out;
    }



    /**
     * Renders the header part of the contact.
     *
     * @since 2.0.0
     *
     * @param  array  $contact    Contact
     * @param  string $contact_id Contact id
     * @return string             HTML th
     */
    private static function output_contact_th( $contact_id, $contact )
    {
        $prefix = MOBILE_CONTACT_BAR__NAME . '[contacts][' . esc_attr( $contact_id ) . ']';
        $icon   = esc_attr( $contact['icon'] );

        $out = '';

        // 'type' hidden
        $out .= sprintf( '<input name="' . $prefix . '[type]" value="%s" type="hidden">', esc_attr( $contact['type'] ));

        // 'icon' hidden
        $out .= '<input name="' . $prefix . '[icon]" value="' . $icon . '" type="hidden">';

        // 'title' hidden
        if( 'Sample' == $contact['type'] )
        {
            $out .= sprintf( '<input name="' . $prefix . '[title]" value="%s" type="hidden">', esc_html( $contact['title'] ));
        }


        $out .= '<ul class="mcb-th">';

        // 'checkbox' input
        $out .= sprintf( '<li class="mcb-contact-checkbox"><input name="' . $prefix . '[checked]" value="1" %s type="checkbox"></li>',
            ( $contact['checked'] ) ? checked( $contact['checked'], 1, false ) : ''
        );

        // 'icon'
        $out .= '<li class="mcb-contact-icon ui-sortable-handle"><i class="' . $icon . ' fa-lg"></i></li>';

        // 'title'
        if( 'Sample' == $contact['type'] )
        {
            $out .= sprintf( '<li class="mcb-contact-title">%s</li>', esc_html( $contact['title'] ));
        }
        else
        {
            // 'title' input
            $out .= sprintf( '<li class="mcb-contact-title"><input name="' . $prefix . '[title]" value="%s" placeholder="%s" type="text"></li>',
                esc_html( $contact['title'] ),
                esc_attr__( 'Short name', 'mobile-contact-bar' )
            );
        }

        $out .= '</ul>';

        return $out;
    }



    /**
     * Renders the data part of the contact.
     *
     * @since 2.0.0
     *
     * @param  array  $args contact_id, contact
     * @return string       HTML td
     */
    private static function output_contact_td( $args )
    {
        $sanitized_uri = Mobile_Contact_Bar_Validator::escape_contact_uri( $args['contact']['uri'] );

        $out = '';

        // 'placeholder' hidden
        $out .= sprintf( '<input name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][placeholder]" value="%s" type="hidden">',
            esc_attr( $args['contact_id'] ),
            $args['contact']['placeholder']
        );

        $out .= '<ul class="mcb-td">';

        // 'URI' contact with empty placeholder has non-editable URI
        if( '' == $args['contact']['placeholder'] )
        {
            // 'URI' hidden
            $out .= sprintf( '<input name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][uri]" value="%s" type="hidden">',
                esc_attr( $args['contact_id'] ),
                $sanitized_uri
            );
            // 'URI'
            $out .= '<li class="mcb-contact-uri">' . $sanitized_uri . '</li>';
        }
        else
        {
            // 'URI' input
            $out .= sprintf(
                '<li class="mcb-contact-uri"><input name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][uri]" value="%s" placeholder="%s" type="text"></li>',
                    esc_attr( $args['contact_id'] ),
                    $sanitized_uri,
                    $args['contact']['placeholder']
            );
        }

        $out .= '<li>' . self::row_actions( 'contact', $args['contact']['type'] ) . '</li>';

        $out .= '</ul>';

        return $out;
    }



    /**
     * Renders the data part of the parameter.
     *
     * @since 1.2.0
     *
     * @param array $args parameter_id, parameter, contact_id, contact_type
     */
    public static function callback_render_parameter_td( $args )
    {
        $out = self::output_parameter_td( $args );
        echo $out;
    }



    /**
     * Outputs the header part of the parameter.
     *
     * @since 2.0.0
     *
     * @param  string $parameter_id
     * @param  string $parameter_key
     * @param  string $contact_id
     * @param  string $contact_type
     * @return string                HTML TH
     */
    private static function output_parameter_th( $parameter_id = '', $parameter_key = '', $contact_id = '', $contact_type = 'Custom' )
    {
        $out = '';

        if( 'Custom' == $contact_type )
        {
            // 'key' input
            $out .= sprintf(
                '<input name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][parameters][%s][key]" class="mcb-parameter-key" value="%s" placeholder="%s" type="text">',
                esc_attr( $contact_id ),
                esc_attr( $parameter_id ),
                esc_attr( $parameter_key ),
                esc_attr__( 'key', 'mobile-contact-bar' )
            );
        }
        else
        {
            // 'key' hidden
            $out .= sprintf(
                '<input name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][parameters][%s][key]" value="%s" type="hidden">',
                esc_attr( $contact_id ),
                esc_attr( $parameter_id ),
                esc_attr( $parameter_key )
            );
            // 'key'
            $out .= $parameter_key;
        }
        return $out;
    }



    /**
     * Outputs the data part of the parameter.
     *
     * @since 2.0.0
     *
     * @param  array  $args parameter_id, parameter, contact_id, contact_type
     * @return string       HTML td
     */
    private static function output_parameter_td( $args )
    {
        $out = '';

        // 'type' hidden
        $out .= sprintf(
            '<input name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][parameters][%s][type]" value="%s" type="hidden">',
            esc_attr( $args['contact_id'] ),
            esc_attr( $args['parameter_id'] ),
            esc_attr( $args['parameter']['type'] )
        );

        // 'placeholder' hidden
        $out .= sprintf(
            '<input name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][parameters][%s][placeholder]" value="%s" type="hidden">',
            esc_attr( $args['contact_id'] ),
            esc_attr( $args['parameter_id'] ),
            esc_attr( $args['parameter']['placeholder'] )
        );


        $out .= '<ul class="mcb-ul">';
        $out .= '<li class="mcb-parameter-value">';

        // 'value' input
        switch( $args['parameter']['type'] )
        {
            case 'text':
            case 'email':
                $out .= sprintf(
                    '<input type="text" name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][parameters][%s][value]" value="%s" placeholder="%s">',
                    esc_attr( $args['contact_id'] ),
                    esc_attr( $args['parameter_id'] ),
                    esc_attr( $args['parameter']['value'] ),
                    esc_attr( $args['parameter']['placeholder'] )
                );
                break;

            case 'textarea':
                $out .= sprintf(
                    '<textarea name="' . MOBILE_CONTACT_BAR__NAME . '[contacts][%s][parameters][%s][value]" placeholder="%s">%s</textarea>',
                    esc_attr( $args['contact_id'] ),
                    esc_attr( $args['parameter_id'] ),
                    esc_attr( $args['parameter']['placeholder'] ),
                    esc_textarea( $args['parameter']['value'] )
                );
                break;
        }

        $out .= '</li>';

        $out .= '<li>' . self::row_actions( 'parameter', $args['contact_type'] ) . '</li>';

        $out .= '</ul>';

        return $out;
    }



    /**
     * Sanitizes the option (settings and contacts).
     *
     * @since 0.1.0
     *
     * @param  array $input Multidimensional array of the option.
     * @return array        Sanitized option.
     */
    public static function callback_sanitize_option( $input )
    {
        // SETTINGS

        $settings           = $input['settings'];
        $sanitized_settings = array();

        foreach( $settings as $section_id => &$section )
        {
            // workaround empty checkboxes
            $section = array_replace(
                array_map( function( $setting ) { if( 'checkbox' == $setting['type'] ) { return 0; }}, self::$settings[$section_id] ),
                $section
            );
            // all settings will be saved, at least with their default values
            $section = array_replace(
                array_map( function( $setting ) { return $setting['default']; }, self::$settings[$section_id] ),
                $section
            );
        }
        unset( $section );



        // sanitize settings
        foreach( self::$settings as $section_id => $section )
        {
            foreach( $section as $setting_id => $setting )
            {
                $value = $settings[$section_id][$setting_id];

                switch( $setting['type'] )
                {
                    case 'select':
                    case 'radio':
                    case 'radio-image':
                        $value = ( in_array( $value, array_keys( $setting['options'] ))) ? $value : $setting['default'];
                        break;

                    case 'color-picker':
                        $value = self::sanitize_color( $value );

                        if( ! self::is_color( $value ))
                        {
                            $value = $setting['default'];
                        }
                        break;

                    case 'checkbox':
                        $value = (int) $value;
                        $value = ( 0 == $value || 1 == $value ) ? $value : $setting['default'];
                        break;

                    case 'number':
                        $value = (int) $value;
                        if( ( isset( $setting['min'] ) && $value < $setting['min'] ) || ( isset( $setting['max'] ) && $value > $setting['max'] ))
                        {
                            $value = $setting['default'];
                        }
                        break;

                    case 'text':
                        $value = sanitize_text_field( $value );
                        break;

                    case 'slider':
                        $value = self::sanitize_unit_interval( $value, $setting['min'], $setting['max'] );
                        break;
                }
                $sanitized_settings[$section_id][$setting_id] = $value;
            }
        }




        // CONTACTS

        $contacts           = $input['contacts'];
        $sanitized_contacts = array();

        $valid_contact_types = array();
        foreach( glob( plugin_dir_path( MOBILE_CONTACT_BAR__PATH ) . 'includes/contacts/class-*.php' ) as $path )
        {
            $valid_contact_types[] = substr( basename( $path, '.php' ), 6 );
        }

        foreach( $contacts as $contact_id => &$contact )
        {
            // remove contact if invalid 'icon', but leave empty icons
            if( $contact['icon'] && ! Mobile_Contact_Bar_Page::in_icons( $contact['icon'] ))
            {
                unset( $contacts[$contact_id] );
            }

            // remove contact if invalid 'type'
            if( ! in_array( $contact['type'], $valid_contact_types ))
            {
                unset( $contacts[$contact_id] );
            }

            // remove empty 'parameters'
            if( isset( $contact['parameters'] ) && ! $contact['parameters'] )
            {
                unset( $contacts[$contact_id]['parameters'] );
            }
            // reindex 'parameters'
            if( isset( $contact['parameters'] ) && $contact['parameters'] )
            {
                $contacts[$contact_id]['parameters'] = array_values( $contacts[$contact_id]['parameters'] );
            }
        }
        unset( $contact );


        // merge and sanitize contacts
        foreach( $contacts as $contact_id => $contact )
        {
            $sanitized_contact = array();

            // sanitize 'checked'
            $sanitized_contact['checked'] = (int) $contact['checked'];

            // 'type' is already sanitized
            $sanitized_contact['type'] = $contact['type'];

            // 'icon' is already sanitized
            $sanitized_contact['icon'] = $contact['icon'];

            // sanitize 'title'
            $sanitized_contact['title'] = sanitize_text_field( $contact['title'] );

            // copy 'placeholder'
            $sanitized_contact['placeholder'] = $contact['placeholder'];

            // sanitize 'uri'
            $sanitized_contact['uri'] = Mobile_Contact_Bar_Validator::sanitize_contact_uri( $contact['uri'] );

            // sanitize 'parameters'
            if( isset( $contact['parameters'] ))
            {
                foreach( $contact['parameters'] as $parameter_id => $parameter )
                {
                    // sanitize 'key'
                    $sanitized_contact['parameters'][$parameter_id]['key'] = sanitize_key( $parameter['key'] );

                    // sanitize 'type'
                    $sanitized_contact['parameters'][$parameter_id]['type'] = sanitize_key( $parameter['type'] );

                    // sanitize 'placeholder'
                    $sanitized_contact['parameters'][$parameter_id]['placeholder'] = sanitize_text_field( $parameter['placeholder'] );

                    // santitize 'value'
                    $sanitized_contact['parameters'][$parameter_id]['value'] = Mobile_Contact_Bar_Validator::sanitize_parameter_value( $parameter['value'], $sanitized_contact['parameters'][$parameter_id]['type'] );
                }
            }
            $sanitized_contacts[$contact_id] = $sanitized_contact;
        }

        // reindex
        $sanitized_contacts = array_values( $sanitized_contacts );


        self::$option['settings'] = $sanitized_settings;
        self::$option['contacts'] = $sanitized_contacts;

        return self::$option;
    }



    /**
     * Generates the public styles, and stores them in the option.
     *
     * @since 0.1.0
     *
     * @param  array $new_value The new value.
     * @param  array $old_value The old value.
     * @return array            The updated option.
     */
    public static function pre_update_option( $new_value, $old_value = array() )
    {
        $bar    = $new_value['settings']['bar'];
        $icons  = $new_value['settings']['icons'];
        $toggle = $new_value['settings']['toggle'];
        $badges = ( isset( $new_value['settings']['badges'] )) ? $new_value['settings']['badges'] : null;

        $contacts = array_filter( $new_value['contacts'], function( $contact ) { return $contact['checked']; });
        $contacts_count = count( $contacts );


        $styles = '';


        $styles .= '#mobile-contact-bar{';
        $styles .=     '-webkit-box-sizing:border-box;';
        $styles .=             'box-sizing:border-box;';
        $styles .=     'display:block;';
        $styles .=     'font-size:100%;';
        $styles .=     'font-size:1rem;';
        $styles .=     'opacity:' . $bar['opacity'] . ';';
        $styles .=     'position:relative;';
        $styles .=     'width:' . $bar['width'] . '%;';
        $styles .=     'z-index:9998;';
        $styles .= '}';


        $styles .= '#mobile-contact-bar:before,';
        $styles .= '#mobile-contact-bar:after{';
        $styles .=     'content:"";';
        $styles .=     'display:table;';
        $styles .= '}';


        $styles .= '#mobile-contact-bar:after{';
        $styles .=     'clear:both;';
        $styles .= '}';


        $styles .= '#mobile-contact-bar-outer{';
        $styles .=     'background-color:' . $bar['color'] . ';';
        $styles .=     '-webkit-box-sizing:border-box;';
        $styles .=             'box-sizing:border-box;';
        switch( $bar['is_border'] )
        {
            case 'one':
                switch( $bar['vertical_position'] )
                {
                    case 'top':
                        $styles .= 'border-bottom:' . $bar['border_width'] . 'px solid ' . $bar['border_color'] . ';';
                        break;

                    case 'bottom':
                        $styles .= 'border-top:' . $bar['border_width'] . 'px solid ' . $bar['border_color'] . ';';
                        break;
                }
                break;

            case 'two':
                $styles .= 'border-top:' . $bar['border_width'] . 'px solid ' . $bar['border_color'] . ';';
                $styles .= 'border-bottom:' . $bar['border_width'] . 'px solid ' . $bar['border_color'] . ';';
                break;
        }
        $styles .=     'height:' . $bar['height'] . 'px;';
        $styles .=     'overflow:hidden;';
        $styles .=     'width:100%;';
        $styles .= '}';


        $styles .= '#mobile-contact-bar ul{';
        $styles .=     '-webkit-box-sizing:border-box;';
        $styles .=             'box-sizing:border-box;';
        $styles .=     'line-height:0;';
        $styles .=     'list-style-type:none;';
        $styles .=     'margin:0;';
        $styles .=     'padding:0;';
        $styles .=     'position:relative;';
        $styles .=     'text-align:center;';
        $styles .=     'width:100%;';
        $styles .= '}';


        $styles .= '#mobile-contact-bar ul li{';
        $styles .=     '-webkit-box-sizing:border-box;';
        $styles .=             'box-sizing:border-box;';
        $styles .=     'display:inline-block;';
        $styles .=     'height:' . $bar['height'] . 'px;';
        $styles .=     'text-align:center;';
        switch( $bar['is_border'] )
        {
            case 'one':
                $styles .= 'height:' . ( $bar['height'] - $bar['border_width'] ) . 'px;';
                break;

            case 'two':
                $styles .= 'height:' . ( $bar['height'] - 2 * $bar['border_width'] ) . 'px;';
                break;

            case 'none':
                $styles .= 'height:' . $bar['height'] . 'px;';
                break;
        }
        switch( $icons['alignment'] )
        {
            case 'centered':
                $styles .= 'width:' . $icons['width'] . 'px;';
                break;

            case 'justified':
                $styles .= 'width:' . ( 100 / $contacts_count ) . '%;';
                break;
        }
        $styles .= '}';


        switch( $icons['is_border'] )
        {
            case 'two':
                $styles .= '#mobile-contact-bar ul li{';
                $styles .=     'border-left:' . $icons['border_width'] . 'px solid ' . $icons['border_color'] . ';';
                $styles .= '}';


                $styles .= '#mobile-contact-bar ul li:last-child{';
                $styles .=     'border-right:' . $icons['border_width'] . 'px solid ' . $icons['border_color'] . ';';
                $styles .= '}';
                break;

            case 'four':
                $styles .= '#mobile-contact-bar ul li{';
                $styles .=     'border-top:' . $icons['border_width'] . 'px solid ' . $icons['border_color'] . ';';
                $styles .=     'border-bottom:' . $icons['border_width'] . 'px solid ' . $icons['border_color'] . ';';
                $styles .=     'border-left:' . $icons['border_width'] . 'px solid ' . $icons['border_color'] . ';';
                $styles .= '}';


                $styles .= '#mobile-contact-bar ul li:last-child{';
                $styles .=     'border-right:' . $icons['border_width'] . 'px solid ' . $icons['border_color'] . ';';
                $styles .= '}';
                break;
        }


        $styles .= '#mobile-contact-bar ul li a{';
        $styles .=     'color:' . $icons['color'] . ';';
        $styles .=     'cursor:pointer;';
        $styles .=     'display: block;';
        $styles .=     'height:100%;';
        $styles .=     'position:relative;';
        $styles .=     'z-index:9998;';
        $styles .= '}';


        $styles .= '#mobile-contact-bar ul li a:active,';
        $styles .= '#mobile-contact-bar ul li a:focus{';
        $styles .=     'outline:none;';
        $styles .= '}';

        $styles .= '.fa-stack{';
        $styles .=     'height:2em;';
        $styles .=     'line-height:2em;';
        $styles .=     'width:2em;';
        $styles .=     'position:relative;';
        $styles .=     'top:50%;';
        $styles .=     '-webkit-transform:translateY(-50%);';
        $styles .=         '-ms-transform:translateY(-50%);';
        $styles .=             'transform:translateY(-50%);';
        $styles .= '}';


        if( $toggle['is_render'] && $bar['is_fixed'] )
        {
            $styles .= '#mobile-contact-bar-toggle-checkbox:checked ~ #mobile-contact-bar-outer{';
            $styles .=     'height:0;';
            $styles .= '}';


            $styles .= '#mobile-contact-bar-toggle-checkbox{';
            $styles .=     'display:none;';
            $styles .=     'position:absolute;';
            $styles .= '}';


            $styles .= '#mobile-contact-bar-toggle{';
            $styles .=     'cursor:pointer;';
            $styles .=     'display:table;';
            $styles .=     'line-height:0;';
            $styles .=     'margin:0 auto;';
            $styles .=     'position:relative;';
            $styles .=     'z-index:2;';
            $styles .= '}';


            $styles .= '#mobile-contact-bar-toggle span{';
            $styles .=     'display:inline-block;';
            $styles .=     'color:' . $icons['color'] . ';';
            $styles .=     'font-size:' . $toggle['size'] . 'rem;';
            $styles .=     'position:absolute;';
            $styles .=     'bottom:50%;';
            $styles .=     'left:50%;';
            $styles .=     '-webkit-transform:translate(-50%);';
            $styles .=         '-ms-transform:translate(-50%);';
            $styles .=             'transform:translate(-50%);';
            $styles .=     'text-align:center;';
            $styles .=     'width:100%;';
            $styles .=     'z-index:2;';
            $styles .= '}';


            $styles .= '#mobile-contact-bar-toggle svg{';
            $styles .=     'display:inline-block;';
            $styles .=     'pointer-events:none;';
            $styles .=     'fill:' . $toggle['color'] . ';';
            $styles .=     'z-index:1;';
            $styles .= '}';


            if( $toggle['is_animation'] )
            {
                $styles .= '#mobile-contact-bar-outer{';
                $styles .=     '-webkit-transition:height 1s ease;';
                $styles .=     '-moz-transition:height 1s ease;';
                $styles .=     '-o-transition:height 1s ease;';
                $styles .=     'transition:height 1s ease;';
                $styles .= '}';
            }
        } // endif is_toggle


        if( $badges )
        {
            $styles .= '.mobile-contact-bar-badge{';
            $styles .=     'background-color:' . $badges['background_color'] . ';';
            $styles .=     'border-radius:100%;';
            $styles .=     'color:' . $badges['font_color'] . ';';
            $styles .=     'display:block;';
            $styles .=     'font-size:' . $badges['size'] . 'em;';
            $styles .=     'height:1.5em;';
            $styles .=     'width:1.5em;';
            $styles .=     'line-height:1.5;';
            $styles .=     'position:absolute;';
            switch( $badges['place'] )
            {
                case 'top-right':
                    $styles .= 'top:0;';
                    $styles .= 'right:0;';
                    break;

                case 'bottom-right':
                    $styles .= 'bottom:0;';
                    $styles .= 'right:0;';
                    break;

                case 'bottom-left':
                    $styles .= 'bottom:0;';
                    $styles .= 'left:0;';

                    break;
                case 'top-left':
                    $styles .= 'top:0;';
                    $styles .= 'left:0;';
                    break;
            }
            $styles .=     'text-indent:0;';
            $styles .= '}';

        }


        //  bottom
        //  Fixed
        if( 'bottom' == $bar['vertical_position'] && $bar['is_fixed'] )
        {
            if( $bar['placeholder_height'] > 0 )
            {
                $styles .= 'body{';
                $styles .=     'border-bottom:' . $bar['placeholder_height'] . 'px solid ' . $bar['placeholder_color'] . '!important;';
                $styles .= '}';
            }

            $styles .= '#mobile-contact-bar{';
            $styles .=     'position:fixed;';
            $styles .=     'left:0;';
            $styles .=     ( $bar['space_height'] > 0 ) ? 'bottom:' . $bar['space_height'] . 'px;' : 'bottom:0;';
            $styles .= '}';
        }


        //  top
        //  fixed
        if( 'top' == $bar['vertical_position'] && $bar['is_fixed'] )
        {
            if( $bar['placeholder_height'] > 0 )
            {
                $styles .= 'body{';
                $styles .=     'border-top:' . $bar['placeholder_height'] . 'px solid ' . $bar['placeholder_color'] . '!important;';
                $styles .= '}';
            }

                $styles .= '#mobile-contact-bar{';
                $styles .=     'position:fixed;';
                $styles .=     'left:0;';
                $styles .=     ( $bar['space_height'] > 0 ) ? 'top:' . $bar['space_height'] . 'px;' : 'top:0;';
                $styles .= '}';

            if( $toggle['is_render'] )
            {
                $styles .= '#mobile-contact-bar-toggle{';
                $styles .=     'position:absolute;';
                $styles .=     'bottom:-34px;';
                $styles .=     'left:50%;';
                $styles .=     'transform:translateX(-50%);';
                $styles .= '}';
            }
        }


        //  bottom
        //  not fixed
        if( 'bottom' == $bar['vertical_position'] && ! $bar['is_fixed'] )
        {
            if( $bar['placeholder_height'] > 0 )
            {
                $styles .= 'body{';
                $styles .=     'border-bottom:' . $bar['placeholder_height'] . 'px solid ' . $bar['placeholder_color'] . '!important;';
                $styles .= '}';
            }

            $styles .= '#mobile-contact-bar{';
            $styles .=     'margin-top:-' . $bar['height'] . 'px;';
            $styles .=     'position:relative;';
            $styles .=     'left:0;';
            if( $bar['placeholder_height'] > 0 ){
                $styles .= ( $bar['space_height'] > 0 ) ? 'bottom:' . ( $bar['space_height'] - $bar['placeholder_height'] ) . 'px;' : 'bottom:-' . $bar['placeholder_height'] . 'px;';
            }
            else
            {
                 $styles .= ( $bar['space_height'] > 0 ) ? 'bottom:' . $bar['space_height'] . 'px;' : 'bottom:0;';
            }
            $styles .= '}';
        }


        //  top
        //  not fixed
        if( 'top' == $bar['vertical_position'] && ! $bar['is_fixed'] )
        {
            if( $bar['placeholder_height'] > 0 )
            {
                $styles .= 'body{';
                $styles .=     'border-top:' . $bar['placeholder_height'] . 'px solid ' . $bar['placeholder_color'] . '!important;';
                $styles .= '}';
            }

            $styles .= '#mobile-contact-bar{';
            $styles .=     'position:absolute;';
            $styles .=     'left:0;';
            $styles .=     ( $bar['space_height'] > 0 ) ? 'top:' . $bar['space_height'] . 'px;' : 'top:0;';
            $styles .= '}';
        }

        if( $bar['width'] < 100 )
        {
            switch( $bar['horizontal_position'] )
            {
                case 'center':
                    $styles .= '#mobile-contact-bar{';
                    $styles .=     'left:50%;';
                    $styles .=     '-webkit-transform:translateX(-50%);';
                    $styles .=         '-ms-transform:translateX(-50%);';
                    $styles .=             'transform:translateX(-50%);';
                    $styles .= '}';
                    break;
                case 'right':
                    $styles .= '#mobile-contact-bar{';
                    $styles .=     'left:100%;';
                    $styles .=     '-webkit-transform:translateX(-100%);';
                    $styles .=         '-ms-transform:translateX(-100%);';
                    $styles .=             'transform:translateX(-100%);';
                    $styles .= '}';
                    break;
            }
        }


        $new_value['styles'] = $styles;

        return $new_value;
    }



    /**
     * Renders templates.
     *
     * @since 2.0.0
     */
    public static function admin_footer()
    {
        ?>

        <script type="text/html" id="mcb-tmpl-icon-picker">
            <div id="mcb-icon-picker-container">
                <div class="icon-picker-control">
                    <a data-direction="back" href="#">
                        <i class="fas fa-angle-left fa-lg"></i>
                    </a>
                    <input class="" placeholder="<?php esc_attr_e( 'Search', 'mobile-contact-bar' ); ?>" type="text">
                    <a data-direction="forward" href="#">
                        <i class="fas fa-angle-right fa-lg"></i>
                    </a>
                </div>
                <ul class="icon-picker-list">
                    <?php
                    $icons = Mobile_Contact_Bar_Page::icons();
                    foreach( $icons as $icon_id => $section ) :
                        foreach( $section as $icon ) :
                            $title = $icon_id . ' fa-' . $icon; ?>
                            <li data-icon="<?php echo $icon; ?>">
                                <a href="#" title="<?php echo $icon; ?>">
                                    <i class="<?php echo $title; ?>"></i>
                                </a>
                            </li>
                        <?php endforeach;
                    endforeach; ?>
                </ul>
            </div>
        </script>



        <script type="text/html" id="mcb-tmpl-parameter">
            <tr class="mcb-parameter">
                <th scope="row">
                    <?php echo self::output_parameter_th(); ?>
                </th>
                <td>
                    <?php echo self::output_parameter_td( array(
                        'parameter_id' => '',
                        'parameter'    => array(
                            'key'         => '',
                            'type'        => 'text',
                            'placeholder' => 'value',
                            'value'       => '',
                        ),
                        'contact_id'   => '',
                        'contact_type' => 'Custom',
                    )); ?>
                </td>
            </tr>
        </script>

        <?php
    }



    /**
     * Renders a contact and its parameters.
     *
     * @since 2.0.0
     *
     * @uses $_POST
     */
    public static function ajax_add_contact()
    {
        if( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], MOBILE_CONTACT_BAR__NAME ))
        {
            exit;
        }
        if( ! isset( $_POST['contact_type'] ) || ! $_POST['contact_type'] )
        {
            exit;
        }

        $contacts = apply_filters( 'mcb_admin_add_contact', array() );
        $contact  = self::mcb_array_search( 'type', $_POST['contact_type'], $contacts );

        if( $contact )
        {
            $data = null;
            $out  = '';

            $out .= '<tr class="mcb-contact">';
                $out .= '<th scope="row">';
                    $out .= self::output_contact_th( '', $contact );
                $out .= '</th>';
                $out .= '<td>';
                    $out .= self::output_contact_td( array( 'contact_id' => '', 'contact' => $contact, ));
                $out .= '</td>';
            $out .= '</tr>';

            $data['contact'] = $out;


            if( isset( $contact['parameters'] ) && $contact['parameters'] )
            {
                $parameters = array();

                foreach( $contact['parameters'] as $parameter_id => $parameter )
                {
                    $out = '';

                    $out .= '<tr class="mcb-parameter hidden">';
                        $out .= '<th scope="row">';
                            $out .= self::output_parameter_th( $parameter_id, $parameter['key'], '', $contact['type'] );
                        $out .= '</th>';
                        $out .= '<td>';
                            $out .= self::output_parameter_td( array(
                                'parameter_id' => $parameter_id,
                                'parameter'    => $parameter,
                                'contact_id'   => '',
                                'contact_type' => $contact['type'],
                            ));
                        $out .= '</td>';
                    $out .= '</tr>';

                    $parameters[] = $out;
                }
                $data['parameters'] = $parameters;
            }

            $response = json_encode( $data );

            echo $response;
            exit;
        }
        exit;
    }



    /**
     * Outputs the action icons for a contact or a parameter.
     *
     * @since 2.0.0
     *
     * @param  string $row_type 'contact' or 'parameter'
     * @return string           HTML Unordered list of actions.
     */
    private static function row_actions( $row_type, $type )
    {
        $actions = '';

        $actions .= '<ul class="mcb-row-icons">';

        switch( $row_type )
        {
            case 'contact':
                $actions .= sprintf(
                    '<li class="mcb-action mcb-row-toggle-parameters mcb-invisible">
                        <i class="fas fa-caret-down fa-fw" aria-expanded="false" aria-hidden="true" title="%1$s"></i>
                        <span class="screen-reader-text">%1$s</span>
                    </li>',
                    esc_attr__( 'Show query string parameters', 'mobile-contact-bar' )
                );

                $actions .= sprintf(
                    '<li class="mcb-action mcb-row-pick-icon">
                        <i class="fab fa-font-awesome-flag fa-fw" aria-hidden="true" title="%1$s"></i>
                        <span class="screen-reader-text">%1$s</span>
                    </li>',
                    esc_attr__( 'Select a Font Awesome icon', 'mobile-contact-bar' )
                );

                $actions .= sprintf(
                    '<li class="mcb-action mcb-row-add-parameter %1$s">
                        <i class="fas fa-plus fa-fw" aria-hidden="true" title="%2$s"></i>
                        <span class="screen-reader-text">%2$s</span>
                    </li>',
                    ( 'Custom' == $type ) ? '' : 'mcb-invisible',
                    esc_attr__( 'Add a query string parameter', 'mobile-contact-bar' )
                );
                $actions .= sprintf(
                    '<li class="mcb-action mcb-row-delete-contact">
                        <i class="fas fa-times fa-fw" aria-hidden="true" title="%1$s"></i>
                        <span class="screen-reader-text">%1$s</span>
                    </li>',
                    esc_attr__( 'Delete this contact', 'mobile-contact-bar' )
                );
                break;

            case 'parameter':
                $actions .= sprintf(
                    '<li class="mcb-action mcb-row-delete-parameter %1$s">
                        <i class="fas fa-times fa-fw" aria-hidden="true" title="%2$s"></i>
                        <span class="screen-reader-text">%2$s</span>
                    </li>',
                    ( 'Custom' == $type ) ? '' : 'mcb-invisible',
                    esc_attr__( 'Delete this parameter', 'mobile-contact-bar' )
                );
                break;
        }
        $actions .= '</ul>';

        return $actions;
    }



    /**
     * Verifies that a color code is valid.
     *
     * @since 2.0.0
     *
     * @param  string      $color Color code (Hex or RGBA).
     * @return bool|string        Either false or the valid color code.
     */
    private static function is_color( $color )
    {
        $color = self::sanitize_color( $color );

        if( $color )
        {
            return $color;
        }

        return false;
    }



    /**
     * Sanitizes color code.
     *
     * @since 2.0.0
     *
     * @param  string $color Color code (Hex or RGBA).
     * @return string        Filtered color code.
     */
    private static function sanitize_color( $color )
    {
       $new_color = self::sanitize_hex_color( $color );

       if( ! $new_color )
       {
           $new_color = self::sanitize_rgba_color( $color );
       }

       return $new_color;
    }



    /**
     * Sanitizes hexadecimal color code.
     *
     * @since 0.1.0
     *
     * @param  string $hex_color Color code.
     * @return string            Filtererd color code.
     *
     * @see https://developer.wordpress.org/reference/functions/sanitize_hex_color/
     */
    private static function sanitize_hex_color( $hex_color )
    {
        if( preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $hex_color ))
        {
            return $hex_color;
        }

        return '';
    }



    /**
     * Sanitizes RGBA color code.
     *
     * @since 0.1.0
     *
     * @param  string $rgba_color Color code.
     * @return string             Filtererd color code.
     */
    private static function sanitize_rgba_color( $rgba_color )
    {
        if( preg_match('/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/i', $rgba_color ))
        {
            return $rgba_color;
        }

        return '';
    }



    /**
     * Sanitizes float value of [0,1] closed interval.
     *
     * @since 0.1.0
     *
     * @param  string    $float Float number.
     * @param  int       $min   Interval min value.
     * @param  int       $max   Interval max value.
     * @return float|int        Sanitized float number or min value.
     */
    private static function sanitize_unit_interval( $float, $min, $max )
    {
        if( preg_match('/^' . $min . '$|^' . $max . '$|^' . $min . '\.\d{1,2}$/', $float ))
        {
            return (float) $float;
        }

        return $min;
    }



    /**
     * Finds item by key and value - multidimensional array search.
     *
     * @since 2.0.0
     *
     * @param  string     $key   Item key.
     * @param  string     $value Item value at that key.
     * @param  array      $array Multidimensional array.
     * @return array|bool        Item or false.
     */
    private static function mcb_array_search( $key, $value, $array )
    {
        foreach( $array as $id => $item )
        {
            if( $item[$key] == $value )
            {
                return $item;
            }
        }
        return false;
    }

}
