function initialiteTypeahead(url, $element, $idElement, id, displayText, onMapBeforeDisplay, onSelect, onAfterSelect, requestDataFunction, $warehouse, $subject, $wayOfSale, priceType, minLength, $dateFrom, $dateTo, $docType, $isAdvPayFisc) {
    if (!onSelect) {
        onSelect = function (item) {
            if (item && item[id].length > 0)
                $element.removeClass("error-input-item");

            $idElement.val(item[id]);
            item.selected = true;
            item.value = item[id];

            return item;
        };
    }

    let timeout;
    $element.typeahead({
        source: function (request, response) {
            if (keyupTimeout)
                clearTimeout(keyupTimeout);

            if (timeout)
                clearTimeout(timeout);

            timeout = setTimeout(function () {
                var data = requestDataFunction ? requestDataFunction() : undefined;
                data = data ? data : {};
                data.search = request;
                data.saleType = priceType;

                if ($warehouse && $warehouse.val())
                    data.warehouse = $warehouse.val();
                if ($subject && $subject.val())
                    data.subject = $subject.val();
                if ($wayOfSale && $wayOfSale.val())
                    data.wayOfSale = $wayOfSale.val();
                if ($dateFrom && $dateFrom.val())
                    data.dateFrom = $dateFrom.val();
                if ($dateTo && $dateTo.val())
                    data.dateTo = $dateTo.val();
                if ($docType && $docType.val())
                    data.docType = $docType.val();
                if ($isAdvPayFisc && $isAdvPayFisc.val())
                    data.isAdvPayFisc = $isAdvPayFisc.val() === "true";
                if ($element.val() === '')
                    data.search = "";

                $.post(url, data)
                    .done(function (result) {
                        var items = [];
                        $.each(result, function (i, item) {
                            if (onMapBeforeDisplay) onMapBeforeDisplay(items, i, item);
                            else items.push(item);
                        });
                        response(items);
                        //Sticky table header
                        if (!$element.hasClass("typeahead-normal"))
                            $(".sticky-header").floatThead();
                    });
            }, 300);
        },
        minLength: minLength ? minLength : 0,
        items: 100,
        updater: onSelect,
        highlighter: Object,
        displayText: displayText,
        afterSelect: onAfterSelect,
        sorter: function (items) {
            return items;
        },
        matcher: function (item) {
            return true;
        },
        appendTo: paw.utils.value.isSet($element.attr('data-appendto'))? $element.attr('data-appendto') : undefined
    });

    $element.on('keyup', function (e) {
        if (e.keyCode === 40 || e.keyCode === 38) {
            applyTypeaheadScroll($(this));
        }

        return true;
    });

    $element.on('blur', function () {
        if ($(this).val() === '') {
            if ($idElement) {
                $idElement.val('');
            }
            $element.val('');
            $element.trigger('change');
        }
    });

    $element.on("click", function () {
        $(this).typeahead('lookup');
    });

    let keyupTimeout;
    $element.on("keyup", function (e) {
        if (keyupTimeout)
            clearTimeout(keyupTimeout);

        if ($(this).val().length === 1 && (e.which !== 13 && e.which !== 38 && e.which !== 40)) {
            let $typeaheadElement = $(this);
            keyupTimeout = setTimeout(function () {
                $typeaheadElement.typeahead('lookup');
            }, 380);
        }
    });

    var maxItems = 100;
    var itemDisplayQuantity = 100;
    var displayedItems = itemDisplayQuantity;
    var itemsScrollPage = 1;
    document.addEventListener('scroll', function (event) {
        if (!$(event.target).hasClass('typeahead')) return;
        if (displayedItems >= maxItems) return;

        if (event.target.classList && event.target.classList.contains('typeahead')) {
            if ($(event.target).scrollTop() + $(event.target).innerHeight() >= $(event.target)[0].scrollHeight) {
                itemsScrollPage++;
                displayedItems = itemDisplayQuantity * itemsScrollPage;

                $element.next('.typeahead').find('li:nth-child(-n+' + (displayedItems + 1) + ')').css('display', 'block');
                $element.next('.typeahead').find('li:nth-child(n+' + (displayedItems + 1) + ')').css('display', 'none');
            }
        }
    }, true);
}

