<?php
// Copyricht (c) MetaClass Groningen, 2003-2015

namespace VisualM\ManagementBundle\Util;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class Gen {
    
    /** @return boolean wheather the browser is Microsoft Internet Explorer */
    static function isBrowserIE() 
    {
        return isSet($_SERVER['HTTP_USER_AGENT']) &&
        preg_match("/MSIE /i", $_SERVER['HTTP_USER_AGENT']);
    }
    
    /** 
     * Assumes the bundle name is the last name that ends with 'Bundle\' postfix
     * @param mixed $objectOrName the object, fully qualified name or subnamespace
     * @return string the namespace of the Bundle, or the entire name if postfix is not found
     */
    static function getBundleNameSpace($objectOrName)
    {
        if (is_object($objectOrName)) {
            $objectOrName = get_class($objectOrName);
        }
        $lastBundlePostfix = strRpos($objectOrName, 'Bundle\\');
        if ($lastBundlePostfix == false) { //assume it is already the bundle namespace
            return $objectOrName;
        }
        return subStr($objectOrName, 0, $lastBundlePostfix + 6);
    }
    
    /** For debugging purposes, for user interface string use StringConverter
     * @return string from which the value van be recognized
     * @param mixed $value
     * @param int $max the maximum number of array elements to be shown.
     * 		If not all array elements are shown, ' ..' is added after the last element */
    static function toString($value, $max=10)
    {
        if (!is_array($value)) {
            return Gen::valueToString($value);
        }
        //array
        $result = "array(";
        $result .= implode(", ", Gen::assocsToStrings($value, $max));
        if ($max && count($value) > $max) {
            $result .= " ..";
        }
        $result .= ")";
        return $result;
    }
    
    /** For debugging purposes, for user interface string use StringConverter
     * @return string from which the value van be recognized
     * @param mixed $value anything but an array */
    static function valueToString($value) 
    {
        if ($value===null) {
            return 'NULL';
        }
        if ( is_bool($value) ) {
            return ($value ? 'true' : 'false');
        }
        if ( is_string($value) ) {
            return "'$value'";
        }
        if ($value instanceof Exception) {
            return Gen::exceptionToString($value);
        }
        if ( is_object($value)) {
            if (method_exists($value, '__toString') ) {
                return $value->__toString(); //had trouble with cast not calling __toString()
            }
            if ($value instanceof \DateTime) {
                return $value->format('Y-m-d H:i:s');
            }
            if (method_exists($value, 'toString') ) {
                return $value->toString(); //depricated support
            } else {
                return 'a '.get_class($value);
            }
        }
        if (is_array($value)) {
            return 'Array';
        }
        return (string) $value;
    }
    
    /** For debugging purposes, for user interface string use StringConverter
    * @return string from which the value van be recognized
    * @param Exception $e  */
    static function exceptionToString($e) {
        return get_class($e). '('. $e->getCode(). ' '. $e->getMessage(). ')';
    }
    
     /**
     * @param Array $array an associative array
     * @param int $max maximim number of elements to include. If null all are included and
     *   Gen::toString is used on the values instead of Gen::valueToString.
     * @return array with strings representing the associations
     */
     static function assocsToStrings($array, $max=null) 
     {
         $result = array();
         $count = 0;
         while ( ($max==null || $count <= $max) && (list($key) = each($array)) ) 
         {
             $result[] = Gen::valueToString($key).'=>'
                 . ($max===null ? Gen::toString($array[$key]) : Gen::valueToString($array[$key]) );
             $count++;
         }
         return $result;
     }

     /**
     * @param object $value
     * @return string from which the object can be recognized within its class,
     * 	if not available ::valueToString is used
     */
     static function labelFromObject($value) 
     {
         if (method_exists($value, 'getLabel') ) {
             return $value->getLabel();
         }
         return Gen::valueToString($value);
     }

     /**
     * @param mixed $value anything but an array
     * @return string from which the value can be recognized within its class,
     * 	if not an object, the value is casted to string
     */
     static function labelFrom($value) 
     {
         if (is_object($value)) {
             return Gen::labelFromObject($value);
         } else {
    		return (string) $value;
         }
	}
    
	static function show($something) 
	{
		print "<pre>";
		print "\n\n##################################\n";	
		if (is_array($something) || is_object($something)) {
		    print (is_array($something) ? 'array' : get_class($something)). "\n\t";
		    print implode("\n\t", Gen::assocsToStrings((array) $something) );
		} else {
		    if (!is_null($something)) {
		        print getType($something). "\n\t";
		    }
    		print Gen::valueToString($something);
		}
		print "\n##################################\n";
		print "</pre>\n";
        return $something;
	}
	
