<?php
/**
 * Contacter
 * Voice feedback form for your website for saving and transcribing user voice messages to text.
 * Exclusively on https://1.envato.market/contacter
 *
 * @encoding        UTF-8
 * @version         1.7.8
 * @copyright       (C) 2018 - 2023 Merkulove ( https://merkulov.design/ ). All rights reserved.
 * @license         Envato License https://1.envato.market/KYbje
 * @contributors    Dmitry Merkulov (dmitry@merkulov.design)
 * @support         help@merkulov.design
 **/

namespace Merkulove\Contacter;

use Merkulove\Contacter\Unity\Plugin;
use Merkulove\Contacter\Unity\Settings;
use Merkulove\Contacter\Unity\TabAssignments;
use Merkulove\Contacter\Unity\UI;

/** Includes the autoloader for libraries installed with Composer. */
require Plugin::get_path() . 'vendor/autoload.php';

use Google\ApiCore\ApiException;
use Google\Cloud\Speech\V1\RecognitionAudio;
use Google\Cloud\Speech\V1\RecognitionConfig;
use Google\Cloud\Speech\V1\RecognitionConfig\AudioEncoding;
use Google\Cloud\Speech\V1\SpeechClient;

/** Exit if accessed directly. */
if ( ! defined( 'ABSPATH' ) ) {
	header( 'Status: 403 Forbidden' );
	header( 'HTTP/1.1 403 Forbidden' );
	exit;
}

/**
 * SINGLETON: Caster class contain main plugin logic.
 *
 * @since 1.0.0
 *
 **/
final class Caster {

	/**
	 * The one true Caster.
	 *
     * @since 1.0.0
     * @access private
	 * @var Caster
	 **/
	private static $instance;

	/**
	 * @var string - Error message after processing AJAX recording form
	 */
	private static $form_error_message;

    /**
     * Set up the plugin.
     *
     * @since 1.0.0
     * @access public
     *
     * @return void
     **/
    public function setup() {

        /** Define hooks that run on both the front-end and the dashboard. */
        $this->both_hooks();

        /** Define public hooks. */
        $this->public_hooks();

        /** Define admin hooks. */
        $this->admin_hooks();

	    /** Create directory for saving records */
	    ContacterRecord::create_directory();

    }

	/**
	 * Add admin menu for plugin settings.
	 *
	 * @since 1.0.0
	 * @access public
	 **/
	public function add_admin_menu() {

		add_submenu_page(
			'edit.php?post_type=' . ContacterRecord::POST_TYPE,
			'Contacter ' . esc_html__( 'Settings', 'contacter' ),
			esc_html__( 'Settings', 'contacter' ),
			'manage_options',
			'mdp_contacter_settings',
			[Settings::get_instance(), 'options_page']
		);

	}

	/**
	 * Remove admin menu added from Unity.
	 *
	 * @since 1.0.0
	 * @access private
	 *
	 * @return void
	 **/
	public function remove_admin_menu() {

		/** Remove menu item by slug. */
		remove_menu_page( 'mdp_contacter_settings' );

	}

    /**
     * Define hooks that run on both the front-end and the dashboard.
     *
     * @since 1.0.0
     * @access private
     *
     * @return void
     **/
    private function both_hooks() {

        /** Country name filter */
        add_filter( 'contacter_country', [ $this,'country_filter' ] );

	    /** Register contacter_form and contacter_record post types. */
	    CustomPost::get_instance();

	    /** Adds all the necessary shortcodes. */
	    Shortcodes::get_instance();

	    /** Allow svg uploading. */
	    SvgHelper::get_instance();

	    /** Add AJAX callback. */
	    add_action( 'wp_ajax_contacter_send', [$this, 'contacter_send'] );
	    add_action( 'wp_ajax_nopriv_contacter_send', [$this, 'contacter_send'] );

	    /** Enable Custom Notifications and Alerts. */
	    $this->enable_notifications();

    }

    public function country_filter( $country ) {

        if( 'Russia' === $country ) {

            return $country . '(' . esc_html__( 'terrorist state', 'contacter' ) . ')';

        }

        return $country;

    }