function generateRow(titles, values, widths, display) {
    return function (item) {
        if (item.selected) return item[display ? display : 'value'];

        var row = '<table class="table sticky-header">';
        if (item.row === 0) {
            row += '<thead><tr class="row">';
            for (let i = 0; i < values.length; i++) {
                row += '<th style="position: sticky; top: 0" ' + (widths ? 'width="' + widths[i] + '"' : '') + '>' + item.localization[titles[i]] + '</th>';
            }
            row += '</tr></thead>';
            row += '<tbody>';
        }

        row += '<tbody>';
        row += '<tr class="row">';
        for (let i = 0; i < values.length; i++) {
            row += '<td ' + (widths ? 'width="' + widths[i] + '"' : '') + '>' + item[values[i]] + '</td>';
        }
        row += '</tr>';
        row += '</tbody></table>';

        return row;
    };
}

function applyTypeaheadScroll($input) {
    var $parent = $input.next('.typeahead');
    var $element = $($parent.find('li.active')[0]);

    if ($element) {
        if ($element.position().top >= $parent.height()) {
            $parent.scrollTop($parent.scrollTop() + $element.position().top - $parent.height() + $element.height());
        }
        else if ($element.position().top < 0) {
            $parent.scrollTop($parent.scrollTop() + $element.position().top);
        }
    }
}

//Partner typeahead
function initializePartnerTypeahead(url, $element, $idElement, afterSelectFunction, requestDataFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["company", "title", "address", "post", "vatNumber"],
            ["id", "name_only", "address", "zip_code", "vat"],
            ["30%", "20%", "20%", "10%", "20%"]),
        function (items, i, item) {
            items.push({
                id: item.Id,
                name: i + '|' + (item.Code || '') + '|' + (item.Name || '') + '|' + (item.VAT || ''),
                name_only: item.Name,
                vat: item.VAT,
                isVat: item.isVAT,
                address: item.Address,
                complete_address: item.Address + (!item.ZipCode ? "" : ", " + item.ZipCode) + (!item.VAT ? "" : ", " + item.VAT),
                zip_code: item.ZipCode,
                town: item.Town,
                country: item.Country,
                code: item.Code,
                wayOfSale: item.WayOfSale,
                wayOfSaleSupplier: item.WayOfSaleSupplier,
                rebate: item.Rabate,
                rabateSupplier: item.RabateSupplier,
                regNo: item.RegNo,
                paymentMethodId: item.PaymentMethodId,
                paymentMethod: item.PaymentMethod,
                paymentMethodIdSupplier: item.PaymentMethodIdSupplier,
                daysForPayment: item.DaysForPayment,
                daysForPaymentSupplier: item.DaysForPaymentSupplier,
                statement: item.Statement ? item.Statement.trim() : "",
                statementSupplier: item.StatementSupplier ? item.StatementSupplier.trim() : "",
                bankAccountRefCode: item.BankAccountRefCode,
                delivery: item.Delivery,
                suppDelivery: item.SuppDelivery,
                parityPost: item.ParityPost,
                suppParityPost: item.SuppParityPost,
                currency: item.Currency,
                suppCurrency: item.SuppCurrency,
                priceRate: item.PriceRate,
                clerk: item.Clerk,
                clerkSupp: item.ClerkSupp,
                worker: item.ClerkWorker,
                payer: item.Payer,
                row: i,
                localization: item.Localization
            });
        },
        function (item) {
            $idElement.val(item.id);

            if (item && item.id.length > 0) {
                $element.removeClass("error-input-item");
                $element.closest(".form-group").find("span[class^='field-validation']").text("");
            }

            return {
                selected: true,
                id: item.id,
                value: $("#OurCompIsName2").val() && $("#OurCompIsName2").val() === "True" ? (item.name_only ? item.name_only : item.id) : item.id,
                wayOfSale: item.wayOfSale,
                wayOfSaleSupplier: item.wayOfSaleSupplier,
                completeAddress: item.complete_address,
                rebate: item.rebate,
                rabateSupplier: item.rabateSupplier,
                name: item.name_only,
                vat: item.vat,
                isVAT: item.IsVAT,
                regNo: item.regNo,
                address: item.address,
                paymentMethod: item.paymentMethod,
                paymentMethodId: item.paymentMethodId,
                paymentMethodIdSupplier: item.paymentMethodIdSupplier,
                statement: item.statement,
                statementSupplier: item.statementSupplier,
                bankAccountRefCode: item.bankAccountRefCode,
                delivery: item.delivery,
                suppDelivery: item.suppDelivery,
                parityPost: item.parityPost,
                suppParityPost: item.suppParityPost,
                daysForPayment: item.daysForPayment,
                daysForPaymentSupplier: item.daysForPaymentSupplier,
                currency: item.currency,
                suppCurrency: item.suppCurrency,
                priceRate: item.priceRate,
                clerk: item.clerk,
                clerkSupp: item.clerkSupp,
                worker: item.worker,
                payer: item.payer
            };
        },
        afterSelectFunction,
        requestDataFunction
    );
}

