<?php

class Vamtam_Customizer {
	private static $instance;

	private $sections;
	private $args;

	protected $class_from_type_cache = array();

	private $controls;

	public static $version = '1.1.0';

	public static function get_instance() {
		if ( ! isset( self::$instance ) ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	public function __construct( $args = array() ) {
		// Autoload classes on demand
		if ( function_exists( '__autoload' ) )
			spl_autoload_register( '__autoload' );
		spl_autoload_register( array( $this, 'autoload' ) );

		$this->args     = $args;
		$this->sections = array();
		$this->dir      = plugin_dir_path( __FILE__ );

		$this->class_from_type_cache = array();

		add_action( 'customize_register', array( $this, 'customize_register' ), 5 );
		add_action( 'customize_save_after', array( $this, 'customize_save_after' ) );

		add_action( 'after_setup_theme', array( $this, 'setup_options' ), 5 );

		add_action( 'wp_ajax_vamtam-customizer-control', array( $this, 'control_ajax' ) );

		add_action( 'customize_controls_enqueue_scripts', array( $this, 'customize_controls_enqueue_scripts' ), 7 );
	}

	public function customize_controls_enqueue_scripts() {
	}

	public function customize_save_after( $wp_customize ) {
		global $vamtam_fonts;

		$fonts_by_family = vamtam_get_fonts_by_family();

		$google_fonts = array();

		$customized = json_decode( stripslashes_deep( $_POST['customized'] ), true );

		$fields  = $this->get_fields_by_id();
		$options = $this->get_options();

		$compiler = false;

		foreach ( $fields as $id => $field ) {
			$full_id = $this->args['opt_name'] . '[' . $id . ']';

			// cache google fonts, so we can just load them later
			if ( 'typography' === $field['type'] ) {
				$font_id = $fonts_by_family[ $options[ $id ]['font-family'] ];
				$font    = $vamtam_fonts[ $font_id ];

				if ( isset( $font['gf'] ) && $font['gf'] ) {
					$google_fonts[ $font_id ][] = isset( $options[ $id ]['variant'] ) ? $options[ $id ]['variant'] : 'normal';
				}

				if ( ! empty( $options[ $id ]['line-height'] ) && strpos( $options[ $id ]['line-height'], 'px' ) !== false ) {
					$options[ $id ]['line-height'] = round( (int)$options[ $id ]['line-height'] / (int)$options[ $id ]['font-size'], 2 );
				}
			}

			// if a compiler option was changed
			if ( isset( $customized[ $full_id ] ) && ( isset( $field['compiler'] ) && $field['compiler'] ) ) {
				$compiler = true;
			}
		}

		$options['google_fonts'] = self::build_google_fonts_url( $google_fonts, $options['gfont-subsets'] );

		$this->set_options( $options );

		if ( $compiler ) {
			do_action( "vamtam_customizer/{$this->args['opt_name']}/compiler", $options );
		}

		do_action( 'vamtam_saved_options' );
	}

	public static function build_google_fonts_url( $google_fonts, $subsets ) {
		$font_imports_url = '';

		if ( is_array( $google_fonts ) && count( $google_fonts ) ) {
			$param = array();

			foreach ( $google_fonts as $font => $weights ) {
				// always include a bold version, if available
				$weights[] = 'bold';

				if ( strpos( implode( '', $weights ), 'italic' ) !== false ) {
					$weights[] = '700i';
				}

				// this is used so that we can add other mandatory weights if we have used them in css
				$weights = apply_filters( 'vamtam_customizer_font_weights', $weights, $font );

				$weights = str_replace( ' ', '', implode( ',', array_unique( $weights ) ) );

				$param[] = urlencode( $font ) . ':' . $weights;
			}

			$param = implode( '|', $param );

			$subsets_filtered = array();

			foreach ( $subsets as $subset => $set ) {
				if ( $set === '1' ) {
					$subsets_filtered[] = $subset;
				}
			}

			$subsets = implode( ',', $subsets_filtered );

			$font_imports_url = 'https://fonts.googleapis.com/css?family=' . $param . '&subset=' . $subsets;
		}

		return $font_imports_url;
	}

	public function autoload( $class ) {
		$file = 'class-' . str_replace( '_', '-', strtolower( $class ) ) . '.php';

		if ( is_readable( $this->dir . $file ) ) {
			include_once $this->dir . $file;
			return;
		}

		if ( is_readable( $this->dir . 'controls/' . $file ) ) {
			include_once $this->dir . 'controls/' . $file;
			return;
		}

		if ( is_readable( $this->dir . 'ajax/' . $file ) ) {
			include_once $this->dir . 'ajax/' . $file;
			return;
		}
	}

	public function control_ajax() {
		// ucwords $delimiter param was added in 5.4.32, 5.5.16
		$type = str_replace( ' ', '_', ucwords( str_replace( '-', ' ', $_POST['control'] ) ) );

		$class = 'Vamtam_Customize_' . $type . '_Ajax';

		if ( class_exists( $class ) ) {
			$ajax_obj = new $class( $this->get_options() );

			$method = array( $ajax_obj, 'ajax_' . $_POST['method'] );

			if ( is_callable( $method ) ) {
				call_user_func( $method );
				exit;
			}
		}

		header( 'Content-type: application/json' );

		echo json_encode( array(
			'error' => 'cannot call ' . $class . '::ajax_' . $_POST['method'],
		) );

		exit;
	}

	public function get_arg( $key ) {
		return $this->args[ $key ];
	}

	public function setup_options() {
		if ( ! isset( $GLOBALS[ $this->args['opt_name'] ] ) ) {
			$GLOBALS[ $this->args['opt_name'] ] = $this->get_options();

			if ( is_customize_preview() && isset( $_POST['customized'] ) ) {
				$raw_options = json_decode( stripslashes_deep( $_POST['customized'] ), true );

				if ( ! empty( $raw_options ) ) {
					if ( is_array( $raw_options ) ) {
						foreach ( $raw_options as $key => $value ) {
							if ( strpos( $key, $this->args['opt_name'] ) !== false ) {
								$key = str_replace( $this->args['opt_name'] . '[', '', rtrim( $key, ']' ) );

								$GLOBALS[ $this->args['opt_name'] ][ $key ] = $value;
							}
						}
					}
				}
			}
		}
	}

	public function get_options() {
		$options = get_option( $this->args['opt_name'] );

		if ( false === $options ) {
			$options = array();
		}

		return wp_parse_args( $options, $this->get_defaults() );
	}

	public function get_defaults() {
		$options = array();
		$fields  = $this->get_fields_by_id();

		foreach ( $fields as $id => $field ) {
			if ( isset( $field['default'] ) ) {
				$options[ $id ] = $field['default'];
			}
		}

		return $options;
	}

	public function set_options( $options ) {
		return update_option( $this->args['opt_name'], $options );
	}

	public function add_section( array $args ) {
		$top_level_sections = count( $this->sections );

		if ( isset( $args['fields'] ) ) {
			$args['fields'] = apply_filters( 'vamtam_customizer_fields_options', $args['fields'] );
		}

		$args['permissions'] = isset( $args['permissions'] ) ? $args['permissions'] : 'edit_theme_options';

		if ( isset( $args['subsection'] ) && $args['subsection'] && $top_level_sections > 0 ) {
			$this->sections[ $top_level_sections - 1 ]['children'][] = $args;
		} else {
			$args['children'] = array();
			$this->sections[] = $args;
		}
	}

	public function get_sections() {
		return $this->sections;
	}

	public function get_fields_by_id() {
		if ( ! isset( $this->fields_by_id ) ) {
			$this->fields_by_id = array();

			foreach ( $this->sections as $section ) {
				if ( count( $section['children'] ) > 0 ) {
					foreach ( $section['children'] as $child ) {
						foreach ( $child['fields'] as $field ) {
							$this->fields_by_id[ $field['id'] ] = $field;
						}
					}
				} else {
					if ( isset( $section['fields'] ) ) {
						foreach ( $section['fields'] as $field ) {
							$this->fields_by_id[ $field['id'] ] = $field;
						}
					}
				}
			}
		}

		return $this->fields_by_id;
	}

	public function get_fields_by_type( $type ) {
		$fields = array();

		$options = $this->get_fields_by_id();

		foreach ( $options as $id => $opt ) {
			if ( isset( $opt['type'] ) && $opt['type'] === $type ) {
				$fields[ $id ] = $opt;
			}
		}

		return $fields;
	}

	public function customize_register( WP_Customize_Manager $wp_customize ) {
		if ( method_exists( $wp_customize, 'register_section_type' ) ) {
			$wp_customize->register_section_type( 'Vamtam_Customizer_Section' );
		}

		if ( method_exists( $wp_customize, 'register_panel_type' ) ) {
			$wp_customize->register_panel_type( 'Vamtam_Customizer_Panel' );
		}

		$priority = 1;

		foreach ( $this->sections as $section ) {
			if ( empty( $section['priority'] ) ) {
				$section['priority'] = $priority++;
			}

			$parent_id = $this->args['opt_name'] . '-' . $section['id'];

			if ( count( $section['children'] ) > 0 ) {

				$wp_customize->add_panel( $parent_id, array(
					'priority'    => $section['priority'],
					'capability'  => $section['permissions'],
					'title'       => $section['title'],
					'section'     => $section,
					'description' => '',
				) );

				foreach ( $section['children'] as $child ) {
					if ( empty( $child['priority'] ) ) {
						$child['priority'] = $priority++;
					}

					$wp_customize->add_section( $this->args['opt_name'] . '-' . $child['id'], array(
						'title'       => $child['title'],
						'priority'    => $child['priority'],
						'description' => $child['description'],
						'section'     => $child,
						'capability'  => $child['permissions'],
						'panel'       => $parent_id,
					) );

					$this->setup_fields( $child, $wp_customize );
				}
			} else {
				if ( ! isset( $section['preexisting'] ) || ! $section['preexisting'] ) {
					$wp_customize->add_section( $parent_id, array(
						'priority'    => $section['priority'],
						'capability'  => $section['permissions'],
						'title'       => $section['title'],
						'section'     => $section,
						'description' => '',
					) );
				}

				$this->setup_fields( $section, $wp_customize );
			}
		}
	}

	private function setup_fields( $section, WP_Customize_Manager $wp_customize ) {
		$priority = 1;

		foreach ( $section['fields'] as $field ) {
			$field['id'] = $this->args['opt_name'] . '[' . $field['id'] . ']';

			if ( ! isset( $field['priority'] ) ) {
				$field['priority'] = $priority++;
			}

			$class = $this->class_from_type( $field['type'], $wp_customize );

			if ( $class ) {
				$type = $field['type'];

				if ( $class !== 'Vamtam_Customize_Control' && strpos( $class, 'Vamtam' ) !== false ) {
					$type = 'vamtam-' . $type;
				}

				$wp_customize->add_setting( $field['id'], array(
					'default'           => isset( $field['default'] ) ? $field['default'] : '',
					'type'              => 'option',
					'transport'         => isset( $field['transport'] ) ? $field['transport'] : 'refresh',
					'sanitize_callback' => $this->get_sanitize_callback( $field, $wp_customize ),
				) );

				$control_attrs = array_merge( $field, array(
					'section'  => isset( $field['section'] ) ? $field['section'] : $this->args['opt_name'] . '-' . $section['id'],
					'settings' => $field['id'],
					'type'     => $type,
					'field'    => $field,
				) );

				$wp_customize->add_control( new $class( $wp_customize, $field['id'], $control_attrs ) );
			}
		}
	}

	private function get_sanitize_callback( $field, $wp_customize ) {
		$callback = '';

		if ( isset( $field['sanitize_callback'] ) ) {
			$callback = $field['sanitize_callback'];
		} else {
			$class = $this->class_from_type( $field['type'], $wp_customize );

			$common_callbacks = array(
				'color'      => 'sanitize_hex_color',
				'number'     => array( __CLASS__, 'sanitize_number' ),
				'image'      => 'esc_url_raw',
				'multicheck' => array( __CLASS__, 'sanitize_array' ),
			);

			if ( isset( $common_callbacks[ $field['type'] ] ) ) {
				$callback = $common_callbacks[ $field['type'] ];
			} elseif ( is_callable( array( $class, 'sanitize_callback' ) ) ) {
				$callback = array( $class, 'sanitize_callback' );
			}
		}

		return apply_filters( 'vamtam_customize_setting_sanitize_callback', $callback );
	}

	public static function sanitize_array( $value ) {
		if ( ! is_array( $value ) ) {
			// objects should be saved as arrays
			if ( is_object( $value ) ) {
				return (array) $value;
			}

			// not an array or an object - default to an empty array
			return array();
		}

		// everything is ok
		return $value;
	}

	public static function sanitize_number( $value ) {
		$can_validate = method_exists( 'WP_Customize_Setting', 'validate' );

		if ( ! is_numeric( $value ) ) {
			return $can_validate ? new WP_Error( 'nan', esc_html__( 'Not a number', 'morz' ) ) : null;
		}

		return intval( $value );
	}

	protected function class_from_type( $type, WP_Customize_Manager $wp_customize ) {
		if ( isset( $this->class_from_type_cache[ $type ] ) ) {
			return $this->class_from_type_cache[ $type ];
		}

		$orig_type = $type;

		// ucwords $delimiter param was added in 5.4.32, 5.5.16
		$type = str_replace( ' ', '_', ucwords( str_replace( '-', ' ', $type ) ) );

		$class = '';

		$test_core   = 'WP_Customize_' . $type . '_Control';
		$test_vamtam = 'Vamtam_Customize_' . $type . '_Control';

		if ( class_exists( $test_vamtam ) ) {
			$class = $test_vamtam;

			try {
				$reflection = new \ReflectionMethod( $test_vamtam, 'content_template' );

				$declaringClass = $reflection->getDeclaringClass()->getName();
				$proto          = $reflection->getPrototype();

				if ( $proto && $proto->getDeclaringClass()->getName() !== $declaringClass ) {
					$wp_customize->register_control_type( $class );
				}
			} catch ( Exception $e ) {
				// getPrototype will throw if content_template() is not overriden
			}
		} elseif ( class_exists( $test_core ) ) {
			$class = $test_core;
		} else {
			$class = 'Vamtam_Customize_Control'; // default WP Core control - implements text, radio, select, etc.
		}

		$this->class_from_type_cache[ $orig_type ] = $class;

		return $class;
	}
}