	/**
	 * Process AJAX requests from frontend.
	 *
	 * @since 1.0.0
	 * @access public
	 **/
	public function contacter_send() {

		/** Verifies the Ajax request to prevent processing requests external of the blog. */
		check_ajax_referer( 'contacter-nonce', 'nonce' );

		/** Exit if no data to process. */
		if ( empty( $_POST ) ) { wp_die(); }

		/** Get custom  */
		$record_name = $_POST[ 'contacter-record' ] ?? false;

		/** Get Contacter Form ID. */
		$cForm_id =  filter_input( INPUT_POST, 'cform-id', FILTER_SANITIZE_NUMBER_INT );

		/** Remove service variables. */
		unset( $_POST['action'], $_POST['nonce'], $_POST['cform-id'] );

		/** Save Audio file. */
		$audio_file_path = $this->save_audio_file( $cForm_id, $record_name );

        /** Make visible in Media Library */
        $options = Settings::get_instance()->options;
        if ( $options[ 'media_library' ] === 'on' ) {

	        Attachment::get_instance()->create_attachment( $record_name, $cForm_id );

        }

		/** Replace audio file by attached file */
		if ( ! $audio_file_path ) {

			$audio_file_path = $this->save_form_file( $cForm_id, true );

		}

		/** Save Attachments file. */
		if ( count( $_FILES ) > 0 ) {

			$form_file_paths = $this->save_form_file( $cForm_id );

		} else {

			$form_file_paths = false;

		}

		/** Create post with record or return error message */
		$post_id = false;
        if ( empty( self::$form_error_message ) ) {

			/** Create contacter_record record. */
			$post_id = $this->create_record( $cForm_id, $audio_file_path, $form_file_paths );

            /** Save post meta */
            add_post_meta( $post_id, 'contacter_ip', $_POST['ip'] ?? '' );
            add_post_meta( $post_id, 'contacter_country', $_POST['country'] ?? '' );
            add_post_meta( $post_id, 'contacter_country_name', $_POST['country_name'] ?? '' );
            add_post_meta( $post_id, 'contacter_city', $_POST['city'] ?? '' );

			/** Fire event to send email notification. */
			do_action( 'contacter_record_added', $post_id );

		}

        /** Return response to the front-end */
        if ( $post_id ) {

            $response = array(
	            'status' => true,
            );

            if ( $options[ 'show_record_id' ] === 'on' ) {
	            $response[ 'postID' ] = apply_filters( 'contacter_record_id__frontend', $post_id );
            }

	        /** Returns success message */
	        echo json_encode( $response );

        } else {

	        /** Returns error message */
	        echo json_encode( array(
		        'status' => false,
		        'message' => apply_filters( 'contacter_form_error_message', self::$form_error_message )
	        ) );

        }

		wp_die();

	}

