// this currently will bind all fields with 'data-bind' attributes
// to the 'store'. 

app.addSetter('databind.onInput', (data, bindPath, value) => {
    // console.log(`binding ${bindPath} to ${value}`);
    put(data, bindPath, value);

    /*!
    * Add items to an object at a specific path
    * (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
    * @param  {Object}       obj  The object
    * @param  {String|Array} path The path to assign the value to
    * @param  {*}            val  The value to assign
    */
    function put(obj, path, val) {

        /**
         * If the path is a string, convert it to an array
         * @param  {String|Array} path The path
         * @return {Array}             The path array
         */
        var stringToPath = function (path) {

            // If the path isn't a string, return it
            if (typeof path !== 'string') return path;

            // Create new array
            var output = [];

            // Split to an array with dot notation
            path.split('.').forEach(function (item, index) {

                // Split to an array with bracket notation
                item.split(/\[([^}]+)\]/g).forEach(function (key) {

                    // Push to the new array
                    if (key.length > 0) {
                        output.push(key);
                    }

                });

            });

            return output;

        };

        // Convert the path to an array if not already
        path = stringToPath(path);

        // Cache the path length and current spot in the object
        var length = path.length;
        var current = obj;

        // Loop through the path
        path.forEach(function (key, index) {

            // Check if the assigned key shoul be an array
            var isArray = key.slice(-2) === '[]';

            // If so, get the true key name by removing the trailing []
            key = isArray ? key.slice(0, -2) : key;

            // If the key should be an array and isn't, create an array
            if (isArray && Object.prototype.toString.call(current[key]) !== '[object Array]') {
                current[key] = [];
            }

            // If this is the last item in the loop, assign the value
            if (index === length - 1) {

                // If it's an array, push the value
                // Otherwise, assign it
                if (isArray) {
                    current[key].push(val);
                } else {
                    current[key] = val;
                }
            }

            // Otherwise, update the current place in the object
            else {

                // If the key doesn't exist, create it
                if (!current[key]) {
                    current[key] = {};
                }

                // Update the current place in the object
                current = current[key];

            }

        });

    };



});

Reef.databind = function(reef){

    let el = document.querySelector(reef.elem);
    let store = reef.store;

    if ( !store ){
        console.error("Databind only works when using a store.");
        return;
    }

    // bind all elements on the page that have a data-bind item
    const bindData = debounce(() => {
        let elems = el.querySelectorAll("[data-bind]");

        for ( let i = 0; i < elems.length; ++i ){
            let elem = elems[i];
            let bindName = elem.getAttribute("data-bind");

            if ( bindName ){
                let val = get(store.data, bindName, "");

                // dom values are strings, convert back so we can compare safely
                if ( typeof(val) == 'array' ){
                    let strs = [];
                    for ( let i = 0; i < val.length; ++i ){
                        strs.push(val[i].toString());
                    }
                    val = strs;
                } else {
                    val = val.toString();
                }
            
                // multiple selects need special handling
                if ( elem.tagName == "SELECT" && elem.matches("[multiple]") ){
                    let options = elem.querySelectorAll("option");
                    for ( let i = 0; i < options.length; ++i ){
                        if ( val.indexOf(options[i].value) > -1 ){
                            options[i].selected = true;
                        }
                    }
                } else if ( elem.type == "radio" || elem.type == "checkbox" ){
                    if ( elem.value == val.toString() ){
                        elem.checked = true;
                    } else {
                        elem.checked = false;
                    }

                } else {
                    elem.value = val;
                }
            }
        }
    });

        

    el.addEventListener('input', (evt) => {

        let target = evt.target;
        let bindPath = target.getAttribute("data-bind");
        
        if (bindPath) {
            let val = target.value;

            if ( target.type == "checkbox" ){
                if ( ! target.checked ){
                    val = null;
                }
            }

            // multiple selects need special handling
            else if ( target.tagName == 'SELECT' && target.matches("[multiple]") ){
                val = [];
                let options = target.querySelectorAll("option");
                for ( let i = 0; i < options.length; ++i ){
                    if ( options[i].selected ){
                        val.push(parseString(options[i].value));
                    }
                }
            }

            // for checkbox, radio, and select fields - map numeric and boolean values to
            // actual types instead of strings
            else if ( target.type == 'checkbox' || target.type == 'radio' ){
                val = parseString(val);
            }
            
            store.do('databind.onInput', bindPath, val);
            //put(store.data, bindPath, val);
        }
    });
   
    el.addEventListener('render', (evt) => {
        bindData();
    });

    // convert booleans and numbers
    function parseString(str){
        if ( str == "true" ){
            return true;
        } else if ( str == "false" ){
            return false;
        } else {
            return parseFloat(str) || str;
        }
    }


    /**
     * Debounce functions for better performance
     * (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
     * @param  {Function} fn The function to debounce
     */
    function debounce(fn) {

        // Setup a timer
        var timeout;

        // Return a function to run debounced
        return function () {

            // Setup the arguments
            var context = this;
            var args = arguments;

            // If there's a timer, cancel it
            if (timeout) {
                window.cancelAnimationFrame(timeout);
            }

            // Setup the new requestAnimationFrame()
            timeout = window.requestAnimationFrame(function () {
                fn.apply(context, args);
            });

        }

    };


    /*!
    * Get an object value from a specific path
    * (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
    * @param  {Object}       obj  The object
    * @param  {String|Array} path The path
    * @param  {*}            def  A default value to return [optional]
    * @return {*}                 The value
    */
    function get(obj, path, def) {

        /**
         * If the path is a string, convert it to an array
         * @param  {String|Array} path The path
         * @return {Array}             The path array
         */
        var stringToPath = function (path) {

            // If the path isn't a string, return it
            if (typeof path !== 'string') return path;

            // Create new array
            var output = [];

            // Split to an array with dot notation
            path.split('.').forEach(function (item) {

                // Split to an array with bracket notation
                item.split(/\[([^}]+)\]/g).forEach(function (key) {

                    // Push to the new array
                    if (key.length > 0) {
                        output.push(key);
                    }

                });

            });

            return output;

        };

        // Get the path as an array
        path = stringToPath(path);

        // Cache the current object
        var current = obj;

        // For each item in the path, dig into the object
        for (var i = 0; i < path.length; i++) {

            // If the item isn't found, return the default (or null)
            if (!current[path[i]]) return def;

            // Otherwise, update the current  value
            current = current[path[i]];

        }

        return current;

    };

}