	/** Prints a backtrace in html.
	 * @param array $traceArray array from php's debug_backtrace method
	 */
	static function printBacktrace($traceArray) 
	{
    	print "\n<TABLE border=1 cellspacing=0>
    	<THEAD>
                 <TR bgcolor='#CCCCCC'>
                 <TD>obj/cls</TD><TD>func</TD><TD>line</TD><TD>file</TD>
			</TR>
    			</THEAD>
                 <TBODY>";
		$deepest = count($traceArray);
		for ($i=0; $i <= $deepest ; $i++) 
		{
    	    if (isSet($traceArray[$i])) {
    	        $frame = $traceArray[$i];
    	    } else {
                $frame = array('function' => '-', 'args' => array() );
    	    }
            print "\n<TR vAlign=top><TD>";
            if (isSet($frame['object']) && !Gen::is_ofType($frame['object'], 'Exception')) {
                 print Gen::toString($frame['object']);
            } else {
                print isSet($frame['class']) ? $frame['class'] : '-';
            }
            print "</TD><TD>";
			print $frame['function'];
			print "</TD><TD>";
			print isSet($prev['line']) ? $prev['line'] : '&nbsp;';
			print "</TD><TD>";
			print isSet($prev['file']) ? Gen::getRelativePath($prev['file']) : '&nbsp;';
			print "</TD></TR>";
			if (isSet($frame['args']) && count($frame['args']) > 0) {
				print "\n<TR><TD colspan=4><PRE>";
				for ($j=0; $j < count($frame['args']); $j++) 
				{
					print "\n	". htmlEntities(Gen::toString($frame['args'][$j]));
				}
				print " </PRE></TD></TR>";
			}
    		$prev = $frame ;
		}
		print "
    	</TBODY>
    </TABLE>";
	}
    
	/*** @return string with qualified strings separated by a separator
	 * @param array $array to be imploded
	* @param string $separator by which the elements are to be seperated
	* @param string $qualifier by wich the elements are surrounded.
	*   The qualifier itself is doubled. */
	static function toCsvString($array, $separator=';', $qualifier='"') 
	{
	    $result = '';
	    reset($array);
	    $firstKey = key($array);
	    $dblQf = "$qualifier$qualifier";
	    while (list($key, $value) = each($array)) 
	    {
	        if ($key !== $firstKey) {
	            $result .= $separator;
	        }
	        $result .= $qualifier;
	        $result .= str_replace($qualifier, $dblQf, Gen::toString($value));
	        $result .= $qualifier;
	    }
	    return $result;
	}
    	
	//
	/** explodes csv string
	* only to be used if strings are qualified, if not, use normal explode
	* @param string $str with qualified strings separated by a separator
	* @param string $separator by which the elements are seperated
	* @param string $qualifier by wich the elements are surrounded.
	*   The qualifier itself is expected to be doubled witing the qualified string.
	* @return array of string
	*/
	static function fromCsvString($str, $separator=';', $qualifier='"') 
	{
    	$arr = explode($separator, $str);
    	$result = array();
    	$quoteMode = false;
    	$quoteKey = ord($qualifier);
    	$dblQf = "$qualifier$qualifier";
    	for ($i=0; $i<count($arr); $i++) 
    	{
        	$unQualified = str_replace($dblQf, $qualifier, $arr[$i]);
        	if ($quoteMode) {
        	    $result[count($result) - 1] .= $separator. $unQualified;
        	} else {
        	    $result[] = $unQualified;
        	}
        	$counts = count_chars($arr[$i], 1);
        	if(isSet($counts[$quoteKey]) && $counts[$quoteKey] & 1) {// odd
            	$quoteMode = !$quoteMode;
        	}
    	}
    	for ($i=0; $i<count($result); $i++)
    	{
        	if (subStr($result[$i], 0, 1) == $qualifier) {
        	    $result[$i] = subStr($result[$i], 1, strLen($result[$i]) - 2);
        	}
    	}
	    return $result;
	}
	
    public static function array_remove(&$array, $value)
    {
        forEach(array_keys($array, $value) as $key)
        {
            unSet($array[$key]);
        }
    }

    public function nullOrCall($object, $methodName, $arguments = array())
    {
        if ($object === null) return null;
        return call_user_func_array(array($object, $methodName), $arguments);
    }

    /**
     * @return boolean wheather $class is a subclass or equal to $type
     * @param string $class fully qualified class name
     * @param string $type fully qualified class or interface name
     * On php.net it seems as if is_a and instanceOf may also do
     * what this function does, but when tested it did not work.
     */
    public function isSubbclassOrIs($class, $type)
    {
        return $class == $type || is_subclass_of($class, $type);
    }
}

?>