//Basic Partner typeahead
function initializeBasicPartnerTypeahead(url, $element, $idElement, afterSelectFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["company", "title"],
            ["id", "name_only"],
            ["40%", "60%"]),
        function (items, i, item) {
            items.push({
                id: item.Id,
                name: i + '|' + (item.Code || '') + '|' + (item.Name || '') + '|' + (item.VAT || ''),
                name_only: item.Name,
                row: i,
                localization: item.Localization
            });
        },
        function (item) {
            $idElement.val(item.id);

            if (item && item.id.length > 0) {
                $element.removeClass("error-input-item");
                $element.closest(".form-group").find("span[class^='field-validation']").text("");
            }

            return {
                selected: true,
                value: item.id
            };
        },
        afterSelectFunction
    );
}

//User typeahead
function initializeUserTypeahead(url, $element, $idElement, onAfterSelect) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["id", "name", "code"],
            ["id", "name_only", "code"],
            ["20%", "50%", "30%"]),
        function (items, i, item) {
            items.push({
                id: item.Id,
                name: item.Name,
                name_only: item.Name,
                code: item.Code,
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            return { selected: true, value: item.name_only };
        }, onAfterSelect
    );
}

//Ident typeahead
function initializeIdentTypeahead(url, $element, $idElement, onAddFunction, priceType, defaultDecimalPlacesPrice, $warehouse, $subject, $wayOfSale, localization) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        function (item) {
            if (item.selected) return item.value;
            var row = "";
            var pos = item.row;

            if (pos === 0) {
                row += '<table class="table">';
                row += '<thead><tr>';
                row += '<th width="35%">' + localization.ident + '</th>';
                row += '<th width="35%">' + localization.name + '</th>';
                row += '<th width="15%">' + localization.price + '</th>';
                row += '<th width="15%">' + localization.stock + '</th>';
                row += '</tr></thead>';
                row += '<tbody>';
            }
            else {
                row += '<table class="table">';
                row += '<tbody>';
            }
            row += '<tr>';
            row += '<td width="35%">' + item.id + '</td>';
            row += '<td width="35%">' + item.name + '</td>';
            row += '<td width="15%">' + formatDisplay(defaultDecimalPlacesPrice, getPriceByType(item, priceType)) + '</td>';
            row += '<td width="15%">' + formatDisplay(defaultDecimalPlacesPrice, item.stock) + '</td>';
            row += '</tr>';
            row += '</tbody></table>';

            return row;
        },
        function (items, i, item) {
            items.push({
                id: item.Ident,
                name: item.Name,
                name_only: item.Name,
                price: item.SuppliersPrice,
                sellingPrice: item.SellingPrice,
                retailPrice: item.RetailPrice,
                quantity: 1,
                discount: item.Discount,
                rabate: item.Rebate,
                vat: item.VAT,
                vatCode: item.VATCode,
                measurementUnit: item.Unit,
                taxPrice: 0,
                basisForTax: item.SuppliersPrice - (item.SuppliersPrice / 100 * 10),
                priceForValue: 0,
                stock: item.Stock,
                wsPrice: item.WSPrice,
                wsPrice2: item.WSPrice2,
                prStPrice: item.PrStPrice,
                contractPrice: item.ContractPrice,
                contractPriceRabate: item.ContractPriceRabate,
                department: item.Department,
                costDrv: item.CostDrv,
                localization: localization,
                isActive: item.Active === "T",
                row: i,
                priceRate: item.PriceRate
            });
        },
        onAddFunction,
        undefined,
        null,
        $warehouse,
        $subject,
        $wayOfSale,
        (priceType === PriceType.Wholesale || priceType === PriceType.OrderBuyer || priceType === PriceType.Pos) ? "P" : "N", 0
    );
}

