<?php
// Handle .JS, .CSS, .ICO (or .png or .svg), and .LESS include files (to be extended to include Fonts and apple icons)
class FileObject {
	private $version = NULL;
	private $integrity = NULL;
	private $filename = NULL;
	private $cross_origin = NULL;
	private $library = '';
	private $attach = array();
	private $handlebars = NULL;
    private $contenttype = NULL;
	private $css = '';		// inline css
	private $ext = '';
	private $command = '';
	private $autorun = [];
	private $title = '';
	private $tag = '';		// a name associated with this entry.  See depends
	private $depends = array();
	private $macro = FALSE;		// is this a macro?
	public  $md5;				// md5 of html content
	public  $x_node;			 
	private $attr = [];

	public function __construct($ext = '') {
		$this->ext = $ext;	// file extension, or 'handlebars'
	}
	
	// Get/Set filename;
	public function filename() {
		if (func_num_args()) {
			$value = func_get_arg(0);
			$this->filename = $value;
			$x = parse_url($value, PHP_URL_PATH);
			$this->ext = strtolower(pathinfo($x, PATHINFO_EXTENSION));
			if ($this->ext == 'json') {
                $this->ext = 'js';
			} elseif ($this->ext == '') {
				if (strpos($x, 'css') !== FALSE) {
					$this->ext = 'css';
				} elseif (strpos($x, 'js') !== FALSE) {
					$this->ext = 'js';
				}
			}
			return $this;
		}
		return $this->filename;
	}
	
	// get/set library
	public function library() {
		if (func_num_args()) {
			$this->library = func_get_arg(0);	// $library_identifier
			return $this;
		}
		return $this->library;
	}

	// set value if parameter specified, return value if missing
	public function title() {
		if (func_num_args()) {
			$this->title = func_get_arg(0);
			return $this;
		}
		return $this->title;
	}

	public function ContentType() {
		if (func_num_args()) {
			$this->contenttype = func_get_arg(0);
			return $this;
		}
		return $this->contenttype;
	}

    // handlebars template, or partial
	public function html() {
		if (func_num_args()) {
			$this->handlebars = func_get_arg(0);
			hook_execute('handlebars.add', $this);
			$this->md5 = md5($this->handlebars);
			return $this;
		}
		return $this->handlebars;
	}
	
	public function ext() {
		if (func_num_args()) {
			$this->ext = func_get_arg(0);
			return $this;
		}
		return $this->ext;
	}

	public function tag() {
		if (func_num_args()) {
			$this->tag = strtolower(func_get_arg(0));
			return $this;
		}
		return $this->tag;
	}

	public function depends() {
		if (func_num_args()) {
			$this->depends = func_get_args();
			return $this;
		}
		return $this->depends;
	}

	public function macro() {
		if (func_num_args()) {
			$this->macro = func_get_arg(0);
			return $this;
		}
		return $this->macro;
	}

	public function version() {
		if (func_num_args()) {
			$this->version = func_get_arg(0);
			return $this;
		}
		return $this->version;
	}

	public function integrity() {
		if (func_num_args()) {
			$this->integrity = func_get_arg(0);
			return $this;
		}
		return $this->integrity;
	}

	public function CrossOrigin() {
		if (func_num_args()) {
			$this->cross_origin = func_get_arg(0);
			return $this;
		}
		return $this->cross_origin;
	}

	public function AddCSS($css) {
		$this->css .= rtrim($css) . "\n\n";
		return $this;
	}
	
	public function Attr($key) {
		$c = func_num_args();
		if (!$c) {
			return $this->attr;
		} elseif ($c == 1) {
			return $this->attr[$key] ?? FALSE;
		} else {
			$this->attr[$key] = func_get_arg(1);
			return $this;
		}
	}

	public function JavascriptData($variable_name, $variable_data) {
		$this->attach[$variable_name] = $variable_data;
	}
	
	public function GetJavascriptData($variable_name) {
		return isset($this->attach[$variable_name]) ? $this->attach[$variable_name] : NULL;
	}
	
	public function JavascriptCommand($command, $autorun = 1) {
		if (!preg_match('~;\s*$~', $command)) $command .= ';';
		if ($autorun) {
			$this->autorun[$autorun][] = $command;
		} else {
			if ($this->command != '') $this->command .= "\n";
			$this->command .= $command;
		}
		return $this;
	}
	
	private function _assembleAutorun() {
		$output = '';
		ksort($this->autorun);
		foreach($this->autorun as $level => $info) {
			$lines = join("\n", $info);
			if ($output != '') $output .= "\n";
			$output .= $lines;
		}
		return <<<BLOCK
	$(function() {
{$output}
	});

BLOCK;
	}
	
	private function _localized_js($javascript_include) {
		$pre_include = $post_include = '';
		
		$base = "\t";
		foreach($this->attach as $variable_name => $variable_data) {
			$data = json_encode($variable_data);
			$entry = <<<BLOCK
{$base}var {$variable_name}={$data};

BLOCK;
			$pre_include .= $entry;
		}
		
		if ($this->command != '') {
			$post_include .= $this->command;
		}
		
		if (count($this->autorun)) {
			$post_include .= $this->_assembleAutorun();
		}
		$result = '';
		if ($javascript_include == '' && $pre_include != '') {		// no <script> section, so Before can be merged in with After
			$post_include = $pre_include . $post_include;
			$pre_include = '';
		}
		if ($pre_include != '') {
			$result .= <<<BLOCK
<script type="text/javascript">/* <![CDATA[ */
$pre_include/* ]]> */</script>

BLOCK;
		}
		$result .= $javascript_include;
		if ($post_include != '') {
			$result .= <<<BLOCK
<script type="text/javascript">/* <![CDATA[ */
$post_include/* ]]> */</script>

BLOCK;
		}
		$result .= $this->_addcss();
		return $result;
	}
	
