<?php

/**
 * Media
 *
 * @package Less
 * @subpackage tree
 */
class Less_Tree_Media extends Less_Tree {

	public $features;
	public $rules;
	public $index;
	public $currentFileInfo;
	public $isReferenced;
	public $type = 'Media';

	public function __construct( $value = array(), $features = array(), $index = null, $currentFileInfo = null ) {
		$this->index = $index;
		$this->currentFileInfo = $currentFileInfo;

		$selectors = $this->emptySelectors();

		$this->features = new Less_Tree_Value( $features );

		$this->rules = array( new Less_Tree_Ruleset( $selectors, $value ) );
		$this->rules[0]->allowImports = true;
	}

	public function accept( $visitor ) {
		$this->features = $visitor->visitObj( $this->features );
		$this->rules = $visitor->visitArray( $this->rules );
	}

	/**
	 * @see Less_Tree::genCSS
	 */
	public function genCSS( $output ) {
		$output->add( '@media ', $this->currentFileInfo, $this->index );
		$this->features->genCSS( $output );
		Less_Tree::outputRuleset( $output, $this->rules );

	}

	public function compile( $env ) {
		$media = new Less_Tree_Media( array(), array(), $this->index, $this->currentFileInfo );

		$strictMathBypass = false;
		if ( Less_Parser::$options['strictMath'] === false ) {
			$strictMathBypass = true;
			Less_Parser::$options['strictMath'] = true;
		}

		$media->features = $this->features->compile( $env );

		if ( $strictMathBypass ) {
			Less_Parser::$options['strictMath'] = false;
		}

		$env->mediaPath[] = $media;
		$env->mediaBlocks[] = $media;

		array_unshift( $env->frames, $this->rules[0] );
		$media->rules = array( $this->rules[0]->compile( $env ) );
		array_shift( $env->frames );

		array_pop( $env->mediaPath );

		return !$env->mediaPath ? $media->compileTop( $env ) : $media->compileNested( $env );
	}

	public function variable( $name ) {
		return $this->rules[0]->variable( $name );
	}

	public function find( $selector ) {
		return $this->rules[0]->find( $selector, $this );
	}

	public function emptySelectors() {
		$el = new Less_Tree_Element( '', '&', $this->index, $this->currentFileInfo );
		$sels = array( new Less_Tree_Selector( array( $el ), array(), null, $this->index, $this->currentFileInfo ) );
		$sels[0]->mediaEmpty = true;
		return $sels;
	}

	public function markReferenced() {
		$this->rules[0]->markReferenced();
		$this->isReferenced = true;
		Less_Tree::ReferencedArray( $this->rules[0]->rules );
	}

	// evaltop
	public function compileTop( $env ) {
		$result = $this;

		if ( count( $env->mediaBlocks ) > 1 ) {
			$selectors = $this->emptySelectors();
			$result = new Less_Tree_Ruleset( $selectors, $env->mediaBlocks );
			$result->multiMedia = true;
		}

		$env->mediaBlocks = array();
		$env->mediaPath = array();

		return $result;
	}

	public function compileNested( $env ) {
		$path = array_merge( $env->mediaPath, array( $this ) );

		// Extract the media-query conditions separated with `,` (OR).
		foreach ( $path as $key => $p ) {
			$value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
			$path[$key] = is_array( $value ) ? $value : array( $value );
		}

		// Trace all permutations to generate the resulting media-query.
		//
		// (a, b and c) with nested (d, e) ->
		//	a and d
		//	a and e
		//	b and c and d
		//	b and c and e

		$permuted = $this->permute( $path );
		$expressions = array();
		foreach ( $permuted as $path ) {

			for ( $i = 0, $len = count( $path ); $i < $len; $i++ ) {
				$path[$i] = Less_Parser::is_method( $path[$i], 'toCSS' ) ? $path[$i] : new Less_Tree_Anonymous( $path[$i] );
			}

			for ( $i = count( $path ) - 1; $i > 0; $i-- ) {
				array_splice( $path, $i, 0, array( new Less_Tree_Anonymous( 'and' ) ) );
			}

			$expressions[] = new Less_Tree_Expression( $path );
		}
		$this->features = new Less_Tree_Value( $expressions );

		// Fake a tree-node that doesn't output anything.
		return new Less_Tree_Ruleset( array(), array() );
	}

	public function permute( $arr ) {
		if ( !$arr )
			return array();

		if ( count( $arr ) == 1 )
			return $arr[0];

		$result = array();
		$rest = $this->permute( array_slice( $arr, 1 ) );
		foreach ( $rest as $r ) {
			foreach ( $arr[0] as $a ) {
				$result[] = array_merge(
					is_array( $a ) ? $a : array( $a ),
					is_array( $r ) ? $r : array( $r )
				);
			}
		}

		return $result;
	}

	public function bubbleSelectors( $selectors ) {
		if ( !$selectors ) return;

		$this->rules = array( new Less_Tree_Ruleset( $selectors, array( $this->rules[0] ) ) );
	}

}