	/**
	 * Create contacter_record record.
	 *
	 * @param int $cForm_id - ID of Contacter Form.
	 * @param $audio_file_path - Full path to audio file.
	 *
	 * @since  1.0.0
	 * @access public
	 * @return int - post_id
	 **/
	private function create_record( $cForm_id, $audio_file_path, $attach_file_path )
	{

		/** Contacter Form. */
		$cForm = get_post( $cForm_id );

		/** @var string name for record name suffix $field_name */
		$field_name = get_post_meta( $cForm_id, 'mdp_field_record_suffix', true );

		/** Prepare Additional fields. */
		$fields_fb = get_post_meta( $cForm_id, 'mdp_additional_fields_fb', true );
		$fields_fb = json_decode( $fields_fb, true ); // Array with fields params.

		$additional_fields = [];
		$record_name_suffix = '';
		foreach ( $_POST as $key => $value ) {

			foreach ( $fields_fb as $field ) {

				if ( $key === $field[ 'name' ] ) {

					$additional_fields[$key] = [
						'label' => $field[ 'label' ],
						'value' => $value
					];

					if ( $key === $field_name ) {
						$record_name_suffix = ' > ' . esc_html__( $value );
					}

				}

			}

		}

		/** */
		$additional_fields = json_encode( $additional_fields );
		$additional_fields = wp_slash( $additional_fields ); // Added to support أحدث المقالات

		/** Create record. */
		$post_id = wp_insert_post( [
			'post_type'     => 'contacter_record',
			'post_title'    => 'Record: ' . $cForm->post_title . $record_name_suffix,
			'post_status'   => 'pending',
		] );

		/** Fill meta fields. */
		if ( $post_id ) {

			/** Save audio file. */
			if ( $audio_file_path ) {

				update_post_meta( $post_id, 'mdp_contacter_audio', wp_slash( $audio_file_path ) );

				/** Save audio sample rate. */
				$sample_rate = filter_input(INPUT_POST, 'mdp-contacter-audio-sample-rate', FILTER_SANITIZE_STRING );
				update_post_meta( $post_id, 'mdp_contacter_audio_sample_rate', $sample_rate );

			}

			/** Save attachments paths */
			if ( $attach_file_path ) {

				update_post_meta( $post_id, 'mdp_attachments', $attach_file_path );

			}

			/** Save Contacter Form ID. */
			update_post_meta( $post_id, 'mdp_cform_id', $cForm_id );

			/** Save Additional fields. */
			update_post_meta( $post_id, 'mdp_additional_fields', $additional_fields );

		}

		return $post_id;

	}

	/**
	 * Save recorded audio file.
	 *
	 * @param int $cForm_id - ID of Contacter Form.
	 *
	 * @since  1.0.0
	 * @access public
	 * @return string
	 **/
	private function save_audio_file( $cForm_id, $record_name = false ) {

		if ( empty( $_FILES['mdp-contacter-audio'] ) ) { return false; }

		/** Create file name for audio file. */
		$file_path = $this->prepare_audio_name( $cForm_id, $record_name );

		/** Check file mime type. */
		$mime = mime_content_type( $_FILES['mdp-contacter-audio']['tmp_name'] );

		/** Looks like uploading some shit. */
		if ( ! in_array( $mime, [ 'audio/wav', 'audio/x-wav' ] ) ) {

			/** Remove temporary audio file. */
			wp_delete_file( $_FILES['mdp-contacter-audio']['tmp_name'] );

			wp_die();
		}

		/** Save audio file. */
		file_put_contents( $file_path, file_get_contents( $_FILES['mdp-contacter-audio']['tmp_name'] ), FILE_APPEND );

		/** Remove temporary audio file. */
		wp_delete_file( $_FILES['mdp-contacter-audio']['tmp_name'] );

		return $file_path;

	}

