<?php
/**
 * Fonts
 * 
 * This file is loaded at 'after_setup_theme' hook with 2 priority.
 */

if ( ! apply_filters( 'hoot_support_customize_control_betterfont', true ) ) return;

/**
 * Fonts Class
 *
 * @since 3.0.0
 */
class Hoot_Fontography {

	/**
	 * Singleton instance.
	 * @var Hoot_Fontography|null
	 */
	private static $instance = null;

	/**
	 * System fonts list
	 * @var array
	 */
	private $websafe = null;

	/**
	 * Cached Google Fonts (lazy-loaded).
	 * @var array
	 */
	private $gfonts = null;

	/**
	 * Cached All Fonts (lazy-loaded).
	 * @var array
	 */
	private $allfonts = null;

	/**
	 * Font Weights
	 * @var array
	 */
	private $weights = null;

	/**
	 * Active Google Fonts
	 * @var array
	 */
	private $activegfonts = array();

	/**
	 * Prevent direct instantiation.
	 */
	private function __construct() {}

	/**
	 * Get Web safe fonts list.
	 * @return array
	 */
	public function get_websafe_fonts() {
		if ( $this->websafe === null ) {
			$this->websafe = array(
				'label' => esc_html__( 'Web Safe Fonts', 'olius' ),
				'fonts' => array(
					'Arial' => array(
						'c' => 'Helvetica, sans-serif',
						'v' => array( '300', '400', '700' ),
					),
					'Helvetica' => array(
						'c' => 'sans-serif',
						'v' => array( '300', '400', '700' ),
					),
					'Verdana' => array(
						'c' => 'Geneva, sans-serif',
						'v' => array( '300', '400', '700' ),
					),
					'Trebuchet MS' => array(
						'c' => 'Helvetica, sans-serif',
						'v' => array( '300', '400', '700' ),
					),
					'Georgia' => array(
						'c' => 'serif',
						'v' => array( '300', '400', '700' ),
					),
					'Times New Roman' => array(
						'c' => 'serif',
						'v' => array( '300', '400', '700' ),
					),
					'Tahoma' => array(
						'c' => 'Geneva, sans-serif',
						'v' => array( '300', '400', '700' ),
					),
					'Courier' => array(
						'c' => 'monospace',
						'v' => array( '300', '400', '700' ),
					),
				),
			);
			$this->websafe = apply_filters( 'hoot_fontography_websafe', $this->websafe );
		}
		return $this->websafe;
	}

	/**
	 * Get Google Fonts (loaded once).
	 * @return array
	 */
	public function get_gfonts() {
		if ( $this->gfonts === null ) {
			$fontsfile = apply_filters( 'hoot_gfonts_json', hoot_data()->libdir . 'js/gfonts.json' );
			if ( file_exists( $fontsfile ) ) {
				$json = file_get_contents( $fontsfile );
				$json = json_decode( $json, true );
				if ( is_array( $json ) && !empty( $json ) ) {
					$this->gfonts = array(
						'label' => esc_html__( 'Google Fonts', 'olius' ),
						'fonts' => $json,
					);
				} else {
					$this->gfonts = array();
				}
			} else {
				$this->gfonts = array();
			}
			$this->gfonts = apply_filters( 'hoot_fontography_gfonts', $this->gfonts );
		}
		return $this->gfonts;
	}

	/**
	 * Show Gfonts Preview
	 * @return bool
	 */
	public function show_gfont_preview(){
		return (bool) apply_filters( 'hoot_fontography_show_gfont_preview', true );
	}

	/**
	 * Show Gfonts Footer Note
	 * @return bool
	 */
	public function show_gfonts_note(){
		return apply_filters( 'hoot_fontography_show_gfonts_note', '' );
	}

	/**
	 * Get all fonts: Google + System.
	 * @return array
	 */
	public function get_fonts() {
		if ( $this->allfonts === null ) {
			$this->allfonts = array(
				'inherit' => esc_html__( 'Inherit', 'olius' ),
				'websafe' => $this->get_websafe_fonts(),
				'gfonts' => $this->get_gfonts(),
			);
		}
		return $this->allfonts;
	}