//Ident basic typeahead
function initializeIdentBasicTypeahead(url, $element, $idElement, localization, $isAdvPayFisc, onAddFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        function (item) {
            if (item.selected) return item.value;
            var row = "";
            var pos = item.row;

            if (pos === 0) {
                row += '<table class="table">';
                row += '<thead><tr>';
                row += '<th width="35%">' + localization.item + '</th>';
                row += '<th width="65%">' + localization.name + '</th>';
                row += '</tr></thead>';
                row += '<tbody>';
            }
            else {
                row += '<table class="table">';
                row += '<tbody>';
            }
            row += '<tr>';
            row += '<td width="35%">' + item.id + '</td>';
            row += '<td width="65%">' + item.name + '</td>';
            row += '</tr>';
            row += '</tbody></table>';

            return row;
        },
        function (items, i, item) {
            items.push({
                id: item.Ident,
                name: item.Name,
                name_only: item.Name,
                localization: item.Localization,
                row: i
            });
        },
        onAddFunction,
        undefined,
        null,
        null,
        null,
        null,
        null,
        0,
        null,
        null,
        null,
        $isAdvPayFisc
    );
}

//Post typeahead
function initializePostTypeahead(url, $element, $idElement, afterSelectFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["code", "title", "region"],
            ["id", "name", "region"],
            ["30%", "40%", "30%"]),
        function (items, i, item) {
            items.push({
                id: item.Post,
                name: item.Name,
                name_only: item.Post + " - " + item.Name,
                region: item.Region,
                municipCode: item.MunicipId,
                municip: item.Municip,
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            $element.val(item.name_only);
            return { selected: true, value: item.id, name: item.name, municipCode: item.municipCode, municip: item.municip };
        },
        afterSelectFunction,
        function () {
            return { country: $("#SelectedCountryId").val() };
        }
    );
}

//Excise typeahead
function initializeExciseTypeahead(url, $element, $idElement, onAddFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["code", "title", "type"],
            ["id", "name", "type"],
            ["30%", "45%", "25%"]),
        function (items, i, item) {
            items.push({
                id: item.SetOfTax,
                name: item.Name,
                type: item.Type,
                anQid: item.AnQid,
                localization: item.Localization,
                row: i
            });
        },
        onAddFunction
    );
}

