//#region ItemData
function ItemData(observer, options) {
    this.oldValueKey = 'taskFormHub_oldValue';
    this.observer = observer;
    var thatItemData = this;
    this._options = {
        //имя блока или инпута
        name: '',
        //селектор к блоку или инпуту
        selector: '',
        //если true, то на блок или инпут вешается событие keyup и change
        //которое отправляет данные на сервер
        useDefaultEvent: true,
        initConflictAfterSubmit: {
            compareEqualsCurrentWithSubmitValue: true,
            canConflict: true
        }
    };

    this._options = $.extend({}, this._options, options);

    if (!this.IsOnForm()) {
        return;
    }

    this.InitTaskRightsDisplayType();

    //чтобы повторно не отправлять инфу что блок изменился
    this.isSendBlockChangeEvent = false;
    this.usersEditThisBlock = [];

    if (this._options.useDefaultEvent && this.TaskRightsDisplayType() == ItemDataHelper.TaskRightsDisplayType.Edit) {
        $(this._options.selector).on('keyup change', function () {
            thatItemData.SendBlockChange();
        });
    }

    this.isConflict = false;
    this.$conflictMessageElement = undefined;

    this.SetOldValue();

    return this;
}

ItemData.oldValueKey = 'taskFormHub_oldValue';
ItemData.taskRightsDisplayTypeKey = 'taskrights-displaytype';

//#region ItemData.prototype

ItemData.prototype.IsOnForm = function () {
    return $(this._options.selector).length > 0;
}

ItemData.prototype.InitTaskRightsDisplayType = function () {
    var $block = $(this._options.selector);
    var $field = $block.closest('.field');
    this.taskRightsDisplayType = ItemDataHelper.TaskRightsDisplayType[$field.attr(ItemData.taskRightsDisplayTypeKey)] || ItemDataHelper.TaskRightsDisplayType.None;
}

ItemData.prototype.TaskRightsDisplayType = function () {
    return this.taskRightsDisplayType || ItemDataHelper.TaskRightsDisplayType.None;
}

//#region ItemData.prototype change blocks

ItemData.prototype.GetBlockTitle = function () {
    var $block = $(this._options.selector);
    var $field = $block.closest('.field');
    var $label = $field.find('label');
    return $label.clone().children().remove().end().text().trim();
}

ItemData.prototype.GetSelectorType = function () {
    var $selector = $(this._options.selector);
    if ($selector.is('textarea'))
        return 'textarea';
    if ($selector.is('select'))
        return 'select';
    if ($selector.hasClass('dateTime'))
        return 'datetime';
    if ($selector.hasClass('numeric'))
        return 'numeric';

    return 'string';
}

//куда мы вставляем наш span
//по дефолту после label в блоке .field
//part1 - системная
ItemData.prototype.Append1 = function (userConnectionId, userId, userName, data) {
    var existUserConnectionId = false;
    for (i = 0; i < this.usersEditThisBlock.length; i++) {
        if (this.usersEditThisBlock[i].userConnectionId == userConnectionId) {
            existUserConnectionId = true;
            break;
        }
    }

    if (!existUserConnectionId) {
        this.usersEditThisBlock.push({
            userConnectionId: userConnectionId,
            userId: userId,
            userName: userName
        });
    }

    if (this.TaskRightsDisplayType() != ItemDataHelper.TaskRightsDisplayType.None /* 'None' */) {
        this.Append2(userConnectionId, userId, userName, data, existUserConnectionId);
    }
}
//part2 - пользовательская
ItemData.prototype.Append2 = function (userConnectionId, userId, userName, data, existUserConnectionId) {
    var $block = $(this._options.selector);
    var $field = $block.closest('.field');
    var $label = $field.find('label');
    $field.find('span.taskFormHub-blockInfo').remove();
    var $span = this.GetSpanTemplate(userConnectionId, userId);
    $label.after($span);
    this.Highlight();
}