	/**
	 * Save attached files
	 *
	 * @param int $cForm_id - ID of Contacter Form.
	 *
	 * @return array|false
	 */
	private function save_form_file( $cForm_id, $replaceAudioRecord = false ) {

		if ( empty( $_FILES )/* || count( $_FILES ) === 1*/ ) { return false; } // Exit if no attachemts

		$file_paths = array();
		$attachedAudio = false;

		// Process each attachment file
		foreach( $_FILES as $key => $file_meta ) {

            if ( $file_meta['error'] > 0 || $file_meta[ 'size' ] <= 0 ) { continue; } // Skip in file input in not required and not filled
			if ( 'mdp-contacter-audio' === $key ) { continue; } // Dont process contacter record

			// Check file mime type
			$file_mime = mime_content_type( $file_meta[ 'tmp_name' ] );
			$file_size = filesize( $file_meta[ 'tmp_name' ] );

			if ( ! $file_mime || ! $file_size ) { continue; } // Dont process empty files

			// Get all after last dot in the file name
			$array          = explode( '.', $file_meta['name'] );
			$file_extension = array_pop( $array );

			// Limits
			$limit_size = get_post_meta( $cForm_id, 'mdp_form_file_size', true );
			$limit_mime = explode( ',', get_post_meta( $cForm_id, 'mdp_form_mime', true ) );
			$limit_mime = array_map( 'trim', $limit_mime ); // Trim sapces in the array value

			// Remove file id mime type is incorrect
			if ( ! in_array( $file_mime, $limit_mime ) && $limit_mime[ 0 ] !== '*' ) {

				wp_delete_file(  $file_meta[ 'tmp_name' ] );
				self::$form_error_message =
					esc_html__( 'The type of the attached file', 'contacter' ) .
					'(' . esc_attr__( $file_meta[ 'name' ] ) . ') ' .
					esc_html__( 'does not match the allowed types.', 'contacter' );
				continue;

			}

			// Remove file id file size larger than file size limit
			if ( intval( $limit_size ) * 1024 < $file_size ) {

				wp_delete_file(  $file_meta[ 'tmp_name' ] );
				self::$form_error_message =
					esc_html__( 'The size of the attached file', 'contacter' ) .
					'(' . esc_attr__( $file_meta[ 'name' ] ) . ') ' .
					esc_html__( 'does not match the allowed size limit.', 'contacter' );
				continue;

			}

			// Replace voice record by uploaded file if voice record is missing or disabled
			if ( $replaceAudioRecord ) {

				if ( strpos( $file_mime, 'audio' ) >= 0 ) {

					$attachedAudio = $this->prepare_file_name( $cForm_id, $key, $file_extension, $file_meta[ 'name' ] );
					break;

				} else {

					continue;

				}

			}

			// Create file name for file
			$file_path = $this->prepare_file_name( $cForm_id, $key, $file_extension, $file_meta[ 'name' ] );

			// Save file
			file_put_contents( $file_path, file_get_contents( $file_meta[ 'tmp_name' ] ), FILE_APPEND );

			// Remove temporary audio file
			wp_delete_file( $file_meta[ 'tmp_name' ] );

			// Store attachment path to array
			$file_paths[ $key ] = $file_path;

		}

		if ( $replaceAudioRecord ) {

			if ( $attachedAudio ) {

				return $attachedAudio;

			} else {

				return false;

			}

		} else {

			return $file_paths;

		}

	}

	/**
	 * Prepare unique file name for wav audio file.
	 *
	 * @param int $cForm_id - ID of Contacter Form.
	 *
	 * @since  1.0.0
	 * @access public
	 * @return string
	 **/
	private function prepare_audio_name( $cForm_id, $record_name = false ) {

		/** Prepare File name. */
		$upload_dir     = wp_get_upload_dir();
		$upload_basedir = $upload_dir['basedir'] . '/contacter/'; // Path to upload folder.

		$unique_counter = 0;
		$file_name = $this->build_file_name( $cForm_id, $unique_counter, $record_name );

		/** We do not need collisions. */
		$f_path = $upload_basedir . $file_name;
		if ( file_exists( $f_path ) ) {

			do {
				$unique_counter++;
				$file_name = $this->build_file_name( $cForm_id, $unique_counter, $record_name );
				$f_path = $upload_basedir . $file_name;
			} while ( file_exists( $f_path ) );

		}

		$f_path = wp_normalize_path( $f_path );
		return str_replace( ['/', '\\'], DIRECTORY_SEPARATOR, $f_path );

	}

	/**
	 * Prepare unique attachment file name.
	 *
	 * @param int $cForm_id - ID of Contacter Form.
	 * @param string $key - Form field key(id)
	 * @param string $ext - File extension
	 *
	 * @return string|string[]
	 */
	private function prepare_file_name( $cForm_id, $key, $ext, $name ) {

		/** Prepare File name. */
		$upload_dir     = wp_get_upload_dir();
		$upload_basedir = $upload_dir[ 'basedir' ] . '/contacter/'; // Path to upload folder

		$unique_counter = 0;
		$file_name = $this->build_attachment_name( $cForm_id, $key, $ext, $unique_counter, $name );

		/** We do not need collisions. */
		$f_path = $upload_basedir . $file_name;
		if ( file_exists( $f_path ) ) {

			do {
				$unique_counter++;
				$file_name = $this->build_attachment_name( $cForm_id, $key, $ext, $unique_counter, $name );
				$f_path = $upload_basedir . $file_name;
			} while ( file_exists( $f_path ) );

		}

		$f_path = wp_normalize_path( $f_path );
		$f_path = str_replace( ['/', '\\'], DIRECTORY_SEPARATOR, $f_path );

		return $f_path;

	}