	/**
	 * Get Font Weights
	 * 'all' returns entire array, 'fontname' returns weights for that font, 'default' returns default[400,700]
	 * includeinherit adds 'Inherit' option at the top
	 * @return array
	 */
	public function get_fontweights( $font='', $includeinherit=true ) {
		$inherit = array( '0' => esc_html__( 'Inherit', 'olius' ) );
		if ( $this->weights === null ) {
			$this->weights = array(
				'100' => esc_html__( '100 : Thin', 'olius' ),
				'200' => esc_html__( '200 : Extra-Light', 'olius' ),
				'300' => esc_html__( '300 : Light', 'olius' ),
				'400' => esc_html__( '400 : Regular', 'olius' ),
				'500' => esc_html__( '500 : Medium', 'olius' ),
				'600' => esc_html__( '600 : Semi-Bold', 'olius' ),
				'700' => esc_html__( '700 : Bold', 'olius' ),
				'800' => esc_html__( '800 : Extra-Bold', 'olius' ),
				'900' => esc_html__( '900 : Black', 'olius' ),
			);
		}

		$weights = array();
		if ( $font === 'all' ) {
			$weights = $this->weights;
		} elseif ( $font === 'default' ) {
			$weights = array(
				'400' => $this->weights['400'],
				'700' => $this->weights['700'],
			);
		} elseif ( ! empty( $font ) && is_string( $font ) ) {
			$allfonts = $this->get_fonts();
			$itkeys = is_array( $allfonts ) ? array_keys( $allfonts ) : array();
			foreach ( $itkeys as $itkey ) {
				if ( is_array( $allfonts[ $itkey ] )
					&& isset( $allfonts[ $itkey ]['fonts'] ) && is_array( $allfonts[ $itkey ]['fonts'] )
					&& isset( $allfonts[ $itkey ]['fonts'][ $font ] ) && is_array( $allfonts[ $itkey ]['fonts'][ $font ] )
				) {
					$weights = isset( $allfonts[ $itkey ]['fonts'][ $font ]['v'] ) && is_array( $allfonts[ $itkey ]['fonts'][ $font ]['v'] ) ? $allfonts[ $itkey ]['fonts'][ $font ]['v'] : array();
					// filter while preserving order from $this->weights
					$weights = array_intersect_key( $this->weights, array_flip( $weights ) );
					break;
				}
			}
		}
		if ( $includeinherit ) {
			$weights = $inherit + $weights;
		}
		return $weights;
	}

	/**
	 * Get Subsets
	 * @return array
	 */
	public function get_subsets( $font='' ) {
		$subsets = array();
		if ( ! empty( $font ) && is_string( $font ) ) {
			$allfonts = $this->get_fonts();
			$subsets = !empty( $allfonts['gfonts'] ) && is_array( $allfonts['gfonts'] )
				&& isset( $allfonts['gfonts']['fonts'] ) && is_array( $allfonts['gfonts']['fonts'] )
				&& isset( $allfonts['gfonts']['fonts'][ $font ] ) && is_array( $allfonts['gfonts']['fonts'][ $font ] )
				&& isset( $allfonts['gfonts']['fonts'][ $font ]['ss'] ) && is_array( $allfonts['gfonts']['fonts'][ $font ]['ss'] ) ? $allfonts['gfonts']['fonts'][ $font ]['ss'] : array();
		}
		return $subsets;
	}

	/**
	 * Get Font CSS properties array
	 * @return array
	 */
	public function resolve_font( $mod='', $cssvar='', $suffixes=null ) {
		$cssarray = array();

		$cssvar = is_string( $cssvar ) ? $cssvar : '';
		$cssvars = array();

		$suffixes = is_array( $suffixes ) ? $suffixes : array( 'size' => 'px', 'lheight' => 'em', 'lspace' => 'px' );

		if ( is_array( $mod ) ) {
			$value = $mod;
		} else {
			$value = hoot_get_mod( $mod );
			$value = json_decode($value, true);
			$value = is_array( $value ) ? $value : array();
		}

		// Add to active gfonts if not websafe or inherit so we can enqueue it later
		if ( ! empty( $value['face'] ) ) {
			$websafe = $this->get_websafe_fonts();
			if ( $value['face'] !== 'inherit' && ! isset( $websafe['fonts'][ $value['face'] ] ) ) {
				$this->activegfonts[] = array(
					'face' => $value['face'],
					'subsets' => isset( $value['subsets'] ) && is_array( $value['subsets'] ) ? $value['subsets'] : array(),
					'weight' => isset( $value['weight'] ) ? $value['weight'] : '',
				);
			}
		}

		// Create $cssarray for available props
		foreach ( $value as $prop => $propval ) {
			$suffix = !empty( $suffixes[$prop] ) && is_string( $suffixes[$prop] ) ? $suffixes[$prop] : '';
			switch( $prop ) {
				case 'face':
					if ( $propval !== 'inherit' ) {
						$cssarray['font-family'] = strpos( $propval, ' ' ) !== false ? '"' . $propval . '"' : $propval;
						if ( !empty( $value['category'] ) && is_string( $value['category']) )
							$cssarray['font-family'] .= ', ' . $value['category'];
					}
					if ( empty( $cssarray['font-family'] ) )
						$cssarray['font-family'] = 'inherit';
					$cssvars[ $cssvar . 'family' ] = $cssarray['font-family'];
					break;
				case 'subsets':
					break;
				case 'weight':
					$cssarray['font-weight'] = intval( $propval );
					if ( empty( $cssarray['font-weight'] ) )
						$cssarray['font-weight'] = 'inherit';
					$cssvars[ $cssvar . 'weight' ] = $cssarray['font-weight'];
					break;
				case 'color':
					$cssarray['color'] = hoot_sanitize_color_allowocto( $propval );
					$cssvars[ $cssvar . 'color' ] = $cssarray['color'];
					break;
				case 'size':
					if ( is_array( $propval ) ) {
						$cssarray['font-size'] = hoot_betterrange_css( $propval, $suffix );
					} else {
						$cssarray['font-size'] = intval( $propval ) . $suffix;
					}
					$cssvars[ $cssvar . 'size' ] = $cssarray['font-size'];
					break;
				case 'lheight':
					$cssarray['line-height'] = floatval( $propval ) . $suffix;
					$cssvars[ $cssvar . 'lheight' ] = $cssarray['line-height'];
					break;
				case 'lspace':
					$cssarray['letter-spacing'] = floatval( $propval ) . $suffix;
					if ( $cssarray['letter-spacing'] === 0 )
						$cssarray['letter-spacing'] = 'normal';
					$cssvars[ $cssvar . 'lspace' ] = $cssarray['letter-spacing'];
					break;
				case 'style':
					$cssarray['font-style'] = $propval;
					if ( empty( $cssarray['font-style'] ) )
						$cssarray['font-style'] = 'inherit';
					$cssvars[ $cssvar . 'style' ] = $cssarray['font-style'];
					break;
				case 'trans':
					$cssarray['text-transform'] = $propval;
					if ( empty( $cssarray['text-transform'] ) )
						$cssarray['text-transform'] = 'inherit';
					$cssvars[ $cssvar . 'trans' ] = $cssarray['text-transform'];
					break;
				case 'deco':
					$cssarray['text-decoration'] = $propval;
					if ( empty( $cssarray['text-decoration'] ) )
						$cssarray['text-decoration'] = 'inherit';
					$cssvars[ $cssvar . 'deco' ] = $cssarray['text-decoration'];
					break;
				default:
					break;
			}
		}

		return ( empty( $cssvar ) ? $cssarray : $cssvars );
	}