//удалить инфу о всех юзерах которые изменили блок
//вызывает при дисконекте
ItemData.prototype.ResetAll = function () {
    this.usersEditThisBlock = [];
    var $block = $(this._options.selector);
    var $field = $block.closest('.field');
    //удалить текущий span
    $field.find('span.taskFormHub-blockInfo').remove();
    this.Unhighlight();
}
//как удалить span если пользователь отменил изменения
//part1 - системная
ItemData.prototype.Reset1 = function (userConnectionId, userId, userName) {
    //$.each(usersEditThisBlock, function (k, v) {
    //    if (v.userConnectionId == userConnectionId) {
    //        delete usersEditThisBlock[k];
    //    }
    //});

    this.usersEditThisBlock = this.usersEditThisBlock.filter(function (item) {
        return item.userConnectionId !== userConnectionId;
    });

    this.Reset2(userConnectionId, userId, userName);
}
//part2 - пользовательская
ItemData.prototype.Reset2 = function (userConnectionId, userId, userName) {
    var $block = $(this._options.selector);
    var $field = $block.closest('.field');
    var $label = $field.find('label');
    //удалить текущий span
    $field.find('span.taskFormHub-blockInfo').remove();
    //если есть еще пользователи которые редактируют блок
    if (this.usersEditThisBlock.length > 0 && this.TaskRightsDisplayType() != ItemDataHelper.TaskRightsDisplayType.None) {
        //сделать новый span
        var $span = this.GetSpanTemplate(userConnectionId, userId);
        $label.after($span);
        //подстветить
        this.Highlight();
    }
    else {
        this.Unhighlight();
    }
}

//функция определяет было ли изменение значения блока или инпута
ItemData.prototype.IsChanged = function () {
    var cur = this.GetCurrentVal();
    var old = this.GetOldValue();
    return cur != old;
}

//функция подсветки блока
ItemData.prototype.Highlight = function () {
    $(this._options.selector).addClass('taskFormHub-highlightBlock');
}

//функция убирает подсветку блока
ItemData.prototype.Unhighlight = function () {
    $(this._options.selector).removeClass('taskFormHub-highlightBlock');
}

ItemData.prototype.tmpl_spanTemplateBase =
    tmpl('<span class="taskFormHub-blockInfo" title="{%=o.title%}">\
                <i class="fa fa-pencil" aria-hidden="true"></i><i class="taskFormHub-loading"></i>({%=o.count%})\
         </span>');
//функция возвращает шаблон подсказки когда блок изменен другим пользователем
ItemData.prototype.GetSpanTemplate = function (userConnectionId, userId) {
    var that = this;
    var $span = $(this.tmpl_spanTemplateBase(
        {
            title: $.map(that.usersEditThisBlock, function (item) { return item.userName }).join('<br>'),
            count: that.usersEditThisBlock.length
        }));
    $span.tooltip();
    return $span;
}

ItemData.prototype.GetOldValue = function () {
    return $(this._options.selector).data(ItemData.oldValueKey);
}

ItemData.prototype.GetCurrentVal = function () {
    return $(this._options.selector).val();
}

//функция чтобы запомнить неизмененное значение блока
ItemData.prototype.SetOldValue = function (forceVal) {
    $(this._options.selector).data(ItemData.oldValueKey, (forceVal || this.GetCurrentVal()));
    this.isSendBlockChangeEvent = false;
}

ItemData.prototype.GetSendBlockChangeData = function () {
    return {};
}
ItemData.prototype.SendBlockChange = function () {
    if (!this.IsOnForm() || this.TaskRightsDisplayType() == ItemDataHelper.TaskRightsDisplayType.None /* 'None' */) {
        return;
    }

    if (this.observer.isHubStarted()) {
        var isChange = this.IsChanged();
        if ((isChange && !this.isSendBlockChangeEvent) || !isChange) {
            this.isSendBlockChangeEvent = isChange;
            this.observer.ToServerInvoke('BlockChanged', this._options.name, isChange ? 'changed' : 'nochanged', this.GetSendBlockChangeData());
        }
    }
}

//#endregion

//#region ItemData.prototype conflicts

