<?php
/**
 * Editor settings for the DrezSync Booking field.
 *
 * Adds the booking rules (Calendar ID, open days, hours, slot length) to the
 * field's settings panel in the Gravity Forms editor, and the JavaScript that
 * loads and saves those values onto the field. These hooks only run in the
 * editor.
 *
 * @package DrezSync
 */

defined( 'ABSPATH' ) || exit;

/**
 * Registers the custom field settings UI and its editor JavaScript.
 */
class DrezSync_Field_Settings {

	/**
	 * Constructor.
	 */
	public function __construct() {
		add_action( 'gform_field_standard_settings', array( $this, 'settings' ), 10, 2 );
		add_action( 'gform_editor_js', array( $this, 'editor_js' ) );
		add_filter( 'gform_noconflict_scripts', array( $this, 'allow_noconflict_script' ) );
	}

	/**
	 * Keep the editor script alive when Gravity Forms no-conflict mode is on.
	 *
	 * This one is not cosmetic. No-conflict mode strips unrecognised scripts from
	 * the form editor, and the form editor is exactly where this script runs. Lose
	 * it and the whole booking field settings panel stops working: no stored values
	 * load, the day pills do nothing, and the colour picker cannot save.
	 *
	 * Needed from 1.6.8. The script used to be echoed as a raw block, which
	 * no-conflict mode cannot strip because it only dequeues registered handles.
	 * Moving to the script API for the WordPress.org review is what created the
	 * exposure.
	 *
	 * @param array $scripts Handles Gravity Forms will allow through.
	 * @return array
	 */
	public function allow_noconflict_script( $scripts ) {
		$scripts[] = 'drezsync-field-settings';
		return $scripts;
	}

	/**
	 * Output the custom settings markup in the field settings panel.
	 *
	 * @param int $position Current settings position.
	 * @param int $form_id  Form ID.
	 */
	public function settings( $position, $form_id ) {
		if ( 25 !== $position ) {
			return;
		}
		?>
<?php ?>

<?php ?>
		<li class="drezsync_calendar_setting drezsync-setting field_setting">
			<label for="drezsync_calendar" class="section_label"><?php esc_html_e( 'Google Calendar ID', 'drezsync' ); ?></label>
			<input type="text" id="drezsync_calendar" class="fieldwidth-3"
				onkeyup="SetFieldProperty('drezsyncCalendar', this.value);"
				onchange="SetFieldProperty('drezsyncCalendar', this.value);" />
		</li>

<?php ?>
		<li class="drezsync_days_setting drezsync-setting field_setting">
			<label class="section_label"><?php esc_html_e( 'Open days', 'drezsync' ); ?></label>
			<div id="drezsync-days">
				<?php
				$days = array(
					__( 'Mon', 'drezsync' ) => 1,
					__( 'Tue', 'drezsync' ) => 2,
					__( 'Wed', 'drezsync' ) => 3,
					__( 'Thu', 'drezsync' ) => 4,
					__( 'Fri', 'drezsync' ) => 5,
					__( 'Sat', 'drezsync' ) => 6,
					__( 'Sun', 'drezsync' ) => 0,
				);
				$pill = 'display:inline-block;margin:0 6px 6px 0;padding:5px 11px;border:1px solid #c3c4c7;border-radius:16px;cursor:pointer;font-size:12px;line-height:1.4;background:#fff;color:#1d2327;user-select:none;';
				foreach ( $days as $label => $num ) {
					echo '<span class="drezsync-day-pill" data-day="' . esc_attr( $num ) . '" style="' . esc_attr( $pill ) . '" onclick="drezsyncToggleDay(this);">' . esc_html( $label ) . '</span>';
				}
				?>
			</div>
		</li>

		<li class="drezsync_hours_setting drezsync-setting field_setting">
			<label class="section_label"><?php esc_html_e( 'Hours', 'drezsync' ); ?></label>
			<input type="time" id="drezsync_start" onchange="SetFieldProperty('drezsyncStart', this.value);" />
			<span style="margin:0 6px;"><?php esc_html_e( 'to', 'drezsync' ); ?></span>
			<input type="time" id="drezsync_end" onchange="SetFieldProperty('drezsyncEnd', this.value);" />
			<?php
			$tz = wp_timezone_string();
			if ( false !== strpos( $tz, '/' ) ) {
				echo '<p style="color:#646970;font-size:12px;margin:6px 0 0;">';
				printf(
					/* translators: %s: site timezone name. */
					esc_html__( 'Times use your site timezone: %s. Make sure your Google Calendar uses the same zone.', 'drezsync' ),
					'<strong>' . esc_html( $tz ) . '</strong>'
				);
				echo '</p>';
			} else {
				echo '<p style="color:#8a5200;font-size:12px;margin:6px 0 0;">';
				esc_html_e( 'Your WordPress timezone is a fixed offset, not a city. For bookings and daylight saving to stay correct, set Settings > General > Timezone to your city, matching your Google Calendar.', 'drezsync' );
				echo '</p>';
			}
			?>
		</li>

		<li class="drezsync_slot_setting drezsync-setting field_setting">
			<label for="drezsync_slot" class="section_label"><?php esc_html_e( 'Slot length', 'drezsync' ); ?></label>
			<select id="drezsync_slot" onchange="SetFieldProperty('drezsyncSlot', this.value);">
				<option value="15"><?php esc_html_e( '15 minutes', 'drezsync' ); ?></option>
				<option value="20"><?php esc_html_e( '20 minutes', 'drezsync' ); ?></option>
				<option value="30"><?php esc_html_e( '30 minutes', 'drezsync' ); ?></option>
				<option value="60"><?php esc_html_e( '60 minutes', 'drezsync' ); ?></option>
			</select>
		</li>

<?php ?>

<?php ?>
<?php ?>

		<li class="drezsync_color_setting drezsync-setting field_setting">
			<label for="drezsync_color" class="section_label"><?php esc_html_e( 'Accent color', 'drezsync' ); ?></label>
			<input type="color" id="drezsync_color" oninput="drezsyncColorChanged( this.value, false );" onchange="drezsyncColorChanged( this.value, true );" style="width:60px;height:32px;padding:2px;vertical-align:middle;" />
			<p style="color:#646970;font-size:12px;margin:4px 0 0;"><?php esc_html_e( 'The color of the selected time. Set it to match your brand.', 'drezsync' ); ?></p>
		</li>

<?php ?>
		<?php
	}

