<?php

namespace SmartUI;

class UI {

    const SMARTUI_WIDGET = 'widget';
    const SMARTUI_DATATABLE = 'datatable';
    const SMARTUI_BUTTON = 'button';
    const SMARTUI_TAB = 'tab';

    private static $_ui_calls = array('create', 'set', 'get', 'print');
    private $_track_start_time;

    private static $_uis = array();
    private static $_alerts = array('info', 'danger', 'primary', 'default', 'warning', 'success');
    public static $icon_source = 'fa';
    public static $debug = true;

    public static function register($name, $class) {
        self::$_uis[$name] = $class;
    }

	protected function _call($ui_member, $structure, $name, $args) {
        if (property_exists($structure, $name)) {
            if (!$args) return $structure->{$name};

            $value = null;
            $key = null;
            if (count($args) > 1 && is_array($structure->{$name})) {
                $key = $args[0];
                $value = $args[1];
                if (!is_string($key) && !is_int($key)) {
                    self::err("SmartUI structure property: $name must be string or int.");
                    return null;
                }

                $structure->{$name}[$key] = $value;

                if (isset($args[2]) && Util::is_closure($args[2])) {
                    //process callback
                    $callback = $args[2];
                    Util::run_callback($callback, array($ui_member));
                }

                return $ui_member;
            } else {
                if (isset($args[1]) && Util::is_closure($args[1])) {
                    $value = $args[0];
                    $structure->{$name} = $value;
                    $callback = $args[1];
                    Util::run_callback($callback, array($ui_member));
                    return $ui_member;
                } else if (is_array($structure->{$name}) && (is_string($args[0]) || is_int($args[0]))) {
                    $key = $args[0];
                    if (!is_string($key) && !is_int($key)) {
                        self::err("SmartUI property key: $key must be string or int.");
                        return null;
                    }
                    return $structure->{$name}[$key];
                } else {
                    $value = $args[0];
                    $structure->{$name} = $value;
                    return $ui_member;
                }
            }
        }

        self::err('Undefined structure property: '.$name);

        return null;
    }

    public function start_track() {
        $this->_track_start_time = microtime(true);
    }

    public function __call($name, $args) {
        $calls = explode('_', $name);
        if (!in_array($calls[0], self::$_ui_calls)) {
            self::err("Undefined call: $calls[0]");
            return null;
        }

        $ui_class = strtolower($calls[1]);

        if (isset(self::$_uis[$ui_class]) && $calls[0] == 'create') {
            $reflection = new \ReflectionClass(self::$_uis[$ui_class]);
            $new_ui = $args ? $reflection->newInstanceArgs($args) : $reflection->newInstance();

            $this->start_track();
            return $new_ui;
        } else if ($calls[0] == 'print' && in_array($calls[1], self::$_alerts)) {
            $alert_args = array($args[0], $calls[1]);
            for ($i = 1; $i < count($args); $i++) {
                $alert_args[] = $args[$i];
            }
            return call_user_func_array(array($this, 'print_alert'), $alert_args);
        }

        self::err("\"$ui_class\" is not a registered member of SmartUI: Class not found");
    }

    public function run_time($print = true) {
        $time_end = microtime(true);
        $execution_time = number_format($time_end - $this->_track_start_time, 4);
        if ($print) echo $execution_time.'s';
        else return $execution_time.'s';
    }

	public static function err($message = "SmartUI Error notice:") {
        if (self::$debug) {
            $trace = debug_backtrace();
            trigger_error($message.' in '.$trace[0]['file'].' on line '.$trace[0]['line'], E_USER_NOTICE);
        }
	}