//функция после сабмита другим пользователем, проверяет равны ли данные в блоке
//если нет, то добавить диалог конфликта
//возвращает false когда нет конфликта, true - есть конфликт
ItemData.prototype.InitConflictAfterSubmit = function (submitData) {
    //удалить старую инфу
    this.isConflict = false;
    this.RemoveConflictMessage();

    if (this.TaskRightsDisplayType() == ItemDataHelper.TaskRightsDisplayType.None) {
        //если скрытые поля есть на форме
        //то просто там обновим значение
        if (this.IsOnForm()) {
            this.AppendNotConflictUpdate(submitData);
        }
        return false;
    }

    //мы не изменяли блок
    //1. показать сообщение что нет конфликта
    //2. обновить данные в блоке
    if (!this.IsChanged()) {
        this.PrepareConflictBlockAfterSubmit(submitData);
        this.AppendNotConflict(submitData);
        return false;
    }

    //если изменили, но наше значение совпало с сабмитом
    //то нет конфликта
    if (this._options.initConflictAfterSubmit.compareEqualsCurrentWithSubmitValue
        && this.GetCurrentVal() == this.GetSubmitVal(submitData)) {
        return false;
    }

    if (this._options.initConflictAfterSubmit.canConflict && this.TaskRightsDisplayType() == ItemDataHelper.TaskRightsDisplayType.Edit) {
        this.PrepareConflictBlockAfterSubmit(submitData);
        this.AppendConflict1(submitData);
        return true;
    }
    else {
        return false;
    }

}

ItemData.prototype.RemoveConflictMessage = function () {
    this.isConflict = false;
    this.$conflictMessageElement = undefined;
    $(this._options.selector).siblings('[conflict-block="' + this._options.name + '"]').remove();
}

ItemData.prototype.PrepareConflictBlockAfterSubmit = function (submitData) {

}

//для кастома
ItemData.prototype.GetSubmitVal = function (submitData) {
    return submitData;
}

ItemData.prototype.tmpl_notconflictMessageBase =
        tmpl('\
            <div class="" is-conflict="false" conflict-block="{%=o.blockName%}">'
                + TaskFormHubResources.NotconflictMessageBase +
            '</div>\
        ');
//создать сообщение что нет конфликта и обновить блок данными
ItemData.prototype.AppendNotConflict = function (submitData) {
    var $notconflictMessage = $(this.tmpl_notconflictMessageBase({ blockName: this._options.name }));

    //добавить новую инфу
    $(this._options.selector).after($notconflictMessage);

    this.AppendNotConflictUpdate(submitData);
}
//обновить блок
ItemData.prototype.AppendNotConflictUpdate = function (submitData) {
    $(this._options.selector).val(submitData);
    this.SetOldValue();
    this.SendBlockChange();

    if (this.GetSelectorType() == 'textarea' && $(this._options.selector).hasClass('autogrow')) {
        $(this._options.selector).css('height', '1px').elastic();
    }
}

ItemData.prototype.tmpl_conflictMessageBase =
        tmpl('\
            <div class="" is-conflict="true" conflict-block="{%=o.blockName%}">'
                + TaskFormHubResources.ConflictMessageBase_1 +
                '<a href="#" onclick="return false;"> ' + TaskFormHubResources.ConflictMessageBase_2 + '</a>\
            </div>\
        ');
//<input type="text" class="input-conflict-block" style="width:0px;height:0px;"/>\
//создание сообщения о конфликте и куда его добавить
ItemData.prototype.AppendConflict1 = function (submitData) {
    var that = this;
    var $conflictMessage = $(this.tmpl_conflictMessageBase({ blockName: this._options.name }));

    var selectorType = that.GetSelectorType();

    $conflictMessage.find('a').click(function (e) {
        e.preventDefault();

        var $content = $('<div style="background-color:#f7f9fa"></div>');
        $content.append(that.ConflictDialogContent(selectorType, submitData));

        //that.ConflictDialogContentEvents(selectorType, $content);

        GLOBAL_taskFormHubDialog.openDialog($content, {
            title: that.GetBlockTitle(),
            buttons: {
                Cancel: function () {
                    GLOBAL_taskFormHubDialog.closeDialog();
                },
                Ok: function () {
                    that.ConflictDialogOkCallback(submitData);
                }
            }
        });
    });

    this.isConflict = true;
    this.$conflictMessageElement = $conflictMessage;
    this.AppendConflict2($conflictMessage);
}

//куда добавляем сообщение
ItemData.prototype.AppendConflict2 = function ($conflictMessage) {
    //добавить новую инфу
    $(this._options.selector).after($conflictMessage);
}