	/**
	 * Output the editor JavaScript that shows and binds the settings.
	 */
	public function editor_js() {
		/*
		 * WordPress.org asks that JavaScript reach the page through the script
		 * API rather than a raw script tag. This hook fires only on the form
		 * editor, so it stays our page check. The handle is registered with no
		 * source purely to carry the inline script; because we own it, the
		 * script can never fail to attach the way it could against a Gravity
		 * Forms handle whose name we would be guessing at.
		 *
		 * It now prints in the footer, later than the old inline block. That is
		 * safe: every line below either binds an event or defines a function,
		 * and fieldSettings is not read until a field is clicked.
		 */
		wp_register_script( 'drezsync-field-settings', false, array( 'jquery' ), DREZSYNC_VERSION, true );
		wp_enqueue_script( 'drezsync-field-settings' );
		wp_add_inline_script( 'drezsync-field-settings', $this->editor_script() );
	}

	/**
	 * The form editor script.
	 *
	 * A nowdoc, so PHP leaves the JavaScript alone — it uses $ both for jQuery
	 * objects and inside a regular expression. The pro markers sit here exactly
	 * as they did inline, so the build script still strips Pro code out of the
	 * free zip.
	 *
	 * @return string
	 */
	private function editor_script() {
		return <<<'JS'
			// Show our settings only for the DrezSync Booking field.
			if ( typeof fieldSettings !== 'undefined' && fieldSettings.drezsync_booking ) {
				fieldSettings.drezsync_booking += ', .drezsync-setting';
			}

			// The booking field currently open in the settings panel. A form can hold
			// more than one, so the accent repaint needs to know which one to touch.
			var drezsyncEditingId = null;

			// Load the stored values when a field is selected in the editor.
			jQuery( document ).on( 'gform_load_field_settings', function( event, field ) {
					// Fires for every field. Bail on anything not ours, or our show/hide leaks into other panels.
					if ( ! field || 'drezsync_booking' !== field.type ) {
						jQuery( '.drezsync-setting' ).hide();
						drezsyncEditingId = null;
						return;
					}
				drezsyncEditingId = field.id;
				jQuery( '#drezsync_calendar' ).val( field.drezsyncCalendar || '' );

				jQuery( '#drezsync_start' ).val( field.drezsyncStart || '09:00' );
				jQuery( '#drezsync_end' ).val( field.drezsyncEnd || '17:00' );
				jQuery( '#drezsync_slot' ).val( field.drezsyncSlot || '30' );
				jQuery( '#drezsync_color' ).val( field.drezsyncColor || '#ff4e43' );

				var days = field.drezsyncDays || [ 1, 2, 3, 4, 5 ];
				jQuery( '.drezsync-day-pill' ).each( function() {
					var d = parseInt( jQuery( this ).data( 'day' ), 10 );
					jQuery( this ).toggleClass( 'is-selected', days.indexOf( d ) !== -1 );
					drezsyncPaintDay( this );
				} );
			} );

			// Save the accent colour and repaint the preview. Without the repaint the
			// editor keeps whatever colour the field was last rendered with, so the
			// setting looks like it did nothing until the form is saved and reloaded.
			// Dragging in the picker only repaints; the value is stored on commit.
			function drezsyncColorChanged( val, save ) {
				if ( save ) {
					SetFieldProperty( 'drezsyncColor', val );
				}
				drezsyncPaintAccent( val );
			}

			// Paint the accent onto the selected time pill in the field preview.
			// Only time-slot bookings show an accent, so range mode finds no pill
			// and this quietly does nothing.
			function drezsyncPaintAccent( color ) {
				if ( null === drezsyncEditingId ) {
					return;
				}
				if ( ! /^#[0-9a-fA-F]{6}$/.test( color ) ) {
					color = '#ff4e43';
				}
				var $wrap = jQuery( '#field_' + drezsyncEditingId ).find( '.drezsync-booking' );
				$wrap.attr( 'data-color', color );
				$wrap.find( '.drezsync-booking__time-pill.is-selected' ).css( {
					'background': color,
					'border-color': color,
					'color': '#fff'
				} );
			}

			// Colour a day pill based on whether it is selected.
			function drezsyncPaintDay( el ) {
				var on = jQuery( el ).hasClass( 'is-selected' );
				jQuery( el ).css( {
					'background': on ? '#2271b1' : '#fff',
					'border-color': on ? '#2271b1' : '#c3c4c7',
					'color': on ? '#fff' : '#1d2327'
				} );
			}

			// Toggle a day pill, then save the open days as an array of weekday numbers.
			function drezsyncToggleDay( el ) {
				jQuery( el ).toggleClass( 'is-selected' );
				drezsyncPaintDay( el );
				var days = [];
				jQuery( '.drezsync-day-pill.is-selected' ).each( function() {
					days.push( parseInt( jQuery( this ).data( 'day' ), 10 ) );
				} );
				SetFieldProperty( 'drezsyncDays', days );
			}


			// Fill the choice-field picker with the form's drop down, radio, and checkbox fields.
JS;
	}

}