	/**
	 * Create Gfonts Enqueue URL
	 * @credit http://themeshaper.com/2014/08/13/how-to-add-google-fonts-to-wordpress-themes/
	 * @return string
	 */
	public function gfonts_style_uri() {
		if ( empty( $this->activegfonts ) )
			return '';

		// 1. Create group of weights and subsets for each family used
		$fonts = array();
		foreach ( $this->activegfonts as $value ) {
			$face = $value['face'];
			$fonts[ $face ] = empty( $fonts[ $face ] ) ? array( 'weight' => array(), 'subsets' => array() ) : $fonts[ $face ];
			if ( !empty( $value['weight'] ) ) {
				$fonts[ $face ]['weight'][] = $value['weight'];
			}
			if ( !empty( $value['subsets'] ) && is_array( $value['subsets'] ) ) {
				$fonts[ $face ]['subsets'] = array_merge( $fonts[ $face ]['subsets'], $value['subsets'] );
			}
		}
		$fonts = apply_filters( 'hoot_enqueue_gfonts_array', $fonts );

		// 2. Create string for each family used with weights, italics, subsets
		$args = array();
		foreach ( $fonts as $fontname => $fontarr ) {
			if ( ! is_array( $fontarr ) )
				continue;
			$string = str_replace( ' ', '+', $fontname );

			$weight = !empty( $fontarr['weight'] ) && is_array( $fontarr['weight'] ) ? array_unique( array_map( 'trim', $fontarr['weight'] ) ) : array();
			if ( ! in_array( '400', $weight ) )
				$weight[] = '400';
			sort( $weight, SORT_NUMERIC );
			if ( count( $weight ) == 1 ) {
				$string .= ':ital@0;1';
			} else {
				$wparts = array();
				$iparts = array();
				foreach ( $weight as $w ) {
					$wparts[] = "0,{$w}";
					$iparts[] = "1,{$w}";
				}
				$string .= ':ital,wght@' . implode( ';', array_merge( $wparts, $iparts ) );
			}
			$subsets = !empty( $fontarr['subsets'] ) && is_array( $fontarr['subsets'] ) ? array_unique( array_map( 'trim', $fontarr['subsets'] ) ) : array();
			if ( ! in_array( 'latin', $subsets ) )
				$subsets[] = 'latin';
			$string .= '&subset=' . implode( ',', $subsets );

			$args[] = substr( add_query_arg( 'family', $string, '' ), 1 );
		}

		// 3. Create final url using strings of each family
		$fonts_url = '';
		if ( !empty( $args ) ) {
			$fonts_url = 'https://fonts.googleapis.com/css2?' . implode( '&', $args ) . '&display=swap';
			if ( function_exists( 'hoot_wptt_get_webfont_url' ) ) {
				if ( hoot_get_mod( 'load_local_fonts' ) ) {
					$fonts_url = hoot_wptt_get_webfont_url( esc_url_raw( $fonts_url ) );
				}
				elseif( isset( $_GET['cleanup-local-fonts'] ) && class_exists( 'Hoot_WPTT_WebFont_Loader' ) ) {
					$font_possible_cleanup = new Hoot_WPTT_WebFont_Loader( $fonts_url );
				}
			}
		}

		return $fonts_url;
	}

	/**
	 * Singleton accessor.
	 *
	 * @return Hoot_Fontography
	 */
	public static function get_instance() {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

}

/**
 * Get the Hoot_Fontography instance.
 *
 * @return Hoot_Fontography
 */
function hootfontography() {
	return Hoot_Fontography::get_instance();
}