ItemData.prototype.ConflictDialogContent = function (selectorType, submitData) {
    var that = this;

    //одиночный инпут или textarea
    if (selectorType == 'string' || selectorType == 'textarea') {
        return $(
            '<div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-' + that._options.name + '" value="1" checked />' + TaskFormHubResources.ConflictDialogContent_UseOldValue + '\
                </label>\
                <textarea class="width98 autogrow" name="conflictradioresolve-' + that._options.name + '-value1" style="min-width:600px">' + that.GetCurrentVal() + '</textarea>\
            </div><hr>\
            <div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-' + that._options.name + '" value="0" />' + TaskFormHubResources.ConflictDialogContent_UseNewValue + '\
                </label>\
                <textarea class="width98 autogrow" name="conflictradioresolve-' + that._options.name + '-value0" style="min-width:600px" disabled>' + submitData + '</textarea>\
            </div>\
            ');
    }
    else if (selectorType == 'numeric') {
        return $(
            '<div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-'+ that._options.name + '" value="1" checked />' + TaskFormHubResources.ConflictDialogContent_UseOldValue + '\
                </label>\
                <input type="text" class="numeric numeric100 intRange" name="conflictradioresolve-' + that._options.name + '-value1" value="' + that.GetCurrentVal() + '" style="width:90%" />\
            </div><hr>\
            <div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-' + that._options.name + '" value="0" />' + TaskFormHubResources.ConflictDialogContent_UseNewValue + '\
                </label>\
                <input type="text" class="numeric numeric100 intRange" name="conflictradioresolve-' + that._options.name + '-value0" value="' + submitData + '" style="width:90%" disabled />\
            </div>\
            ');
    }
    else if (selectorType == 'datetime') {
        return $(
            '<div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-' + that._options.name + '" value="1" checked />' + TaskFormHubResources.ConflictDialogContent_UseOldValue + '\
                </label>\
                <input class="datePicker dateTime" name="conflictradioresolve-' + that._options.name + '-value1" value="' + that.GetCurrentVal() + '" />\
            </div><hr>\
            <div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-' + that._options.name + '" value="0" />' + TaskFormHubResources.ConflictDialogContent_UseNewValue + '\
                </label>\
                <input class="datePicker dateTime" name="conflictradioresolve-' + that._options.name + '-value0" value="' + submitData + '" disabled />\
            </div>\
            ');
    }
    else if (selectorType == 'select') {
        return $(
            '<div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-' + that._options.name + '" value="1" checked />' + TaskFormHubResources.ConflictDialogContent_UseOldValue + '\
                </label>\
                <select class="px300" name="conflictradioresolve-' + that._options.name + '-value1">\
                    <option value="' + that.GetCurrentVal() + '">' + $(that._options.selector).find('option:selected').text() + '</option>\
                </select>\
            </div><hr>\
            <div>\
                <label>\
                    <input type="radio" name="conflictradioresolve-' + that._options.name + '" value="0" />' + TaskFormHubResources.ConflictDialogContent_UseNewValue + '\
                </label>\
                <select class="px300" name="conflictradioresolve-' + that._options.name + '-value0">\
                    <option value="' + submitData + '">' + $(that._options.selector).find('option[value="' + submitData + '"]').text() + '</option>\
                </select>\
            </div>\
            ');
    }
}

//из-за этого при решении конфликта если выбрать "новое значение" и изменить, то будет баг что блок не определится как измененный, т.к. ниже вызывается SetOldValue()
ItemData.prototype.ConflictDialogContentEvents = function (selectorType, $content) {
    //var that = this;
    //if (selectorType == 'string' || selectorType == 'textarea' || selectorType == 'datetime' || selectorType == 'numeric') {
    //    $content.find('[name="conflictradioresolve-' + that._options.name + '"][value="1"]').click(function () {
    //        $content.find('[name="conflictradioresolve-' + that._options.name + '-value1"]').prop('disabled', false);
    //        $content.find('[name="conflictradioresolve-' + that._options.name + '-value0"]').prop('disabled', true);
    //    });

    //    $content.find('[name="conflictradioresolve-' + that._options.name + '"][value="0"]').click(function () {
    //        $content.find('[name="conflictradioresolve-' + that._options.name + '-value0"]').prop('disabled', false);
    //        $content.find('[name="conflictradioresolve-' + that._options.name + '-value1"]').prop('disabled', true);
    //    });
    //}
}