	private function _addcss() {
		if ($this->css != '') {
			return <<<BLOCK
<style type="text/css">
{$this->css}</style>
BLOCK;
		} else {
			return '';
		}
	}
	
	protected function LocalFilename() {
		$filename = $this->filename;
		if (is_null($filename)) return FALSE;
		$x = parse_url($filename);
		if (empty($x['host'])) {		// local file - get version if needed
			$source_file = HOME . '/public/' . ltrim($x['path'], '/');
			if (preg_match('!^/~/(.*?)/(.*)!', $x['path'], $match)) {	// special case - symlink does not exist
				$file = ltrim(str_replace(['..', '&', '<', '>', '|'], '', $match[2]), '/');
				$source_file = HOME . '/modules/' . $match[1] . '/static/' . $file;
			}
			if (file_exists($source_file)) {
				return $source_file;
			} else {
				return FALSE;			// not found
			}
		} else {
			return $filename;			// external file to be downloaded from interwebs
		}
		
	}
	
	public function __toString() {
		// if local, use file md5 as version
		$filename = $this->filename;
		if (!empty($filename) && $this->ext != 'less') {
			$x = parse_url($filename);
			$ver = $this->version;
			if (empty($x['host'])) {		// local file - get version if needed
				if ($ver === NULL) {
					$source_file = HOME . '/public/' . $x['path'];
					if (preg_match('!^/~/(.*?)/(.*)!', $x['path'], $match)) {	// special case - symlink does not exist
						$file = str_replace(['..', '&', '<', '>', '|'], '', $match[2]);
						$source_file = HOME . '/modules/' . $match[1] . '/static/' . $file;
						if (file_exists($source_file)) {
							$ver = substr(md5_file($source_file), 0 , 7);
						} else {
							$ver = 'missing';
						}
					} elseif (!file_exists($source_file)) {
						$ver = 'missing';
					} else {
						$ver = substr(md5_file($source_file), 0 , 7);
					}
				}
			}
			if (!empty($ver)) {
				$filename .= strpos($filename, '?') === FALSE ? '?' : '&';
				$filename .= 'ver=' . $ver;
			}
		}
		switch($this->ext) {
			case '':
				return $this->_localized_js('');
			
			case 'js':
				$res = '';
				if ($this->integrity !== NULL) $res .= " integrity=\"{$this->integrity}\"";
				if ($this->cross_origin !== NULL) $res .= " crossorigin=\"{$this->cross_origin}\"";
				foreach($this->attr as $key => $value) {
					$res .= " $key";
					if (!is_null($value)) {
						$res .= "=\"" . htmlspecialchars($value) . "\"";
					}
				}
                $type = $this->ContentType();
                if (empty($type)) $type = 'text/javascript';
				return $this->_localized_js("<script type=\"{$type}\" src=\"{$filename}\"$res></script>\n");

			case 'css':
                $type = $this->ContentType();
                if (empty($type)) $type = 'text/css';
				$res = "<link type=\"{$type}\" rel=\"stylesheet\" href=\"{$filename}\">\n";
				return $res . $this->_addcss();

			// cannot be an external file
			case 'less':		// convert less files to css
				if (($p = strrpos($filename, '.')) !== FALSE) {
					$ext = strtolower(substr($filename, $p + 1));
					if ($ext == 'less') $filename = substr($filename, 0, $p);
				}
//				$d = urlencode($filename);
				return "<link type=\"text/css\" rel=\"stylesheet\" href=\"/~/output/css{$filename}\">\n";
				
			case 'ico':
				return "<link type=\"image/x-icon\" rel=\"icon\" href=\"{$filename}\">\n";
			case 'png':
				return "<link type=\"image/png\" rel=\"icon\" href=\"{$filename}\">\n";
			case 'svg':
				return "<link type=\"image/svg+xml\" rel=\"icon\" href=\"{$filename}\">\n";

			case 'handlebars':
				return $this->handlebars;

			default:
				return '(unsupported file extension)';
		}
	}
	
	public function ApplyLate($callable) {
		$this->handlebars = call_user_func($callable, $this->handlebars);
	}
	
	public function AsArray() {
		return [
			'type'		=> $this->ext(),
			'content'	=> $this->__toString(),
			'md5'		=> $this->md5,
			'macro'		=> $this->macro(),
			'filename'	=> $this->LocalFilename(),
		];
	}

}

class http_status {
	
	private $status;
	private $message;
	private $note;
	private $errnum = FALSE;
	
	public function __construct($http_status = 200) {
		$this->status = $http_status;
		$this->note = $this->message = '';
	}
	
	public function message() {
		if (func_num_args()) {
			$this->message = func_get_arg(0);
			return $this;
		}
		return $this->message;
	}

	public function status() {
		if (func_num_args()) {
			$this->status = func_get_arg(0);
			return $this;
		}
		return $this->status;
	}
	
	public function note() {
		if (func_num_args()) {
			$this->note = func_get_arg(0);
			return $this;
		}
		return $this->note;
	}
	
	public function isError() {
		$status = intval($this->status / 100);
		return $status == 4 || $status == 5;
	}

	public function Errnum() {
		if (func_num_args()) {
			$this->errnum = func_get_arg(0);
			return $this;
		}
		return $this->errnum;
	}
	
}