	/**
	 * Build file name.
	 *
	 * @param int $cForm_id - ID of Contacter Form.
	 * @param $counter
	 * @param bool $record_name
	 *
	 * @return string
	 * @since  1.0.0
	 * @access public
	 */
	public function build_file_name( $cForm_id, $counter, $record_name = false ) {

		$options = Settings::get_instance()->options;

        if ( $options[ 'file_name' ] === 'on' && $options[ 'file_name_pattern' ] !== '' ) {

	        $pattern = $options[ 'file_name_pattern' ];
            $pattern = str_replace( '[form-id]', $cForm_id, $pattern );
	        $pattern = str_replace( '[date]', gmdate( 'Y-m-d' ), $pattern );
	        $pattern = str_replace( '[time]', gmdate( 'H:i:s' ), $pattern );
            $base = $pattern;

        } else {

	        $base = ! $record_name ?
		        'contacter-' . $cForm_id . '-' . gmdate( 'Y-m-d-H:i:s' ) : 'contacter-' . str_replace( ' ', '-', $record_name );

        }

		$index = $counter > 0 ? '-' . $counter : '';

		return $base . $index . '.wav';

	}

	/**
	 * Build the form attachment file name
	 *
	 * @param int $cForm_id - ID of Contacter Form.
	 * @param $key - Form field key
	 * @param $ext - Uploaded file extension
	 * @param $counter
	 * @param bool $name
	 *
	 * @return string - File name with extension
	 */
	private function build_attachment_name( $cForm_id, $key, $ext, $counter, $name = false ) {

		$original = get_post_meta( $cForm_id, 'mdp_form_file_original', true );
		$use_original = $original === 'on';

		$base = ! $use_original || ! $name ?
			'-' . $cForm_id . '-' . gmdate( 'Y-m-d\TH:i:s\Z' ) . '-' . $key :
			'-' . str_replace( '.' . $ext, '', $name );

		$index = $counter > 0 ? '-' . $counter : '';

		return 'contacter' . $base . $index . '.' . $ext;

	}

    /**
     * Register all the hooks related to the public-facing functionality.
     *
     * @since 1.0.0
     * @access private
     *
     * @return void
     **/
    private function public_hooks() {

        /** Work only on frontend area. */
        if ( is_admin() ) { return; }

	    /** CSS and JS */
	    Assets::get_instance()->public_assets();

	    /** Show Float button. */
	    if ( 'on' === Settings::get_instance()->options['show_fbutton'] ) {
		    add_action( 'wp_footer', [ $this, 'render_fbutton' ] );
	    }

    }