    public static function get_progress($value, $type = '', $options = array()) {
        $real_value = str_replace('%', '', $value);
        $percent_value = $real_value.'%';

        $options_map = array(
            'transitional' => false,
            'class' => array(),
            'attr' => array(),
            'background' => '',
            'container' => 'div'
        );

        $new_options_map = Util::set_array_prop_def($options_map, $options, 'class');

        $classes = array();
        $classes[] = 'progress-bar';
        if ($type) $classes[] = 'progress-bar-'.$type;

        // add additional user classes
        if (is_array($new_options_map['class'])) {
            array_merge($classes, $new_options_map['class']);
        } else {
            $classes[] = $new_options_map['class'];
        }

        if ($new_options_map['background']) $classes[] = 'bg-color-'.$new_options_map['background'];

        $attrs = array();
        $attrs_html = array();

        if ($new_options_map['transitional'])
            $attrs['aria-valuetransitiongoal'] = $real_value;
        else
            $attrs['style'] = 'width: '.$percent_value;


        // add additional user attributes
        if (is_array($new_options_map['attr'])) {
            array_merge($attrs, $new_options_map['attr']);
        } else {
            $attrs_html[] = $new_options_map['attr'];
        }

        foreach ($attrs as $attr => $attr_value) {
            $attrs_html[] = $attr.'="'.$attr_value.'"';
        }

        return'<'.$new_options_map['container'].' class="'.implode(' ', $classes).'" '.implode(' ', $attrs_html).'></'.$new_options_map['container'].'>';
    }

    public static function print_stack_progress($progress_bars, $base_options = array(), $return = false) {
        $options_map = array(
            'tooltip' => array(),
            'position' => 'left', // left, right, bottom (for vertical)
            'wide' => false,
            'size' => 'md', // sm, xs, md, xl, micro
            'striped' => false, // true or 'active'
            'vertical' => false
        );

        $new_options_map = Util::set_array_prop_def($options_map, $base_options, 'class');

        $container_classes = array();
        $container_classes[] = "progress";
        if ($new_options_map['vertical']) $container_classes[] = 'vertical';
        if ($new_options_map['position']) $container_classes[] = $new_options_map['position'];
        if ($new_options_map['wide']) $container_classes[] = 'wide-bar';
        if ($new_options_map['striped']) {
            $container_classes[] = 'progress-striped';
            if ($new_options_map['striped'] === 'active')
                $container_classes[] = 'active';
        }

        $container_classes[] = 'progress-'.$new_options_map['size'];

        $container_attrs = array();
        $container_attrs_html = array();

        if ($new_options_map['tooltip']) {
            $tooltip_prop =  array(
                'placement' => 'top',
                'title' => ''
            );
            $tooltip = $new_options_map['tooltip'];
            $new_tooltip_prop = Util::set_array_prop_def($tooltip_prop, $tooltip, 'title');
            $container_attrs['rel'] = 'tooltip';
            $container_attrs['data-original-title'] = $new_tooltip_prop['title'];
            $container_attrs['data-placement'] = $new_tooltip_prop['placement'];
        }

        foreach ($container_attrs as $container_attr => $attr_value) {
            $container_attrs_html[] = $container_attr.'="'.$attr_value.'"';
        }

        $result = '<div class="'.implode(' ', $container_classes).'" '.implode(' ', $container_attrs_html).'>';
        $result .= implode('', $progress_bars);
        $result .= '</div>';

        if ($return) return $result;
        else echo $result;
    }

