<?php
/*
 * Plugin Name: WP Sudoku Plus
 * Plugin URI:
 * Description: Simple Sudoku
 * Author: opajaap
 * Author URI:
 * Text Domain: wp-sudoku-plus
 * Version: 2.2
 * License: GPLv2
*/

if ( ! defined( 'ABSPATH' ) ) exit;

global $wpdb;

define( 'WP_SUDOKU', $wpdb->prefix . 'wp_sudoku' );

function wpsud_activate() {
global $wpdb;

	$table = $wpdb->prefix . 'wp_sudoku';
	$wp_sudoku = 	"CREATE TABLE $table (
						id bigint(20) NOT NULL,
						data tinytext NOT NULL,
						rating smallint(5) NOT NULL,
						won bigint(20) NOT NULL default 0,
						lost bigint(20) NOT NULL default 0,
						PRIMARY KEY  (id)
					) DEFAULT CHARACTER SET utf8;";

	require_once ABSPATH . 'wp-admin/includes/upgrade.php';

	dbDelta( $wp_sudoku );
	if ( ! wp_next_scheduled( 'wpsud_cron_event' ) ) {
		wp_schedule_single_event( time() + 1, 'wpsud_cron_event', array(), true );
	}
}

add_action( 'wpsud_cron_event', 'wpsud_fill_db' );

function wpsud_fill_db() {
global $wpdb;

	// Do they need us?
	$count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku" );

	if ( $count >= 200000 ) {
		return;
	}

	// Init wp filesystem
	require_once ( ABSPATH . '/wp-admin/includes/file.php' );
	global $wp_filesystem;

	if ( ! $wp_filesystem )	{
		WP_Filesystem();
	}

	$dataarr 	= $wp_filesystem->get_contents_array( dirname(__FILE__) . '/data.txt' );
	$ratsarr 	= $wp_filesystem->get_contents_array( dirname(__FILE__) . '/rating.txt' );

	$start_time = time();
	$avail_time = ini_get( 'max_execution_time' );

	$margin 	= 15;
	$end_time 	= $start_time + $avail_time - $margin;
	$idx 		= get_option( 'wp-sudoku-data-count', '0' );

	while ( $idx < 200000 && time() < $end_time ) {

		$raw 	= $dataarr[$idx];
		$id 	= ltrim( substr( $raw, 0, 7 ), '0' );
		$data 	= substr( $raw, 8, 81 );
		$rat 	= $ratsarr[$idx];
		$rating = substr( $rat, 8, 1 );
		$wpdb->query( $wpdb->prepare( "INSERT INTO {$wpdb->prefix}wp_sudoku ( `id`, `data`, `rating` ) VALUES ( %d, %s, %d )", $id, $data, $rating ) );

		$idx++;

		// Update statistics
		if ( $idx % 1000 == 0 ) {

			for ( $rat = 1; $rat < 8; $rat++ ) {
				$ratarr[$rat] = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku
																 WHERE rating = %d", $rat ) );
			}

			update_option( 'wp-sudoku-rating-counts', $ratarr );
			update_option( 'wp-sudoku-data-count', $id );
		}
	}

	if ( $id < 200000 && ! wp_next_scheduled( 'wpsud_cron_event' ) ) {
		$bret = wp_schedule_single_event( time() + 60, 'wpsud_cron_event', array(), true );
		exit();
	}
}

register_activation_hook( __FILE__, 'wpsud_activate' );

function wpsud_deactivate() {
global $wpdb;

	$wpdb->query( "DROP TABLE {$wpdb->prefix}wp_sudoku" );
	delete_option( 'wp-sudoku-data-count' );
	delete_option( 'wp-sudoku-rating-counts' );
}

register_deactivation_hook( __FILE__, 'wpsud_deactivate' );