	/**
	 * Render Float button.
	 *
	 * @since 1.0.0
	 * @access public
	 **/
	public function render_fbutton( $content = '' ) {

		/** Checks if float button should work on this page. */
		if ( ! TabAssignments::get_instance()->display() ) { return $content; }

		/** Only one float button on page */
		if ( str_contains( $content, 'mdp-contacter-fbutton-box' ) ) {
			return $content;
		}

		/** Get Plugin Settings. */
		$options = Settings::get_instance()->options;
		$options['fbutton_c_form'] = str_replace( 'ID-', '', $options['fbutton_c_form'] );

		/** Form not exist or not publish => exit. */
		if ( 'publish' !== get_post_status( $options['fbutton_c_form'] ) ) { return $content; }

		/** Prepare Classes. */
		$classes = ' mdp-contacter-fbutton-box ';
		$classes .= ' ' . $options['fbutton_position'] . ' '; // Float Button Position.
		$classes .= ' mdp-entrance-' . $options['fbutton_animation'] . ' '; // Entrance Animation.
		$classes .= ' mdp-hover-' . $options['fbutton_hover_animation'] . ' '; // Float Button Hover Animation.
		$classes = trim( $classes );

		/** Button caption */
		$fbutton_caption = wp_kses_post( $options[ 'fbutton_caption' ] );

		?>
		<!-- Start Contacter WordPress Plugin -->
		<div class="<?php esc_attr_e( $classes ); ?>" style="display: none;">
			<button
				id="mdp-contacter-fbutton"
				class="mdp-icon-position-<?php esc_attr_e( $options['fbutton_icon_position'] ); ?>"
				aria-label="<?php esc_html_e( 'Send Voice Message', 'contacter' ); ?>">
				<?php if ( $options['fbutton_icon_position'] !== 'none' ) : ?>
				<span class="mdp-contacter-fbutton-icon"><?php $this->inline_svg_e( $options['fbutton_icon'] ); ?></span>
				<?php endif; ?>

				<?php if ( $options['fbutton_caption'] ) : ?>
				<span class="mdp-contacter-fbutton-caption"><?php esc_html_e( $fbutton_caption, 'contacter' ); ?></span>
				<?php endif; ?>
			</button>
		</div>
		<div class="mdp-contacter-fbutton-overlay mdp-contacter-modal-animation-<?php esc_attr_e( $options['modal_animation'] ); ?>">
			<div class="mdp-contacter-modal-wrap">
				<div class="mdp-contacter-fbutton-modal" role="dialog" aria-modal="true" aria-labelledby="mdp-contacter-fbutton">
					<button class="mdp-contacter-close" aria-label="<?php esc_html_e( 'Close', 'contacter' ); ?>"></button>
					<?php echo do_shortcode( '[contacter id="' . $options['fbutton_c_form'] . '"]' ); ?>
				</div>
			</div>
		</div>
		<!-- End Contacter WordPress Plugin -->
		<?php

		return $content;

	}

	public function inline_svg_e( $icon ) {

		/** If this users custom svg. */
		if ( is_numeric( $icon ) ) {
			$icon = get_attached_file( $icon );

        /** If icon from library. */
		} else {
			$icon = Plugin::get_path() . 'images/mdc-icons/' . $icon;
		}

		if ( ! is_file( $icon ) ) { return ''; }

		$svg_icon = file_get_contents( $icon );

		/** Escaping SVG with KSES. */
		$kses_defaults = wp_kses_allowed_html( 'post' );

		$svg_args = [
			'svg'   => [
				'class' => true,
				'aria-hidden' => true,
				'aria-labelledby' => true,
				'role' => true,
				'xmlns' => true,
				'width' => true,
				'height' => true,
				'viewbox' => true, // <= Must be lower case!
			],
			'g'     => [ 'fill' => true ],
			'title' => [ 'title' => true ],
			'path'  => [ 'd' => true, 'fill' => true, ],
		];

		$allowed_tags = array_merge( $kses_defaults, $svg_args );

		echo wp_kses( $svg_icon, $allowed_tags );

	}

    /**
     * Register all the hooks related to the admin area functionality.
     *
     * @since 1.0.0
     * @access private
     *
     * @return void
     **/
    private function admin_hooks() {

        /** Work only in admin area. */
        if ( ! is_admin() ) { return; }

	    /** Load JS and CSS for Backend Area. */
	    Assets::get_instance()->admin_assets();

	    /** Add AJAX callback for transcription generation. */
	    add_action( 'wp_ajax_gtrans', [ $this, 'google_translate'] );

	    /** Remove admin menu added from Unity. */
	    add_action( 'admin_menu', [ $this, 'remove_admin_menu' ], 1000 );
	    add_action( 'admin_menu', [ $this, 'add_admin_menu' ], 1001 );

	    /** Show admin warning, if we need API Key. */
	    $speech_recognition = 'on' === Settings::get_instance()->options['speech_recognition'];
	    $dnd_api_key = Settings::get_instance()->options['dnd-api-key'];
	    if ( $speech_recognition && ! $dnd_api_key ) {
		    add_action( 'admin_notices', [ $this, 'api_key_notice' ] );
	    }

        /** Show admin warnings if no one form found */
	    add_action( 'admin_footer', [ $this, 'message_no_forms' ] );

    }