    public static function print_progress($value, $type = '', $options = array(), $return = false) {
        $real_value = str_replace('%', '', $value);
        $percent_value = $real_value.'%';

        $options_map = array(
            'transitional' => false,
            'class' => array(),
            'attr' => array(),
            'background' => '',
            'tooltip' => array(),
            'position' => 'left', // left, right, bottom (for vertical)
            'wide' => false,
            'size' => 'md', // sm, xs, md, xl, micro
            'striped' => false, // true or 'active'
            'vertical' => false,
            'container' => 'div'
        );

        $new_options_map = Util::set_array_prop_def($options_map, $options, 'class');

        $container_classes = array();
        $container_classes[] = "progress";
        if ($new_options_map['vertical']) $container_classes[] = 'vertical';
        if ($new_options_map['position']) $container_classes[] = $new_options_map['position'];
        if ($new_options_map['wide']) $container_classes[] = 'wide-bar';
        if ($new_options_map['striped']) {
            $container_classes[] = 'progress-striped';
            if ($new_options_map['striped'] === 'active')
                $container_classes[] = 'active';
        }

        $container_classes[] = 'progress-'.$new_options_map['size'];

        $container_attrs = array();
        $container_attrs_html = array();

        if ($new_options_map['tooltip']) {
            $tooltip_prop =  array(
                'placement' => 'top',
                'title' => $percent_value
            );
            $tooltip = $new_options_map['tooltip'];
            $new_tooltip_prop = Util::set_array_prop_def($tooltip_prop, $tooltip, 'title');
            $container_attrs['rel'] = 'tooltip';
            $container_attrs['data-original-title'] = $new_tooltip_prop['title'];
            $container_attrs['data-placement'] = $new_tooltip_prop['placement'];
        }

        foreach ($container_attrs as $container_attr => $attr_value) {
            $container_attrs_html[] = $container_attr.'="'.$attr_value.'"';
        }

        $classes = array();
        $classes[] = implode(' ', $container_classes);
        $classes[] = implode(' ', $new_options_map['class'] ? $new_options_map['class'] : array());

        $container_attrs_html[] = implode(' ', $new_options_map['attr']);

        $result = '<'.$new_options_map['container'].' class="'.implode(' ', $classes).'" '.implode(' ', $container_attrs_html).'>';
        $result .= self::get_progress($value, $type, $options);
        $result .= '</'.$new_options_map['container'].'>';

        if ($return) return $result;
        else echo $result;
    }

    public static function print_alert($message, $type = 'info', $options = array(), $return = false) {
        $options_map = array(
            'closebutton' => true,
            'block' => false,
            'container' => 'div',
            'class' => array(),
            'fade_in' => true,
            'icon' => $type,
            'js_escape' => false,
            'heading' => false
        );

        $icon_map = array(
            'info' => self::$icon_source.'-info',
            'warning' => self::$icon_source.'-alert '.self::$icon_source.'-warning',
            'danger' => self::$icon_source.'-times '.self::$icon_source.'-x',
            'success' => ''.self::$icon_source.'-check'
        );
        $new_options_map = Util::set_array_prop_def($options_map, $options, 'class');
        $closebutton_html = $new_options_map['closebutton'] ?
                        '<button class="close" data-dismiss="alert">
                            ×
                        </button>' : '';

        $classes = array();
        $classes[] = 'alert';
        $classes[] = 'alert-'.$type;
        if ($new_options_map['fade_in']) $classes[] = 'fade in';
        if ($new_options_map['block']) $classes[] = 'alert-block';

        if ($new_options_map['icon']) {
            $icon = (isset($icon_map[$new_options_map['icon']]) ? $icon_map[$new_options_map['icon']] : self::$icon_source.'-'.$new_options_map['icon']);
            $icon_html = '<i class="'.self::$icon_source.' '.$icon.'"></i>';
        } else $icon_html = '';

        $heading = '';
        if ($new_options_map['heading']) {
            $heading = '<h4 class="alert-heading">'.$icon_html.' '.$new_options_map['heading'].'</h4>';
            $icon_html = '';
        }

    	$result = '<div class="'.implode(' ', $classes).'">
                        '.$closebutton_html.'
                        '.$heading.'
                        '.$icon_html.'
                        '.$message.'
                    </div>';

        if ($new_options_map['js_escape']) $result = preg_replace("/\s+/", " ", $result);

    	if ($return) return $result;
    	else echo $result;
    }