function wpsud_main( $size = '36' ) {
global $wpdb;
global $wpsud_puzzle_js;
global $wpsud_sc_width;

	$avail_puzzles = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku" );
	if ( !$avail_puzzles ) {
		echo 'Please wait a moment for the puzzles to get loaded, reload the page after a minute or so';
		return;
	}

	// Just to be sure...
	if ( ! defined( 'DOING_AJAX' ) ) {
		if ( get_option( 'wp-sudoku-data-count', '0' ) < '200000' ) {
			if ( ! wp_next_scheduled( 'wpsud_cron_event' ) ) {
				wp_schedule_single_event( time() + 1, 'wpsud_cron_event', array(), true );
			}
		}
	}

	$size = floor( intval( strval( intval( $size ) ) ) );

	$s = $size ? $size : 16;
	if ( $s < 8 ) $s = 8;
	$si = 3 * $s + 2;
	$sb = 3 * $si + 2;
	$sm = 3 * $sb + 4;
	$sn = intval( $si * 9 / 10 );

	$ratarr = get_option( 'wp-sudoku-rating-counts', array( '1' => 0, '2' => 0, '3' => 0, '4' => 0, '5' => 0, '6' => 0, '7' => 0 ) );

	// A puzzle of a certain rating requested?
	$puzzle = false;
	$dummy = wp_verify_nonce( 'dummy-code', 'dummy-action' ); // Just to satisfy Plugin Check
	if ( isset( $_REQUEST['rating'] ) ) {

		// Security check
		$rating = strval( intval( $_REQUEST['rating'] ) );
		if ( $rating < '1' || $rating > '7' ) {
			$rating = '1';
		}

		// Find a puzzle of the requested rating
		$puzzle = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wp_sudoku WHERE rating = %d ORDER BY RAND() LIMIT 1", $rating ), ARRAY_A );
		$puzno  = $puzzle['id'];
		$data   = $puzzle['data'];
	}

	// A puzzle of a certain id requested?, else use a random id
	else {

		// Security check
		$puzno 	= isset( $_REQUEST['puzno'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['puzno'] ) ) : wp_rand( 1, get_option( 'wp-sudoku-data-count' ) );
		$puzno 	= strval( intval( $puzno ) );
		if ( $puzno < '0' || $puzno > '200000' ) {
			esc_html_e( 'Puzzle not available', 'wp-sudoku-plus' );
			exit;
		}

		// Puzno = 0 means empty puzzle
		if ( $puzno == '0' ) {
			$data = '000000000000000000000000000000000000000000000000000000000000000000000000000000000';
			$rating = '0';
		}

		// Puzno <> 0
		else {
			$puzzle = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wp_sudoku WHERE id = %s", $puzno ), ARRAY_A );
			if ( isset( $_REQUEST['data'] ) ) {
				$data = sanitize_text_field( wp_unslash( $_REQUEST['data'] ) );
			}
			else {
				$data = $puzzle['data'];
			}
			$rating = $puzzle['rating'];
		}
	}

	$result = '';

	// Open overall container
	if ( ! defined( 'DOING_AJAX' ) ) {
		$result .= 	'<div id="sud-outer"><div id="sud-container" style="width:'.$sm.'px;visibility:hidden;" data-ver="" data-scw="'.$wpsud_sc_width.'">';
		$first = false;
	}

	$result .= '
	<div
		id="sud-main-box"
		class="sud-main-box"
		style="width:'.$sm.'px;height:'.$sm.'px;"
		oncontextmenu="return false;"
		>
		<input type="hidden" id="puzno" value="'.$puzno.'">
		<input type="hidden" id="rating" value="'.$rating.'">
		<input type="hidden" id="nonce" value="'.wp_create_nonce( 'sudoku-'.$puzno ).'" >';
		for ( $ybox = 0; $ybox < 3; $ybox++ ) {
			for ( $xbox = 0; $xbox < 3; $xbox++ ) {
				$result .= '
				<div
					class="sud-block-box sud-block-box-y-'.$ybox.' sud-block-boxx'.$xbox.' sud-block-box-b-'.( $ybox * 3 + $xbox ).'"
					style="'.( floor( ( $xbox + $ybox ) / 2 ) * 2 == ( $xbox + $ybox ) ? 'background-color:#ccc;' : '' ).'width:'.$sb.'px;height:'.$sb.'px"
					>';
					for ( $yitm = 0; $yitm < 3; $yitm++ ) {
						for ( $xitm = 0; $xitm < 3; $xitm++ ) {
							$i = ( ( $ybox * 3 ) + $yitm ) * 9 + $xbox * 3 + $xitm;
							$value = substr( $data, $i, 1 );
							$result .= '
							<div
								class="sud-item-box sud-item-box-y-'.( $ybox * 3 + $yitm ).' sud-item-boxx'.( $xbox * 3 + $xitm ).' sud-item-box-'.$i.'"
								style="width:'.$si.'px;height:'.$si.'px;font-size:'. ( ( $s - 2 ) * 3 ) .'px;line-height:'.$si.'px;"
								>';
								if ( $value ) {
									$result .= 	'
									<div
										id="sud-'.$i.'"
										class="sud-number"
										style="color:#007;"
										>' .
										$value . '
									</div>';
								}
								else {
									$bi = 1;
									for ( $ybtn = 0; $ybtn < 3; $ybtn++ ) {
										$t = $ybtn * $s;
										for ( $xbtn = 0; $xbtn < 3; $xbtn++ ) {
											$l = $xbtn * $s;
											$result .= '
											<div
												id="sud-button-'.($ybox*3+$yitm).'-'.($xbox*3+$xitm).'-'.($ybtn*3+$xbtn+1).'"
												class="sud-bb sud-bby'.( $ybox * 3 + $yitm ).'v'.( $ybtn * 3 + $xbtn + 1 ). /*'
													sud-bb-'.$bi.*/'
													sud-bbx'.( $xbox * 3 + $xitm ).'v'.( $ybtn * 3 + $xbtn + 1 ).'
													sud-bbb-' . ( $ybox * 3 + $xbox ) . 'v' . ( $ybtn * 3 + $xbtn + 1 ).'
													sud-bbv' . ( $ybtn * 3 + $xbtn + 1 ) .'
													sud-bby' . ( $ybox * 3 + $yitm ) . 'x' . ( $xbox * 3 + $xitm ) . 'v' . ( $ybtn * 3 + $xbtn + 1 ) .
													'" ' .
											//	style="width:'.$s.'px;height:'.$s.'px;font-size:'.$s.'px;line-height:'.$s.'px;" ' .
												( wp_is_mobile() ? '
													ontouchstart="sudTS('.($ybox*3+$yitm).','.($xbox*3+$xitm).','.($ybtn*3+$xbtn+1).');"
													ontouchend="sudTE('.($ybox*3+$yitm).','.($xbox*3+$xitm).','.($ybtn*3+$xbtn+1).',false);" '
													: '
													onmousedown="sudMD(event,'.($ybox*3+$yitm).','.($xbox*3+$xitm).','.($ybtn*3+$xbtn+1).');" ' ) . '
												>' .
												($ybtn*3+$xbtn+1).'
											</div>';
											$bi++;
										}
									}
									$result .= '
									<div
										id="sud-' . $i . '"
										class="sud-number"
										style="color:#000;">
									</div>';
								}
							$result .=
							'</div>';
						}
					}
				$result .=
				'</div>';
			}
		}
		$result .=
		'</div>';

		// Create the site specific js code
		$js =  'sudAjaxUrl="' . admin_url( 'admin-ajax.php' ) . '";';
		$js .= 'sudMaxSize='.$size.';';
		$js .= 'sudSmallSpinnerUrl = "' . plugins_url( basename( dirname( __FILE__ ) ) . '/smallspinner.gif' ) . '";';
		$js .= 'jQuery(window).on("resize orientationchange", function(){sudResize()});';
		$js .= 'sudErrorText[0]=\'' . __( 'Undo', 'wp-sudoku-plus' ) . '\';';
		$js .= 'sudErrorText[1]=\'1/3 ' . __( 'Mistakes', 'wp-sudoku-plus' ) . '\';';
		$js .= 'sudErrorText[2]=\'1/3 ' . __( 'Mistakes', 'wp-sudoku-plus' ) . '\';';
		$js .= 'sudErrorText[3]=\'1/3 ' . __( 'Mistakes', 'wp-sudoku-plus' ) . '\';';

		wp_add_inline_script( 'wp-sudoku-plus', $js );

		$js2 = '';
		for ( $i = 0; $i < 81; $i++ ) {
			$v = substr( $data, $i, 1 );
			if ( $v ) {
				$y = floor( $i / 9 );
				$x = $i % 9;
				$js2 .= 'sudButtonDestroy(' . $y . ', ' . $x . ', ' . $v . ', true );';
			}
		}

		// Create the puzzle specific js code
		if ( defined('DOING_AJAX') ) {
			$wpsud_puzzle_js = 'jQuery(document).ready(function(){'.$js2.'});';
		}
		else {
			wp_add_inline_script( 'wp-sudoku-plus', $js.$js2 );
			$wpsud_puzzle_js = '';
		}

		$same  = get_permalink();

		if ( is_array( $puzzle ) && isset( $puzzle['id'] ) ) {
			if ( strpos( $same, '?' ) ) {
				$same .= '&puzno=' . $puzzle['id'];
			}
			else {
				$same .= '?puzno=' . $puzzle['id'];
			}
		}

		$new   = get_permalink();
		if ( isset( $_REQUEST['rating'] ) ) {

			$rating = strval( intval( $_REQUEST['rating'] ) ) ;
			if ( strpos( $new, '?' ) ) {
				$new .= '&rating=' . $rating;
			}
			else {
				$new .= '?rating=' . $rating;
			}
		}
		$empty = get_permalink();
		if ( strpos( $empty, '?' ) ) {
			$empty .= '&puzno=0';
		}
		else {
			$empty .= '?puzno=0';
		}

		// Open nav container
		$result .=
			'<div id="sud-nav-container">';
			for ( $xbox = 0; $xbox < 11; $xbox++ ) {
				$result .= '
				<div id="sud-navi-box-'.$xbox.'"' .
					' class="' . ( $xbox == 10 ? '' : 'sud-navi-box ' ) . 'sud-navi-box-' . $xbox . '"' .
					' onclick="'.($xbox==10?'sudStackPop('.$puzzle['id'].','. $size.');':'sudHiLite('.$xbox.');').'"' .
					' title="';
					switch ( $xbox ) {
						case 0:
							$result .= __( 'Clear hilites', 'wp-sudoku-plus' );
							break;
						case 10:
							$result .= __( 'Undo', 'wp-sudoku-plus' );
							break;
						default:
							$result .= __( 'Hilite', 'wp-sudoku-plus' ) . ' ' . $xbox;
							break;
					}

					$result .= '"' .
					' style="font-size:'. ( (  $s - 2 ) * 2 ) .'px;line-height:'. $sn .'px;'.($xbox == 10 ? 'color:transparent;' : '' ).'"' .
					' >';
					switch ( $xbox ) {
						case 0:
							$result .= 'X';
							break;
						case 10:
							$result .= '
								<img id="sud-undo" src="' . plugins_url( basename( dirname( __FILE__ ) ) . '/undo.png' ) . '"/>';
							break;
						default:
							$result .= $xbox;
							break;
					}
					$result .=
				'</div>';
			}
		$result .=
		'</div>';

		// Open legenda container
		$result .= '
		<div
			id="wp-sudoku-legenda"
			style="clear:both;width:'.$sm.'px;"
			>';

		// Statistics this puzzle
		if ( $puzno ) {
		$result .=
			__( 'Puzzle', 'wp-sudoku-plus' ) .
			' #' . $puzzle['id'] . ', ' .
			__( 'Won', 'wp-sudoku-plus' ) .
			': ' .
			'<span' .
				' id="won"' .
				' style="cursor:pointer;"' .
				/* Translators: Puzzle number */
				' title="' . esc_attr( sprintf( __( 'Total times puzzle #%d has been solved', 'wp-sudoku-plus' ), $puzzle['id'] ) ) . '"' .
				' >' .
				$puzzle['won'] .
			'</span>, ' .
			__( 'Lost', 'wp-sudoku-plus' ) .
			': ' .
			'<span' .
				' id="lost"' .
				' style="cursor:pointer;"' .
				/* Translators: Puzzle number */
				' title="' . esc_attr( sprintf( __( 'Total times a visitor failed to solve puzzle #%d', 'wp-sudoku-plus' ), $puzzle['id'] ) ) . '"' .
				' >' .
				$puzzle['lost'] .
			'</span>.<br>';
		}

		// Level selection box
		$result .=
			__( 'Level', 'wp-sudoku-plus' ) .
			': ' .
			'<select' .
				' id="wp-sudoku-rating"' .
				' onchange="sudGetPuzzle( false, this.value, false, ' . $s . ')"' .
				' >';
				for ( $i = 1; $i < 8; $i++ ) {
				$result .=
					'<option' .
						' value="' . $i . '"' .
						( $rating == $i ? ' selected="selected" ' : '' ) .
						( $ratarr[$i] == 0 ? ' disabled="disabled" title="'.esc_attr__('Not yet available', 'wp-sudoku-plus').'"' : '' ) .
						'>' .
						$i .
					'</option>';
				}
		$result .=
		'</select>' .
		', ';

		// Total statistics this level
		if ( $puzno ) {
			$temp = $wpdb->get_results( $wpdb->prepare( "SELECT won FROM {$wpdb->prefix}wp_sudoku
														 WHERE rating = %d AND won <> 0", $rating ), ARRAY_A );
			$totwon = 0;
			if ( $temp ) {
				foreach ( $temp as $t ) {
					$totwon += $t['won'];
				}
			}
			$temp = $wpdb->get_results( $wpdb->prepare( "SELECT lost FROM {$wpdb->prefix}wp_sudoku
														 WHERE rating = %d AND lost <> 0", $rating ), ARRAY_A );
			$totlost = 0;
			if ( $temp ) {
				foreach ( $temp as $t ) {
					$totlost += $t['lost'];
				}
			}
			$result .=
			__( 'Won', 'wp-sudoku-plus' ) .
			': ' .
			'<span' .
				' id="totwon"' .
				' style="cursor:pointer;"' .
				/* Translators: Puzzle level number */
				' title="' . esc_attr( sprintf( __( 'Total times a puzzle of level %d has been solved', 'wp-sudoku-plus' ), $rating ) ) . '"' .
				' >' .
				$totwon .
			'</span>, ' .
			__( 'Lost', 'wp-sudoku-plus' ) .
			': ' .
			'<span' .
				' id="totlost"' .
				' style="cursor:pointer;"' .
				/* Translators: Puzzle level number */
				' title="' . esc_attr( sprintf( __( 'Total times a visitor failed to solve a puzzle of level %d', 'wp-sudoku-plus' ), $rating ) ) . '"' .
				' >' .
				$totlost .
			'</span>.' .
			'<img
				id="sud-busy-spin"
				src="' . plugins_url( basename( dirname( __FILE__ ) ) . '/smallspinner.gif' ) . '"
				style="display: none"
			/>' .
			'<br>';
		}

		// New puzzle links
		$day = sprintf( '%02d%03d', wp_date('y'), wp_date('z') ) + 1; // Jan 1 is day 1 here...
		$result .= '
		<a
			id="sud-same"
			style="cursor:pointer;"
			onclick="sudClearStack();sudGetPuzzle( ' . $puzno . ', false, false, ' . $s . ' )">' .
			__('Same', 'wp-sudoku-plus' ) . '
		</a>
		<a
			style="cursor:pointer;"
			onclick="sudClearStack();sudGetPuzzle( false, ' . $rating . ', false, ' . $s . ' )">' .
			__('New', 'wp-sudoku-plus' ) . '
		</a>';

		if ( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku" ) >= $day ) {
			$result .= '
			<a
				style="cursor:pointer;"
				onclick="sudClearStack();sudGetPuzzle( '. $day . ', false, false, ' . $s . ' )">' .
				__('Daily', 'wp-sudoku-plus' ) . '
			</a>';
		}

		$result .= '
		<a
			style="cursor:pointer;"
			onclick="sudClearStack();sudGetPuzzle( 0, false, false, ' . $s . ' )">' .
			__( 'Empty', 'wp-sudoku-plus' ) . '
		</a>
		<a
			style="cursor:pointer;"
			onclick="sudToggle(\'wp-sudoku-help\')">' .
			__( 'Help', 'wp-sudoku-plus' ) . '
		</a>';

	// Close legenda container
	$result .=
	'</div>';

	// Help and info box
	$result .= 	'
	<div' .
		' id="wp-sudoku-help">'.
		__( 'Click left to select, click right to remove option, type digit to show.', 'wp-sudoku-plus' ) . '<br>' .
		/* Translators: count of puzzles */
		sprintf( __( 'There are %d puzzles available.', 'wp-sudoku-plus' ), $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku" ) ) .
		'<table style="border-collapse:collapse;width:100%;" >' .
			'<thead>' .
				'<td>' . __( 'Level', 'wp-sudoku-plus' ) . '</td>' .
				'<td>' . __( 'Total', 'wp-sudoku-plus' ) . '</td>' .
				'<td>' . __( 'Won', 'wp-sudoku-plus' ) . '</td>' .
				'<td>' . __( 'Lost', 'wp-sudoku-plus' ) . '</td>' .
			'</thead>' .
			'<tbody>';
			for ( $level = 1; $level < 8; $level++ ) {
				$result .= '
				<tr>' .
					'<td>' . $level . '</td>' .
					'<td>' . $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku WHERE `rating` = %d", $level ) ) . '</td>' .
					'<td id="sud-'.$level.'-won" >' .
						$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku
														 WHERE rating = %d
														 AND won <> 0", $level ) ) .
					'</td>' .
					'<td id="sud-'.$level.'-lost" >' .
						$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}wp_sudoku
														 WHERE rating = %d
														 AND lost <> 0", $level ) ) .
					'</td>' .
				'</tr>';
			}
			$result .= '
			</tbody>
		</table>
	</div><!-- end -->';

	// Spinner
//	$result .= wpsud_ajaxspin($sm);

	// Close overall container
	if ( ! defined( 'DOING_AJAX' ) ) {
		$result .= '</div><!-- end -->';	// #sud-container
$result .= wpsud_ajaxspin($sm);
$result .= '</div>';
	}
	return wpsud_compress( $result );
}

// Shortcode handler
function wpsud_shortcode_handler( $xatts ) {
	global $wpsud_sc_width;
	$atts = shortcode_atts( array( 'size' => '36' ), $xatts );
	$wpsud_sc_width = $atts['size'];
	return wpsud_main( $wpsud_sc_width );
}

// Enqueue script
function wpsud_add_scripts() {
	if ( ! is_admin() ) wp_enqueue_script( 'wp-sudoku-plus', plugins_url( '/wp-sudoku-plus.js' , __FILE__ ), array( 'jquery' ), time(), true );
}

// Enqueue style
function wpsud_add_style() {
	if ( ! is_admin() ) wp_enqueue_style( 'wp-sudoku-plus-css', plugins_url( '/wp-sudoku-plus.css' , __FILE__ ), array(), time() );
}

// Ajax callback
function wpsud_ajax_callback() {
global $wpdb;
global $wpsud_puzzle_js;

	if ( ! isset( $_REQUEST['wpsud-action'] ) ) return;

	if ( ! wp_verify_nonce( isset( $_REQUEST['nonce'] ) ? sanitize_text_field( wp_unslash(  $_REQUEST['nonce'] ) ) : '', 'sudoku-'.( isset( $_REQUEST['prevpuzno'] ) ? sanitize_text_field( wp_unslash(  $_REQUEST['prevpuzno'] ) ) : '' ) ) ) {
		esc_html_e( 'Security check failure', 'wp-sudoku-plus' );
		exit;
	}

	$action 	= isset( $_REQUEST['wpsud-action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['wpsud-action'] ) ) : '';
	$puzno 		= isset( $_REQUEST['puzno'] ) ? strval( intval( $_REQUEST['puzno'] ) ) : '0';

	switch ( $action ) {
		case 'sudfail':
			$failcount = $wpdb->get_var( $wpdb->prepare( "SELECT lost FROM {$wpdb->prefix}wp_sudoku WHERE id = %d", $puzno ) );
			$failcount++;
			$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}wp_sudoku SET lost = %d WHERE id = %d", $failcount, $puzno ) );
			echo esc_html( $failcount );
			break;
		case 'sudwin':
			$wincount = $wpdb->get_var( $wpdb->prepare( "SELECT won FROM {$wpdb->prefix}wp_sudoku WHERE id = %d", $puzno ) );
			$wincount++;
			$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}wp_sudoku SET won = %d WHERE id = %d", $wincount, $puzno ) );
			echo esc_html( $wincount );
			break;
	//	case 'sudget':
		default:
			$size = isset( $_REQUEST['size'] ) ? strval( intval( $_REQUEST['size'] ) ) : 15;
			$result = wpsud_main( $size );
			echo json_encode( ['html' => $result, 'js' => $wpsud_puzzle_js] );
			break;
	}
	exit;
}

// Init
add_action( 'init', 'wpsud_add_scripts' );
add_action( 'init', 'wpsud_add_style' );
add_shortcode( 'sudoku', 'wpsud_shortcode_handler' );
add_action( 'wp_ajax_wpsud', 'wpsud_ajax_callback' );
add_action( 'wp_ajax_nopriv_wpsud', 'wpsud_ajax_callback' );

function wpsud_compress( $html ) {

	$result = str_replace( ["\n", "\t"], " ", $html );
	$result2 = str_replace( ["  ", "> <"], [" ", "><"], $result );
	while ( strlen( $result ) != strlen( $result2 ) ) {
		$result = $result2;
		$result2 = str_replace( "  ", " ", $result2 );
	}
	return $result;
}

// return the ajax spinner html
function wpsud_ajaxspin($sm) {

	return '<svg id="sud-ajaxspin" class="uil-default" width="66px;" height="66px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" stroke=""
style="position:absolute;top:'.( $sm / 2 - 33 ).'px;left:'.( $sm / 2 - 33 ).'px;">
<rect x="0" y="0" width="100" height="100" fill="none" class="bk" ></rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(0 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(22.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.09375s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(45 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.1875s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(67.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.28125s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(90 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.375s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(112.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.46875s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(135 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.5625s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(157.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.65625s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(180 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.75s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(202.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.84375s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(225 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.9375s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(247.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.03125s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(270 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.125s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(292.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.21875s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(315 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.3125s" repeatCount="indefinite"/>
</rect>
<rect class="sud-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(337.5 50 50) translate(0 -32)">
	<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.40625s" repeatCount="indefinite"/>
</rect>
</svg>';
}