	/**
	 * Generate transcription.
	 *
	 * @throws ApiException
	 * @since  1.0.0
	 * @access public
	 **/
	public function google_translate() {

		/** Verifies the Ajax request to prevent processing requests external of the blog. */
		check_ajax_referer( 'admin-contacter-nonce', 'security' );

		/** Exit if no data to process. */
		if ( empty( $_POST ) ) {
			echo json_encode([
				'status' => 'err',
				'message' => 'empty( $_POST ).'
			]);
			wp_die();
		}

		/** Remove service variables. */
		unset( $_POST['action'], $_POST['nonce'] );

		/** Get Contacter Record ID. */
		$record_id =  filter_input(INPUT_POST, 'record_id', FILTER_SANITIZE_NUMBER_INT );
		if ( ! $record_id ) {
			echo json_encode([
				'status' => 'err',
				'message' => 'Contacter Record ID not found.'
			]);
			wp_die();
		}

		/** Get Audio path value from meta if it's exist. */
		$audio_path = get_post_meta( $record_id, 'mdp_contacter_audio', true );
		if ( empty( $audio_path ) ) {
			echo json_encode([
				'status' => 'err',
				'message' => 'Audio path is empty.'
			]);
			wp_die();
		}

		/** Get contents of a file into a string. */
		$content = file_get_contents( $audio_path );

		/** Set string as audio content. */
		$audio = ( new RecognitionAudio() )->setContent( $content );

		/** Automatic punctuation. */
		$punctuation = false;
		if ( 'on' === Settings::get_instance()->options['punctuation'] ) {
			$punctuation = true;
		}

		/** Get Audio Sample Rate. */
		$sample_rate = get_post_meta( $record_id, 'mdp_contacter_audio_sample_rate', true );
		if ( ! $sample_rate || $sample_rate === 'undefined'  ) {
			$sample_rate = Settings::get_instance()->options['sample_rate'];
		}

		/** The audio file's encoding, sample rate and language. */
		$config = new RecognitionConfig([
			'encoding' => AudioEncoding::LINEAR16,
			'sample_rate_hertz' => $sample_rate,
			'language_code' => Settings::get_instance()->options['language'],
			'enable_automatic_punctuation' => $punctuation,
		]);

		/** Instantiates a client. */
		$client = new SpeechClient();

		/** Detects speech in the audio file. */
		$response = $client->recognize( $config, $audio );

		/** Print most likely transcription. */
		$transcript = '';
		foreach ( $response->getResults() as $result ) {
			$alternatives = $result->getAlternatives();
			$mostLikely = $alternatives[0];
			$transcript = $mostLikely->getTranscript();
		}

		if ( $transcript ) {
			echo json_encode([
				'status' => 'ok',
				'transcription' => $this->mb_ucfirst( $transcript )
			]);
		} else {
			echo json_encode([
				'status' => 'ok',
				'transcription' => ''
			]);
		}

		$client->close();

		wp_die();
	}

	/**
	 * ucfirst() function for multibyte character encodings
	 *
	 * @param string $string - String to uppercase first letter.
	 *
	 * @since  1.0.0
	 * @access private
	 *
	 * @return string
	 **/
	private function mb_ucfirst( $string ) {

		return mb_strtoupper( mb_substr( $string, 0, 1 ) ).mb_strtolower( mb_substr( $string, 1 ) );

	}

	/**
	 * Enable Custom Notifications and Alerts.
	 *
	 * @since  1.0.5
	 * @access public
	 **/
	private function enable_notifications() {

		/** Bundle Notification plugin. */
		require_once Plugin::get_path() . '/src/notification/load.php';

		$this->notification_whitelabel( [
			'page_hook'       => 'edit.php?post_type=contacter_record',
			'extensions'      => false, // Hide extensions page.
			'settings'        => false, // Hide settings page.
		] );

	}