    public static function print_list($items, $options = array(), $return = false) {

        $items_html = '';
        $main_attrs = array();

        $default_options = array(
            'type' => 'ul',
            'attr' => array(),
            'class' => array(),
            'id' => ''
        );

        $new_options = Util::set_array_prop_def($default_options, $options, 'class');

        foreach ($items as $item) {
            $item_html = '';
            $item_prop = array(
                'content' => '',
                'subitems' => array(),
                'subitem_options' => array(),
                'class' => '',
                'attr' => array()
            );

            $new_item_prop = Util::get_clean_structure($item_prop, $item, array($item), 'content');

            $content = $new_item_prop['content'];

            if ($new_item_prop['subitems']) {
                $content .= self::print_list($new_item_prop['subitems'], $new_item_prop['subitem_options'], true);
            }

            $attrs = array();
            if ($new_item_prop['class']) $attrs[] = 'class="'.(is_array($new_item_prop['class']) ? implode(' ', $new_item_prop['class']) : $new_item_prop['class']).'"';

            if ($new_item_prop['attr']) {
                foreach ($new_item_prop['attr'] as $key => $value) {
                    $attrs[] = $key.'="'.$value.'"';
                }
            }

            $items_html .= '
                <li'.($attrs ? ' '.implode(' ', $attrs) : '').'>
                    '.$content.'
                </li>
            ';
        }

        if ($new_options['class']) $main_attrs[] = 'class="'.(is_array($new_options['class']) ? implode(' ', $new_options['class']) : $new_options['class']).'"';
        if ($new_options['attr']) {
            foreach ($new_options['attr'] as $key => $value) {
                $main_attrs[] = $key.'="'.$value.'"';
            }
        }

        if ($new_options['id']) $main_attrs[] = 'id="'.$new_options['id'].'"';

        $result = '
            <'.$new_options['type'].($main_attrs ? ' '.implode(' ', $main_attrs) : '').'>
                '.$items_html.'
            </'.$new_options['type'].'>
        ';

        if ($return) return $result;
        else echo $result;
    }

    public static function print_dropdown($items, $options = array(), $return = false) {

        $items_html = '';

        $main_attrs = array();

        $default_options = array(
            'type' => 'ul',
            'attr' => array(),
            'class' => '',
            'id' => '',
            'multilevel' => false
        );

        $new_options = Util::set_array_prop_def($default_options, $options, 'class');

        foreach ($items as $item) {
            $item_html = '';
            $item_prop = array(
                'content' => '',
                'submenu' => array(),
                'class' => array(),
                'attr' => array()
            );

            $new_item_prop = Util::get_property_value($item, array(
                'if_array' => function($item) use ($item_prop) {
                    return Util::set_array_prop_def($item_prop, $item, 'content');
                },
                'if_closure' => function($item) use ($item_prop) {
                    return Util::set_closure_prop_def($item_prop, $item);
                },
                'if_other' => function($item) use ($item_prop) {
                    $item_prop['content'] = $item;
                    return $item_prop;
                }
            ));

            $classes = array();
            if ($new_item_prop['class'])
                $classes[] = is_array($new_item_prop['class']) ? implode(' ', $new_item_prop['class']) : $new_item_prop['class'];

            $attrs = array();

            if ($new_item_prop['attr']) {
                foreach ($new_item_prop['attr'] as $key => $value) {
                    $attrs[] = $key.'="'.$value.'"';
                }
            }

            $content = $new_item_prop['content'];

            if ($new_item_prop['submenu']) {
                $content .= self::print_dropdown($new_item_prop['submenu'], null, true);
                $classes[] = 'dropdown-submenu';
            } else if ($content === '-') {
                $classes[] = 'divider';
            } else if (preg_match("/##(.*)?##/", $content, $header_matches)) {
                $classes[] = 'dropdown-header';
                $content = trim($header_matches[1]);
            }

            $attrs[] = $classes ? ' class="'.trim(implode(' ', $classes)).'"' : '';

            $item_html = '<li'.($attrs ? ' '.implode(' ', $attrs) : '').'>'.$content.'</li>';
            $items_html .= $item_html;
        }

        $classes = array('dropdown-menu');
        if ($new_options['multilevel']) $classes[] = 'multi-level';
        $main_attrs[] = 'role="menu"';

        if ($new_options['class']) $classes[] = $new_options['class'];
        if ($new_options['attr']) {
            foreach ($new_options['attr'] as $key => $value) {
                $main_attrs[] = $key.'="'.$value.'"';
            }
        }

        if ($new_options['id']) $main_attrs[] = 'id="'.$new_options['id'].'"';

        $main_attrs[] = 'class="'.implode(' ', $classes).'"';

        $result = '<ul '.implode(' ', $main_attrs).'>';
        $result .= $items_html;
        $result .= '</ul>';

        if ($return) return $result;
        else echo $result;
    }
}


?>