//Customs tariff typeahead
function initializeCustTariffTypeahead(url, $element, $idElement, onAddFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["code", "title", "rate"],
            ["id", "name", "rate"],
            ["30%", "45%", "25%"]),
        function (items, i, item) {
            items.push({
                id: item.CustTariff,
                name: item.Name,
                rate: item.Rate,
                anQid: item.AnQid,
                localization: item.Localization,
                row: i
            });
        }, function (item) {
            $idElement.val(item.id);
            return { selected: true, value: item.id, name: item.name };
        }
    );
}

//Municip typeahead
function initializeMunicipTypeahead(url, $element, $idElement) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["code", "title"],
            ["id", "name"],
            ["20%", "80%"]),
        function (items, i, item) {
            items.push({
                id: item.MunicipCode,
                name: item.Name,
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            $element.val(item.name);
            return { selected: true, value: item.name };
        }
    );
}

//Classification typeahead
function initializeClassificationTypeahead(url, $element, $idElement, onSelected) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["description", "code"],
            ["name", "acType"],
            ["80%", "30%"]),
        function (items, i, item) {
            items.push({
                id: item.AnQid,
                name: item.AcTypeDesc,
                acType: item.AcType,
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            $element.val(item.name);
            if (paw.utils.value.isFunction(onSelected)) {
                onSelected(item);
            }
            return { selected: true, value: item.name };
        }
    );
}

//Delivery place - post typeahead
function initializeDeliveryPlaceTypeahead(url, $element, $idElement) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["code", "title"],
            ["id", "name"],
            ["50%", "50%"]),
        function (items, i, item) {
            items.push({
                id: item.Post,
                name: item.Name,
                name_only: item.Post + " - " + item.Name,
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            $element.val(item.name_only);
            return { selected: true, value: item.id, name: item.name };
        }
    );
}

//Invoice typeahead
function initializeInvoiceTypeahead(url, $element, $idElement, invoiceType, $dateFrom, $dateTo, $docType, onAddFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        function (item) {
            if (item.selected) return item.value;
            var row = "";
            var pos = item.row;

            if (pos === 0) {
                row += '<table class="table">';
                row += '<thead><tr>';
                row += '<th width="15%">' + item.localization.ackey + '</th>';
                row += '<th width="12%">' + item.localization.date + '</th>';
                row += '<th width="30%">' + item.localization.firstPerson + '</th>';
                row += '<th width="28%">' + item.localization.thirdPerson + '</th>';
                row += '<th width="15%" class="text-right">' + item.localization.forPay + '</th>';
                row += '</tr></thead>';
                row += '<tbody>';
            }
            else {
                row += '<table class="table">';
                row += '<tbody>';
            }
            row += '<tr>';
            row += '<td width="15%">' + item.name + '</td>';
            row += '<td width="12%">' + item.date + '</td>';

            var subj = item.issuer;
            if (invoiceType === "Wholesale")
                subj = item.receiver;
            if (invoiceType === "OrderBuyer")
                subj = item.receiver;

            row += '<td width="30%">' + subj + '</td>';
            row += '<td width="28%">' + (invoiceType === "OrderBuyer" ? item.receiver : item.prsn3) + '</td>';
            row += '<td width="15%" class="text-right">' + formatDisplay(2, item.forPay) + '</td>';
            row += '</tr>';
            row += '</tbody></table>';

            return row;
        },
        function (items, i, item) {
            items.push({
                id: item.AcKey,
                name: item.AcKeyView,
                name_only: item.AcKeyView,
                date: item.Date,
                receiver: item.Receiver,
                issuer: item.Issuer,
                prsn3: item.Prsn3,
                forPay: item.ForPay,
                localization: item.Localization,
                row: i
            });
        },
        onAddFunction,
        undefined,
        null,
        null,
        null,
        null,
        null,
        null,
        $dateFrom,
        $dateTo,
        $docType
    );
}

//Bank typeahead
function initializeBankTypeahead(url, $element, $idElement) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["code", "title"],
            ["id", "name"],
            ["50%", "50%"]),
        function (items, i, item) {
            items.push({
                id: item.Id,
                name: item.Name,
                name_only: item.Id + " - " + item.Name,
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            $element.val(item.name_only);
            return { selected: true, value: item.id, name: item.name };
        }
    );
}