	/**
	 * Sets the Notification plugin in white label mode.
	 *
	 * Args you can use:
	 * - 'page_hook' => 'edit.php?post_type=page' // to move the Notifications under specific admin page
	 *
	 * @since 1.0.5
	 * @param array $args white label args.
	 * @return void
	 **/
	private function notification_whitelabel( $args = [] ) {

		add_filter( 'notification/whitelabel', '__return_true' );

		/** Change Notification CPT page. */
		if ( isset( $args['page_hook'] ) && ! empty( $args['page_hook'] ) ) {

			add_filter( 'notification/whitelabel/cpt/parent', static function( $hook ) use ( $args ) {

				return $args['page_hook'];

			} );

		}

		/** Remove extensions. */
		if ( isset( $args['extensions'] ) && false === $args['extensions'] ) {

			add_filter( 'notification/whitelabel/extensions', '__return_false' );

		}

		/** Remove settings. */
		if ( isset( $args['settings'] ) && false === $args['settings'] ) {

			add_filter( 'notification/whitelabel/settings', '__return_false' );

		}

		/** Settings access. */
		if ( isset( $args['settings_access'] ) ) {

			add_filter( 'notification/whitelabel/settings/access', static function( $access ) use ( $args ) {

				return (array) $args['settings_access'];

			} );

		}

	}

	/**
	 * Show admin warning, if we need API Key.
	 *
	 * @since 1.0.0
	 * @access public
	 * @return void
	 **/
	public function api_key_notice() {

		/** Get current screen. */
		$screen = get_current_screen();
		if ( null === $screen ) { return; }

		/** Contacter Settings Page. */
		if ( Plugin::get_menu_bases() == $screen->base  ) {

			/** Render "Before you start" message. */
			UI::get_instance()->render_snackbar(
				esc_html__( 'This plugin uses the Google Cloud Speech-to-Text API Key File. Set up your Google Cloud Platform project before the start.', 'contacter' ),
				'warning', // Type
				-1, // Timeout
				true, // Is Closable
				[ [ 'caption' => 'Get Key File', 'link' => 'https://docs.merkulov.design/how-to-get-key-file-for-the-contacter-wordpress-plugins' ] ] // Buttons
			);

		}

	}

	/**
	 * This method used in register_activation_hook
	 * Everything written here will be done during plugin activation.
	 *
	 * @since 1.0.0
	 * @access public
	 */
	public function activation_hook() {

		/** Create Default Form. */
		ContacterForm::get_instance()->create_default_form();

	}

	/**
     * Show message if no one forms founded
	 * @return void
	 */
    public function message_no_forms() {

	    $forms = Config::get_instance()->get_forms();
        if ( count( $forms ) ) { return; }

	    /** Get current screen. */
	    $screen = get_current_screen();
	    if ( null === $screen ) { return; }

	    if ( in_array( $screen->base, Plugin::get_menu_bases() )  ) {

		    UI::get_instance()->render_snackbar(
			    esc_html__( 'No forms found. First create a Contacter Form', 'contacter' ),
			    'warning', // Type
			    -1, // Timeout
			    true, // Is Closable
			    [ [ 'caption' => 'Create a form', 'link' => esc_attr__( admin_url( '/post-new.php?post_type=contacter_form' ) ) ] ] // Buttons
		    );

	    } else { ?>

            <div class="settings-error notice notice-warning">
                <p><strong><?php esc_html_e( 'Contacter: ', 'contacter' ); ?></strong></p>
                <?php echo wp_sprintf(
                    '<p>%s <a href="%s">%s</a> %s</p>',
	                esc_html__('No forms found. First create a', 'contacter' ),
                    esc_attr__( admin_url( '/post-new.php?post_type=contacter_form' ) ),
	                esc_html__('Contacter Form', 'contacter' ),
	                esc_html__('by this link.', 'contacter' )
                ) ?>
            </div><?php

	    }

    }

	/**
	 * Main Caster Instance.
	 * Insures that only one instance of Caster exists in memory at any one time.
	 *
	 * @static
     * @since 1.0.0
     * @access public
     *
	 * @return Caster
	 **/
	public static function get_instance() {

		if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {

			self::$instance = new self;

		}

		return self::$instance;

	}

}
