"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
    for (var r = Array(s), k = 0, i = 0; i < il; i++)
        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
            r[k] = a[j];
    return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = require("react");
var sdk_1 = require("@builder.io/sdk");
var device_sizes_constant_1 = require("../constants/device-sizes.constant");
var builder_store_1 = require("../store/builder-store");
var isArray_1 = require("lodash-es/isArray");
var last_1 = require("lodash-es/last");
var set_1 = require("lodash-es/set");
var includes_1 = require("lodash-es/includes");
var omit_1 = require("lodash-es/omit");
var kebabCase_1 = require("lodash-es/kebabCase");
var builder_async_requests_1 = require("../store/builder-async-requests");
var string_to_function_1 = require("../functions/string-to-function");
var react_native_1 = require("react-native");
var Device = { desktop: 0, tablet: 1, mobile: 2 };
var cssCase = function (property) {
    if (!property) {
        return property;
    }
    var str = kebabCase_1.default(property);
    if (property[0] === property[0].toUpperCase()) {
        str = '-' + str;
    }
    return str;
};
// TODO: pull from builer internal utils
var fastClone = function (obj) { return JSON.parse(JSON.stringify(obj)); };
function capitalize(str) {
    if (!str) {
        return;
    }
    return str[0].toUpperCase() + str.slice(1);
}
var BuilderBlock = /** @class */ (function (_super) {
    __extends(BuilderBlock, _super);
    function BuilderBlock() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.privateState = {
            state: {},
            update: function () {
                /* Intentionally empty */
            },
        };
        return _this;
    }
    // TODO: handle adding return if none provided
    // TODO: cache/memoize this (globally with LRU?)
    BuilderBlock.prototype.stringToFunction = function (str, expression) {
        if (expression === void 0) { expression = true; }
        return string_to_function_1.stringToFunction(str, expression, this._errors, this._logs);
    };
    Object.defineProperty(BuilderBlock.prototype, "styles", {
        get: function () {
            // TODO: handle style bindings
            var _a = this.props, block = _a.block, size = _a.size;
            if (!size) {
                // TODO: use device size API for this
                size = 'small';
            }
            var styles = [];
            var startIndex = 0; // sizeNames.indexOf(size || 'large');
            if (block.responsiveStyles) {
                for (var i = startIndex; i < device_sizes_constant_1.sizeNames.length; i = i + 1) {
                    var name_1 = device_sizes_constant_1.sizeNames[i];
                    if (block.responsiveStyles[name_1]) {
                        styles.push(block.responsiveStyles[name_1]);
                    }
                }
            }
            // TODO: animations
            var finalStyleObject = Object.assign.apply(Object, __spreadArrays([{}], styles.reverse()));
            for (var key in finalStyleObject) {
                var value = finalStyleObject[key];
                if (typeof value === 'string') {
                    var matched = value.trim().match(/^([\d\.]+)(px)?$/);
                    if (matched) {
                        finalStyleObject[key] = parseFloat(matched[1]);
                    }
                    if (value.includes('calc(')) {
                        // console.warn('calc not supported');
                        delete finalStyleObject[key];
                    }
                    if (value.includes('vw')) {
                        // console.warn('calc not supported');
                        delete finalStyleObject[key];
                    }
                }
            }
            // TODO: convert transform string to array/object form for react native with regex
            return omit_1.default(finalStyleObject, 'boxSizing', 'transform');
        },
        enumerable: true,
        configurable: true
    });
    BuilderBlock.prototype.componentDidMount = function () {
        var block = this.props.block;
        var animations = block && block.animations;
        // tslint:disable-next-line:comment-format
        ///REACT15ONLY if (this.ref) { this.ref.setAttribute('builder-id', block.id); }
        if (animations) {
            var options = {
                animations: fastClone(animations),
            };
            // TODO: listen to Builder.editingMode and bind animations when editing
            // and unbind when not
            // TODO: apply bindings first
            if (block.bindings) {
                for (var key in block.bindings) {
                    if (key.startsWith('animations.')) {
                        var value = this.stringToFunction(block.bindings[key]);
                        if (value !== undefined) {
                            set_1.default(options, key, value(this.privateState.state));
                        }
                    }
                }
            }
            // TODO: animations
        }
    };
    // <!-- Builder Blocks --> in comments hmm
    BuilderBlock.prototype.getElement = function (index, state) {
        var _this = this;
        if (index === void 0) { index = 0; }
        if (state === void 0) { state = this.privateState.state; }
        var _a = this.props, block = _a.block, child = _a.child, fieldName = _a.fieldName;
        var TagName = react_native_1.View; //  (block.tagName || 'div').toLowerCase();
        if (block.actions && block.actions.click) {
            // TODO: add back
            // TagName = TouchableWithoutFeedback as any;
        }
        var InnerComponent;
        var componentName = block.component && (block.component.name || block.component.component);
        var componentInfo = null;
        if (block.component && !block.component.class) {
            if (block.component && block.component.tag) {
                InnerComponent = block.component.tag;
            }
            else {
                componentInfo = sdk_1.Builder.components.find(function (item) { return item.name === componentName; }) || null;
                if (componentInfo && componentInfo.class) {
                    InnerComponent = componentInfo.class;
                }
                else if (componentInfo && componentInfo.tag) {
                    InnerComponent = componentInfo.tag;
                }
            }
        }
        var isBlock = !includes_1.default(['absolute', 'fixed'], block.responsiveStyles &&
            block.responsiveStyles.large &&
            block.responsiveStyles.large.position /*( this.styles.position */);
        var options = __assign(__assign({}, block.properties), { style: {} });
        options = __assign(__assign({}, options.properties), options);
        if (block.component) {
            options.component = fastClone(block.component);
        }
        // Binding should be properties to href or href?
        // Manual style editor show bindings
        // Show if things bound in overlays hmm
        if (block.bindings) {
            for (var key in block.bindings) {
                var value = this.stringToFunction(block.bindings[key]);
                // TODO: pass block, etc
                set_1.default(options, key, value(state, null, block, {}, Device));
            }
        }
        if (options.hide) {
            return null;
        }
        var latestState = state;
        if (block.actions) {
            var _loop_1 = function (key) {
                var value = block.actions[key];
                if (key === 'click') {
                    key = 'press';
                }
                options['on' + capitalize(key)] = function (event) {
                    var update = function (cb) {
                        _this.privateState.update(function (globalState) {
                            latestState = globalState;
                            var localState = globalState;
                            if (typeof Proxy !== 'undefined') {
                                localState = new Proxy(__assign({}, globalState), {
                                    getOwnPropertyDescriptor: function (target, property) {
                                        try {
                                            return Reflect.getOwnPropertyDescriptor(latestState, property);
                                        }
                                        catch (error) {
                                            return undefined;
                                        }
                                    },
                                    // TODO: wrap other proxy properties
                                    set: function (target, key, value) {
                                        // TODO: do these for deep sets from references hmm
                                        return Reflect.set(latestState, key, value);
                                        // return false;
                                    },
                                    // to prevent variable doesn't exist errors with `with (state)`
                                    has: function (target, property) {
                                        try {
                                            // TODO: if dead trigger an immer update
                                            return Reflect.has(latestState, property);
                                        }
                                        catch (error) {
                                            return false;
                                        }
                                    },
                                    get: function (object, property) {
                                        if (property &&
                                            typeof property === 'string' &&
                                            property.endsWith('Item') &&
                                            !Reflect.has(latestState, property)) {
                                            // TODO: use $index to return a reference to the proxied version of item
                                            // so can be set as well
                                            return Reflect.get(state, property);
                                        }
                                        return Reflect.get(latestState, property);
                                    },
                                });
                            }
                            return cb(localState, event, undefined, {}, Device, update);
                        });
                    };
                    var fn = _this.stringToFunction(value, false);
                    update(fn);
                };
            };
            for (var key in block.actions) {
                _loop_1(key);
            }
        }
        var innerComponentProperties = (options.component || options.options) && __assign(__assign({}, options.options), (options.component.options || options.component.data));
        var isVoid = false;
        var noWrap = componentInfo && (componentInfo.fragment || componentInfo.noWrap);
        var finalOptions = __assign(__assign(__assign({}, omit_1.default(options, 'class', 'component')), { style: this.styles, class: "builder-block " + this.id + (block.class ? " " + block.class : '') + (block.component && !includes_1.default(['Image', 'Video', 'Banner'], componentName)
                ? " builder-has-component"
                : '') + (options.class ? ' ' + options.class : ''), key: this.id + index, 
            // Remove for amp mode
            'builder-id': this.id }), (index !== 0 && {
            'builder-index': index,
        }));
        var children = block.children || finalOptions.children || [];
        // TODO: test it out
        return (react_1.default.createElement(builder_async_requests_1.BuilderAsyncRequestsContext.Consumer, null, function (value) {
            _this._errors = value && value.errors;
            _this._logs = value && value.logs;
            return isVoid ? (react_1.default.createElement(react_1.default.Fragment, null,
                react_1.default.createElement(TagName, __assign({}, finalOptions)))) : InnerComponent && (noWrap || _this.props.emailMode) ? (
            // TODO: pass the class to be easier
            // TODO: acceptsChildren option?
            react_1.default.createElement(react_1.default.Fragment, null,
                react_1.default.createElement(InnerComponent
                // Final options maaay be wrong here hm
                , __assign({}, innerComponentProperties, { attributes: finalOptions, builderBlock: block })))) : (react_1.default.createElement(TagName, __assign({}, finalOptions),
                InnerComponent && (react_1.default.createElement(InnerComponent, __assign({ builderBlock: block }, innerComponentProperties))),
                !InnerComponent && children && children.length
                    ? children.map(function (block, index) { return (react_1.default.createElement(BuilderBlock, { key: (_this.id || '') + index, block: block, index: index, size: _this.props.size, fieldName: _this.props.fieldName, child: _this.props.child, emailMode: _this.props.emailMode })); })
                    : null));
        }));
    };
    Object.defineProperty(BuilderBlock.prototype, "id", {
        get: function () {
            var block = this.props.block;
            if (!block.id.startsWith('builder')) {
                return 'builder-' + block.id;
            }
            return block.id;
        },
        enumerable: true,
        configurable: true
    });
    BuilderBlock.prototype.contents = function (state) {
        var _this = this;
        var block = this.props.block;
        // this.setState(state);
        this.privateState = state;
        if (block.repeat && block.repeat.collection) {
            var collectionPath = block.repeat.collection;
            var collectionName = last_1.default((collectionPath || '').trim().split('(')[0].trim().split('.'));
            var itemName_1 = block.repeat.itemName || (collectionName ? collectionName + 'Item' : 'item');
            var array = this.stringToFunction(collectionPath)(state.state, null, block, {}, Device);
            if (isArray_1.default(array)) {
                return array.map(function (data, index) {
                    var _a;
                    // TODO: Builder state produce the data
                    var childState = __assign(__assign({}, state.state), (_a = { $index: index, $item: data }, _a[itemName_1] = data, _a));
                    return (react_1.default.createElement(builder_store_1.BuilderStoreContext.Provider, { key: index, value: __assign(__assign({}, state), { state: childState }) }, _this.getElement(index, childState)));
                });
            }
            return null;
        }
        return this.getElement();
    };
    BuilderBlock.prototype.render = function () {
        var _this = this;
        return (react_1.default.createElement(builder_store_1.BuilderStoreContext.Consumer, null, function (value) { return _this.contents(value); }));
    };
    return BuilderBlock;
}(react_1.default.Component));
exports.BuilderBlock = BuilderBlock;
//# sourceMappingURL=builder-block.component.js.map