//Bank accounts typeahead
function initializeBankAccountsTypeahead(url, $element, $idElement) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["anNo", "bnkAcc", "type", "bank", "isDefault", "note"],
            ["anNo", "id", "type", "bank", "isDefault", "note"],
            ["10%", "30%", "10%", "20%", "10%", "20%"]),
        function (items, i, item) {
            items.push({
                id: item.BnkAcctNo1,
                anNo: item.AnNo,
                type: item.BnkAcctNo23,
                bank: item.Name,
                note: item.Note,
                isDefault: item.Default === true ? "T" : "F",
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            $element.val(item.id);
            return { selected: true, value: item.id, name: item.name };
        }
    );
}

//CostDrv typeahead
function initializeCostDrvTypeahead(url, $element, $idElement, $departmentElement) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        function (item) {
            if (item.selected) return item.value;
            var row = "";
            var pos = item.row;

            if (pos === 0) {
                row += '<table class="table typeahead-table">';
                row += '<thead><tr class="row">';
                row += '<th width="35%">' + item.localization.costDrv + '</th>';
                row += '<th width="35%">' + item.localization.title + '</th>';
                row += '<th width="15%">' + item.localization.group + '</th>';
                row += '<th width="15%">' + item.localization.status + '</th>';
                row += '</tr></thead>';
                row += '<tbody>';
            }
            else {
                row += '<table class="table typeahead-table">';
                row += '<tbody>';
            }
            row += '<tr class="row">';
            row += '<td width="35%">' + item.id + '</td>';
            row += '<td width="35%">' + item.name + '</td>';
            row += '<td width="15%">' + item.classif + '</td>';
            row += '<td width="15%">' + item.status + '</td>';
            row += '</tr>';
            row += '</tbody></table>';

            return row;
        },
        function (items, i, item) {
            items.push({
                id: item.Id,
                name: item.Name,
                classif: item.Classif,
                status: item.Status,
                department: item.Department,
                row: i,
                localization: item.Localization
            });
        }, function (item) {
            $idElement.val(item.id);
            $element.val(item.id);
            if ($("#FillDeptFromCostDrv").val()) {
                $departmentElement.val(item.department);
            }
            return { selected: true, value: item.id, name: item.name };
        }
    );
}

//Document links typeahead
function initializeDocumentLinksTypeahead(url, $element, $idElement, afterSelectFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["ackeyView", "subject", "subjectName"],
            ["idView", "subject", "subjectName"],
            ["20%", "35%", "45%"]),
        function (items, i, item) {
            items.push({
                id: item.AcKey,
                subject: item.Subject,
                subjectName: item.SubjectName,
                idView: item.AcKeyView,
                row: i,
                localization: item.Localization
            });
        },
        function (item) {
            $idElement.val(item.id);

            return {
                selected: true,
                value: item.id
            };
        },
        afterSelectFunction
    );
}

//Certificates typeahead
function initializeCertificatesTypeahead(url, $element, $idElement, afterSelectFunction) {
    initialiteTypeahead(url, $element, $idElement,
        'id',
        generateRow(
            ["serialNumber", "validTo", "name", "pac"],
            ["serialNumber", "dateValidTo", "issuerName", "pac"],
            ["30%", "20%", "30%", "10%"]),
        function (items, i, item) {
            items.push({
                id: item.AnQid,
                serialNumber: item.SerialNumber,
                dateValidTo: item.DateValidTo,
                issuerName: item.IssuerName,
                pac: item.PAC,
                row: i,
                localization: item.Localization
            });
        },
        function (item) {
            $idElement.val(item.id);

            return {
                selected: true,
                value: item.serialNumber
            };
        },
        afterSelectFunction
    );
}