ItemData.prototype.ConflictDialogOkCallback = function () {
    var selectorType = this.GetSelectorType();

    if (selectorType == 'string' || selectorType == 'textarea' || selectorType == 'numeric' || selectorType == 'datetime' || selectorType == 'select') {
        var $content = GLOBAL_taskFormHubDialog.getContent();
        var checkedVal = $content.find('[name="conflictradioresolve-' + this._options.name + '"]:checked').val();
        var resolveValue = $content.find('[name="conflictradioresolve-' + this._options.name + '-value' + checkedVal + '"]').val();
        $(this._options.selector).val(resolveValue).trigger('change');
        if (checkedVal == 0) {
            this.SetOldValue();
            this.SendBlockChange();

            if (selectorType == 'textarea' && $(this._options.selector).hasClass('autogrow')) {
                $(this._options.selector).css('height', '1px').elastic();
            }
        }
    }

    //удаляем инфу о конфликте
    this.isConflict = false;
    this.$conflictMessageElement = undefined;
    this.RemoveConflictMessage();

    GLOBAL_taskFormHubDialog.closeDialog();
}

ItemData.prototype.ShowConflictMessage = function (show) {
    if (show) {
        $(this._options.selector).siblings('[conflict-block="' + this._options.name + '"]').show();
    }
    else {
        $(this._options.selector).siblings('[conflict-block="' + this._options.name + '"]').hide();
    }
}

ItemData.prototype.IsConflictCond = function () {
    return this.isConflict;
}

//конфликт решен?
ItemData.prototype.ConflictIsResolved = function (scroll) {
    //var $elem = $(this._options.selector).siblings('[conflict-block="' + this._options.name + '"]');
    //var result = $elem.length == 0;
    //if (!result && scroll) {
    //    this.ConflictNotResolvedScroll($elem);
    //}
    //return result;
    var isConflictCond = this.IsConflictCond();
    if (scroll && isConflictCond) {
        this.ConflictNotResolvedScroll();
    }
    return !isConflictCond;
}
//если есть конфликт - скролим до него
ItemData.prototype.ConflictNotResolvedScroll = function () {
    var that = this;
    $('html, body').animate({
        scrollTop: that.$conflictMessageElement.offset().top - 150
    }, 300);
}

//#endregion

//#endregion


//#endregion


//#region доп. поле - базовый класс
function FieldItemData() {
    return ItemData.apply(this, arguments);
}
FieldItemData.prototype = Object.create(ItemData.prototype);
FieldItemData.prototype.PrepareConflictBlockAfterSubmit = function (submitData) {
    checkExpandGroupFields($(this._options.selector), true);
}
FieldItemData.prototype.ConflictNotResolvedScroll = function () {
    checkExpandGroupFields(this.$conflictMessageElement, true);
    ItemData.prototype.ConflictNotResolvedScroll.call(this);
}

//#endregion

//#region системные поля - базовый класс
function SystemFieldItemData() {
    return ItemData.apply(this, arguments);
}
SystemFieldItemData.prototype = Object.create(ItemData.prototype);
SystemFieldItemData.prototype.PrepareConflictBlockAfterSubmit = function (submitData) {
    this._showSystemFields();
}
SystemFieldItemData.prototype.ConflictNotResolvedScroll = function () {
    this._showSystemFields();
    ItemData.prototype.ConflictNotResolvedScroll.call(this);
}
SystemFieldItemData.prototype._showSystemFields = function () {
    var $systemfields = $('#systemfields');
    if (!$systemfields.is(':visible')) {
        $('[linkshowhidesystemfields]').html('&uarr; ' + CommonResources.Hide);
    }
    $systemfields.removeClass('hidden');
}
//#endregion


//#region ItemDataHelper
ItemDataHelper = {
    TaskRightsDisplayType: {
        'None': 0,
        'Edit': 1,
        'View': 2
    },
    ArraysIsEquals: function (a, b) {
        var a1 = a == undefined || a == '' ? [] : a;
        var b1 = b == undefined || b == '' ? [] : b;
        if (a1.length != b1.length)
            return false;

        return a1.sort().toString() == b1.sort().toString();
    },
    ArraysMergeDistinct: function (a, b) {
        var c = a.concat(b.filter(function (item) {
            return a.indexOf(item) < 0;
        }));
        return c;
    }
};
//#endregion