@typeparam TItem
@using Microsoft.JSInterop
@using Radzen.Blazor.Rendering
@using System.Globalization
@using System.Collections
@using Radzen

@if (RowIndex == Column.GetLevel())
{
    <th role="columnheader" rowspan="@(Column.GetRowSpan())" colspan="@(Column.GetColSpan())" @attributes="@Attributes" class="@CssClass"
        scope="col" style="@GetStyle()" data-column-index="@ColumnIndex" @onmouseup=@(args => Grid.EndColumnReorder(args, ColumnIndex))
        aria-sort="@(Grid.AllowSorting && Column.Sortable ? (Column.GetSortOrder() == SortOrder.Ascending ? "ascending" : Column.GetSortOrder() == SortOrder.Descending ? "descending" : "none") : null)">
        <div @onclick='@((args) => Grid.OnSort(args, Column))'>
            @if ((Grid.AllowColumnReorder && Column.Reorderable || Grid.AllowGrouping && Column.Groupable))
            {
                <span id="@(Grid.getColumnUniqueId(ColumnIndex) + "-drag")" class="rz-column-drag"
                @onclick:preventDefault="true" @onclick:stopPropagation="true"
                @onmousedown:preventDefault="true"
                @onmousedown=@(args => Grid.StartColumnReorder(args, ColumnIndex, !string.IsNullOrEmpty(Column.UniqueID) ? Column.UniqueID : Column.Property))
                @ontouchstart:preventDefault="true"
                @ontouchstart=@(args => Grid.StartColumnReorder(args, ColumnIndex, !string.IsNullOrEmpty(Column.UniqueID) ? Column.UniqueID : Column.Property))></span>
            }
            <span class="rz-column-title" title="@GetColumnTitleAttribute()">
                @if (Column.HeaderTemplate != null)
                {
                    <span class="@Column.GetHeaderClass()" @onkeydown:stopPropagation="stopKeydownPropagation" @onkeydown="OnGuardKeyDown">@Column.HeaderTemplate</span>
                }
                else
                {
                    <span class="@Column.GetHeaderClass()">@Column.GetTitle()</span>
                }

                @if (Grid.AllowSorting && Column.Sortable)
                {
                    @if (Column.GetSortOrder() == SortOrder.Ascending)
                    {
                        <span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-asc"></span>
                        @if(Grid.ShowMultiColumnSortingIndex)
                        {
                            <RadzenBadge BadgeStyle="BadgeStyle.Info" Shade="Shade.Lighter" IsPill=true Text="@Column.getSortIndexAsString()" />
                        }
                    }
                    else if (Column.GetSortOrder() == SortOrder.Descending)
                    {
                        <span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-desc"></span>
                        @if(Grid.ShowMultiColumnSortingIndex)
                        {
                            <RadzenBadge BadgeStyle="BadgeStyle.Info" Shade="Shade.Lighter" IsPill=true Text="@Column.getSortIndexAsString()" />
                        }
                    }
                    else
                    {
                        <span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort"></span>
                    }
                }
            </span>
            @if (Grid.AllowColumnResize && Column.Resizable && Column.Parent == null)
            {
                <div id="@(Grid.getColumnUniqueId(ColumnIndex) + "-resizer")" style="cursor:col-resize;float:right;"
                @onclick:preventDefault="true" @onclick:stopPropagation="true" class="rz-column-resizer"
                @onmousedown:preventDefault="true"
                @onmousedown=@StartColumnResize @onmouseup=@StopColumnResize
                @ontouchstart=@StartColumnResizeTouch @ontouchend=@StopColumnResize>&nbsp;</div>
            }
            @{var filterMode = Column.FilterMode ?? Grid.FilterMode;}
            @if (Grid.AllowFiltering && Column.Filterable && (filterMode == FilterMode.Advanced || filterMode == FilterMode.CheckBoxList))
            {
                <button type="button" tabindex="-1" @ref=@filterButton @onclick:stopPropagation="true" @onmousedown=@ToggleFilter
                        class="@getFilterIconCss(Column)" @onclick:preventDefault="true"
                        aria-haspopup="dialog" aria-controls="@Column.GetColumnPopupID()" aria-expanded="false"
                        aria-label="@Grid.FilterToggleAriaLabel">
                    @Grid.FilterIcon
                </button>

                <RadzenPopup Lazy=@(Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand) @ref=popup id="@($"{Column.GetColumnPopupID()}")" class="rz-overlaypanel"
                role="dialog" aria-label="@Grid.FilterText"
                style="display:none;min-width:250px;" @onkeydown="OnFilterPopupKeyPressed">
                    <div class="rz-overlaypanel-content">
                        @if (Column.FilterTemplate != null)
                        {
                            @Column.FilterTemplate(Column)
                        }
                        else
                        {
                            <form id="@($"{Column.GetColumnPopupID()}-form")" @onsubmit="@(args => ApplyFilter())" class="rz-grid-filter">
                                @if (filterMode == FilterMode.Advanced)
                                {
                                    <span class="rz-grid-filter-label">@Grid.FilterText</span>
                                    <RadzenDropDown InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Grid.FilterOperatorAriaLabel + Column.GetFilterOperatorText(Column.GetSecondFilterOperator()) }})"
                                    @onclick:preventDefault="true" Data="@(Column.GetFilterOperators().Select(t => new DropDownItem<FilterOperator> { Text = Column.GetFilterOperatorText(t), Value = t }))" TextProperty="Text" ValueProperty="Value" TValue="FilterOperator" Value="@Column.GetFilterOperator()" Change="@(args => Column.SetFilterOperator((FilterOperator)args))" />
                                    @if (Column.FilterValueTemplate != null)
                                    {
                                        @Column.FilterValueTemplate(Column)
                                    } 
                                    else if (Column.FilterPropertyType != null && (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType)))
                                    {
                                        <RadzenDropDown Disabled="@(!Column.CanSetFilterValue())" AllowClear="false" AllowFiltering="false" TValue="@object"
                                        Value=@Column.GetFilterValue() Multiple="false" Placeholder="@Grid.EnumFilterSelectText" TextProperty="Text" ValueProperty="Value" Data=@EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType, Grid.EnumFilterTranslationFunc)
                                        Change="@(args => Column.SetFilterValue(args))"
                                        InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel  + Column.GetFilterValue() }})" />
                                    }
                                    else if (Column.FilterPropertyType != null && PropertyAccess.IsNumeric(Column.FilterPropertyType))
                                    {
                                        @(Grid.DrawNumericFilter(Column, false))
                                    }
                                    else if (PropertyAccess.IsDate(Column.FilterPropertyType))
                                    {
                                        <RadzenDatePicker Disabled="@(!Column.CanSetFilterValue())" TValue="@object" ShowTime="@Column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="true" DateFormat="@Grid.getFilterDateFormat(Column)"
                                        Value="@Column.GetFilterValue()" Change="@(args => Column.SetFilterValue(args))" AllowInput=@(Grid.AllowFilterDateInput)
                                        InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel  + Column.GetFilterValue() }})" />

                                    }
                                    else if (Column.FilterPropertyType == typeof(bool) || Column.FilterPropertyType == typeof(bool?))
                                    {
                                        <RadzenCheckBox Disabled="@(!Column.CanSetFilterValue())" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel  + Column.GetFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetFilterValue()" Change="@(args => { Column.SetFilterValue(null); Column.SetFilterValue(args); Grid.SaveSettings(); })" />
                                    }
                                    else
                                    {
                                        <RadzenTextBox Disabled="@(!Column.CanSetFilterValue())" id="@($"{Column.GetColumnPopupID()}-sf")" aria-label=@(Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue()) Value="@($"{Column.GetFilterValue()}")" Change="@(args => Column.SetFilterValue(args))" />
                                    }

                                    <RadzenDropDown @onclick:preventDefault="true" TextProperty="Text" ValueProperty="Value" Style="width: 90px" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.LogicalOperatorAriaLabel + (Column.LogicalFilterOperator == LogicalFilterOperator.And ? Grid.AndOperatorText : Grid.OrOperatorText) }})"
                                    Data="@(Enum.GetValues(typeof(LogicalFilterOperator)).Cast<LogicalFilterOperator>().Select(t => new DropDownItem<LogicalFilterOperator> { Text = t == LogicalFilterOperator.And ? Grid.AndOperatorText : Grid.OrOperatorText, Value = t }))" TValue="LogicalFilterOperator" Value="@Column.LogicalFilterOperator" Change="@(args => Column.SetLogicalFilterOperator((LogicalFilterOperator)args))" />

                                    <RadzenDropDown InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Grid.SecondFilterOperatorAriaLabel + Column.GetFilterOperatorText(Column.GetSecondFilterOperator()) }})" @onclick:preventDefault="true" Data="@(Column.GetFilterOperators().Select(t => new DropDownItem<FilterOperator> { Text = Column.GetFilterOperatorText(t), Value = t }))" TextProperty="Text" ValueProperty="Value" TValue="FilterOperator" Value="@Column.GetSecondFilterOperator()" Change="@(args => Column.SetSecondFilterOperator((FilterOperator)args))" />
                                    @if (Column.SecondFilterValueTemplate != null)
                                    {
                                        @Column.SecondFilterValueTemplate(Column)
                                    } 
                                    else if (Column.FilterPropertyType != null && (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType)))
                                    {
                                        <RadzenDropDown Disabled="@(!Column.CanSetFilterValue(false))" AllowClear="false" AllowFiltering="false" TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueAriaLabel }})"
                                        Value=@Column.GetSecondFilterValue() Multiple="false" Placeholder="@Grid.EnumFilterSelectText" TextProperty="Text" ValueProperty="Value" Data=@EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType, Grid.EnumFilterTranslationFunc)
                                        Change="@(args => Column.SetFilterValue(args,false))" />
                                    }
                                    else if (Column.FilterPropertyType != null && PropertyAccess.IsNumeric(Column.FilterPropertyType))
                                    {
                                        @(Grid.DrawNumericFilter(Column, false, false))
                                    }
                                    else if (PropertyAccess.IsDate(Column.FilterPropertyType))
                                    {
                                        <RadzenDatePicker Disabled="@(!Column.CanSetFilterValue(false))" TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title +  Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue() }})"
                                        ShowTime="@Column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="true" DateFormat="@Grid.getFilterDateFormat(Column)"
                                        Value="@Column.GetSecondFilterValue()" Change="@(args => Column.SetFilterValue(args, false))" AllowInput=@(Grid.AllowFilterDateInput) />

                                    }
                                    else if (Column.FilterPropertyType == typeof(bool) || Column.FilterPropertyType == typeof(bool?))
                                    {
                                        <RadzenCheckBox Disabled="@(!Column.CanSetFilterValue(false))" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title +  Grid.SecondFilterValueAriaLabel  + Column.GetSecondFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetSecondFilterValue()" Change="@(args => { Column.SetFilterValue(args, false); Grid.SaveSettings(); })" />
                                    }
                                    else
                                    {
                                        <RadzenTextBox Disabled="@(!Column.CanSetFilterValue(false))" id="@($"{Column.GetColumnPopupID()}-sf2")" aria-label=@(Column.Title + Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue()) Value="@($"{Column.GetSecondFilterValue()}")" Change="@(args => Column.SetFilterValue(args, false))" />
                                    }
                                }
                                else
                                {
                                    @if (Column.FilterValueTemplate != null)
                                    {
                                        @Column.FilterValueTemplate(Column)
                                    }
                                    else
                                    {
                                        <RadzenProgressBarCircular Style="position:absolute;width:100%;" Visible="@isLoading" Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate" />
                                        <RadzenListBox AllowVirtualization="@Column.AllowCheckBoxListVirtualization" AllowClear="true" Multiple="true" Style="height: 300px"
                                        TValue="IEnumerable" Value=@(hasPendingCheckBoxFilter ? pendingCheckBoxFilterValue as IEnumerable : Column.GetFilterValue() as IEnumerable) Change="@ListBoxChange"
                                        Data=@(Column.FilterLookupData ?? filterValues)
                                        TextProperty="@(Column.FilterLookupData != null ? Column.FilterLookupTextProperty : null)"
                                        ValueProperty="@(Column.FilterLookupData != null ? Column.FilterLookupValueProperty : null)"
                                        Count="@filterValuesCount"
                                        LoadData="@(Column.FilterLookupData != null ? default(EventCallback<Radzen.LoadDataArgs>) : EventCallback.Factory.Create<Radzen.LoadDataArgs>(this, LoadFilterValues))"
                                        AllowFiltering="@(Column.FilterLookupData != null ? Column.FilterLookupAllowFiltering : !string.IsNullOrEmpty(Column.GetFilterProperty()))"
                                        Disabled="@(!Column.CanSetFilterValue())" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
                                        InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel  + Column.GetFilterValue() }})">
                                            <Template>
                                                @if (Column.FilterLookupData != null)
                                                {
                                                    @PropertyAccess.GetItemOrValueFromProperty(context, Column.FilterLookupTextProperty)
                                                }
                                                else if (context is Enum enumValue)
                                                {
                                                    @EnumExtensions.GetDisplayDescription(enumValue, Grid.EnumFilterTranslationFunc)
                                                }
                                                else
                                                {
                                                @(!string.IsNullOrEmpty(Column.FormatString) ? string.Format(Column.FormatProvider ?? Grid?.Culture ?? CultureInfo.CurrentCulture, Column.FormatString, context ?? string.Empty) : Convert.ToString(context ?? string.Empty, Grid?.Culture ?? CultureInfo.CurrentCulture))
                                                }
                                            </Template>
                                        </RadzenListBox>
                                    }
                                }
                            </form>
                        }
                    </div>
                    @if (Column.FilterTemplate == null)
                    {
                        <div class="rz-grid-filter-buttons">
                            <RadzenButton ButtonStyle="ButtonStyle.Base" class="rz-clear-filter" Click="@ClearFilter" Text=@Grid.ClearFilterText title="@Grid.ClearFilterText" />
                            @if (!(Grid.AutoApplyCheckBoxListFilter && filterMode == FilterMode.CheckBoxList))
                            {
                                <RadzenButton ButtonStyle="ButtonStyle.Primary" class="rz-apply-filter" form="@($"{Column.GetColumnPopupID()}-form")" ButtonType="ButtonType.Submit" Text=@Grid.ApplyFilterText title="@Grid.ApplyFilterText" />
                            }
                        </div>
                    }
                </RadzenPopup>
            }
        </div>
        @if (Grid.allColumns.Any(c => c.Parent != null) && Grid.AllowFiltering && Column.Filterable && (filterMode == FilterMode.Simple || filterMode == FilterMode.SimpleWithMenu))
        {
            @Grid.RenderSimpleFilter(Column, filterMode)
        }
    </th>
}
else
{
    @foreach(var column in Grid.childColumns.Where(c => c.GetVisible() && c.Parent == Column))
    {
        ColumnIndex = Grid.allColumns.IndexOf(column);//for child must have different columnindex

        <RadzenDataGridHeaderCell RowIndex="@RowIndex" Grid="@Grid" Column="@column" Style="@column.GetStyle(true, true)" ColumnIndex="@ColumnIndex"
        CssClass="@($"rz-unselectable-text {(Grid.AllowSorting && column.Sortable ? "rz-sortable-column" : "")} {column.HeaderCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {Grid.getCompositeCellCSSClass(column)} {Grid.getColumnAlignClass(column)}".Trim())" />
    }
}
@code {
    RadzenPopup? popup;
    ElementReference filterButton;

    int filterValuesCount;
    internal IEnumerable? filterValues;
    string loadDataArgsString = string.Empty;
    bool isLoading = false;
    bool hasPendingCheckBoxFilter;
    object? pendingCheckBoxFilterValue;

    async Task LoadFilterValues(LoadDataArgs loadDataArgs)
    {
        isLoading = true;

        await Task.Yield();

        var property = Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty) ? Column.Property :
            Column.GetFilterProperty();

        var propertyType = PropertyAccess.GetPropertyType(typeof(TItem), property) ?? Column.FilterPropertyType;

        var loadDataArgsString = $"{loadDataArgs.Skip}|{loadDataArgs.Top}{loadDataArgs.Filter}";

        if (Column.Grid.LoadColumnFilterData.HasDelegate)
        {
            var args = new DataGridLoadColumnFilterDataEventArgs<TItem>() { Property = property, Column = Column, Filter = loadDataArgs.Filter, Skip = loadDataArgs.Skip, Top = loadDataArgs.Top };

            await Column.Grid.LoadColumnFilterData.InvokeAsync(args);

            filterValues = !string.IsNullOrEmpty(args.Property) ? 
                args.Data?.AsQueryable().Select(property).Distinct() : args.Data;
            filterValuesCount = args.Count;
        }
        else if(!string.IsNullOrEmpty(property) && (filterValues == null || this.loadDataArgsString != loadDataArgsString) && Column.Grid.Data != null)
        {
            this.loadDataArgsString = loadDataArgsString;

            IQueryable query;

            if(!string.IsNullOrEmpty(loadDataArgs.Filter))
            {
                query = (Column.Grid.childData.Any() ? Column.Grid.GetSelfRefView(Column.Grid.Data.AsQueryable(), "") :
                            Column.Grid.Data.AsQueryable())
                            .Where<TItem>(Column.AlwaysShowAllCheckBoxListData ? 
                                Enumerable.Empty<RadzenDataGridColumn<TItem>>() : 
                                    Column.Grid.allColumns.Where(c => c != Column));

                if (Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty))
                {
                    query = QueryableExtension.Where(query, new FilterDescriptor[] {
                        new FilterDescriptor()
                        {
                            Property = Column.Property,
                            FilterProperty = Column.FilterProperty,
                            FilterValue = loadDataArgs.Filter,
                            FilterOperator = FilterOperator.Contains
                        } },
                            LogicalFilterOperator.Or, FilterCaseSensitivity.CaseInsensitive);
                }
                else
                {
                    query = query.Where(property, loadDataArgs.Filter, StringFilterOperator.Contains, FilterCaseSensitivity.CaseInsensitive);
                }
            }
            else
            {
                query = (Column.Grid.childData.Any() ? Column.Grid.GetSelfRefView(Column.Grid.Data.AsQueryable(), "") :
                    Column.Grid.Data.AsQueryable()).Where<TItem>(Column.AlwaysShowAllCheckBoxListData ?
                                Enumerable.Empty<RadzenDataGridColumn<TItem>>() :
                                    Column.Grid.allColumns.Where(c => c != Column));
            }

            List<object> materializedValues;

            if (Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty))
            {
                var childQuery = query.SelectMany(property);

                if (!string.IsNullOrEmpty(loadDataArgs.Filter))
                {
                    childQuery = childQuery.Where(Column.FilterProperty, loadDataArgs.Filter, StringFilterOperator.Contains, FilterCaseSensitivity.CaseInsensitive);
                }

                var distinctQuery = childQuery.Select(Column.FilterProperty).Distinct();
                materializedValues = QueryableExtension.ToList(distinctQuery).Cast<object>().ToList();
                propertyType = materializedValues.FirstOrDefault(v => v != null)?.GetType() ?? propertyType;
            }
            else
            {
                var distinctQuery = query.Select(property).Distinct();
                materializedValues = QueryableExtension.ToList(distinctQuery).Cast<object>().ToList();
            }

            if (Column.Grid.IsVirtualizationAllowed())
            {
                var currentFilterValue = hasPendingCheckBoxFilter ? pendingCheckBoxFilterValue : Column.GetFilterValue();
                if (currentFilterValue is IEnumerable checkedValues)
                {
                    foreach (var checkedValue in checkedValues.Cast<object>())
                    {
                        if (!materializedValues.Any(v => object.Equals(v, checkedValue)))
                        {
                            materializedValues.Add(checkedValue);
                        }
                    }
                }
            }

            materializedValues.Sort();

            filterValuesCount = materializedValues.Count;

            IEnumerable<object> result = materializedValues;

            if(loadDataArgs.Skip != null)
            {
                result = result.Skip(loadDataArgs.Skip.Value);
            }

            if(loadDataArgs.Top != null)
            {
                result = result.Take(loadDataArgs.Top.Value);
            }

            filterValues = result.ToList().AsQueryable().Cast(propertyType!);

            if (!Column.AllowCheckBoxListVirtualization)
            {
                await InvokeAsync(StateHasChanged);
            }
        }

        isLoading = false;
    }

    string? GetColumnTitleAttribute()
    {
        if (Grid.ShowColumnTitleAsTooltip)
        {
            var title = Column.GetTitle();
            return string.IsNullOrWhiteSpace(title) ? null : title;
        }

        return string.IsNullOrWhiteSpace(Column.HeaderTooltip) ? null : Column.HeaderTooltip;
    }

    async Task ListBoxChange(object args)
    {
        hasPendingCheckBoxFilter = true;

        if (args is not IEnumerable enumerable)
        {
            pendingCheckBoxFilterValue = null;
        }
        else
        {
            Type? propertyType = null;

            if (Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty))
            {
                var collectionType =  PropertyAccess.GetPropertyType(typeof(TItem), Column.Property);
                if (collectionType != null && collectionType.IsGenericType)
                {
                    var itemType = collectionType.GenericTypeArguments.FirstOrDefault();
                    if(itemType != null)
                    {
                        propertyType = PropertyAccess.GetPropertyType(itemType, Column.FilterProperty);
                    }
                }
            }
            else
            {
                propertyType = PropertyAccess.GetPropertyType(typeof(TItem), Column.GetFilterProperty());
            }

            if (propertyType == null)
            {
                propertyType = Column.FilterPropertyType;
            }

            pendingCheckBoxFilterValue = enumerable.Cast<object>().Any()
                ? propertyType != null ? QueryableExtension.ToList(enumerable.AsQueryable().Cast(propertyType)) : enumerable
                : null;
        }

        if (Grid.AutoApplyCheckBoxListFilter)
        {
            Column.SetFilterValue(pendingCheckBoxFilterValue);
            hasPendingCheckBoxFilter = false;
            pendingCheckBoxFilterValue = null;
            await Grid.ApplyFilter(Column, false);
        }
    }

    string getFilterOpen()
    {
        return Grid.FilterPopupRenderMode == PopupRenderMode.Initial ? $"Radzen.togglePopup(this, '{Column.GetColumnPopupID()}', false, null, null, true, true)" : "";
    }

    async Task ToggleFilter()
    {
        if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand && popup != null)
        {
            await popup.ToggleAsync(filterButton);
        }
        else if (Grid.FilterPopupRenderMode == PopupRenderMode.Initial)
        {
            var jsRuntime = Grid.GetJSRuntime();
            if (jsRuntime != null)
            {
                await jsRuntime.InvokeVoidAsync("Radzen.togglePopup", filterButton, Column.GetColumnPopupID(), false, null, null, true, true);
            }
        }

        Grid.allColumns
        .Where(c => c.GetVisible() && c.UniqueID != Column.UniqueID)
        .Select(c => c.headerCell)
        .Where(cell => cell != null)
        .ToList()
        .ForEach(cell => InvokeAsync(cell!.CloseFilter));
    }

    async Task ClearFilter()
    {
        hasPendingCheckBoxFilter = false;
        pendingCheckBoxFilterValue = null;

        if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand && popup != null)
        {
            await popup.CloseAsync();
        }
        await Grid.ClearFilter(Column, true);
    }

    string? getBlur()
    {
        return IsAdvancedNumeric() ? "Radzen.blur(this, event)" : null;
    }

    bool IsAdvancedNumeric()
    {
        return (Column.FilterMode ?? Grid.FilterMode) == FilterMode.Advanced &&
            Column.FilterPropertyType != null && PropertyAccess.IsNumeric(Column.FilterPropertyType) &&
                !(PropertyAccess.IsEnum(Column.FilterPropertyType) || PropertyAccess.IsNullableEnum(Column.FilterPropertyType));
    }

    async Task ApplyFilter()
    {
        if (IsAdvancedNumeric())
        {
            var targetType = Column.FilterPropertyType != null ? (Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType) : typeof(object);

            var jsRuntime = Grid.GetJSRuntime();

            if (jsRuntime == null)
            {
                return;
            }

            if (Column.FilterValueTemplate == null)
            {
                var firstInputValue = await jsRuntime.InvokeAsync<string>("Radzen.getNumericValue", Grid.getFilterInputId(Column) + "f");
                if (!object.Equals($"{Column.GetFilterValue()}", $"{firstInputValue}"))
                {
                    Column.SetFilterValue(!string.IsNullOrEmpty(firstInputValue) ? Convert.ChangeType(firstInputValue, targetType) : null);
                }
            }

            if (Column.SecondFilterValueTemplate == null)
            {
                var secondInputValue = await jsRuntime.InvokeAsync<string>("Radzen.getNumericValue", Grid.getFilterInputId(Column) + "s");
                if (!object.Equals($"{Column.GetSecondFilterValue()}", $"{secondInputValue}"))
                {
                    Column.SetFilterValue(!string.IsNullOrEmpty(secondInputValue) ? Convert.ChangeType(secondInputValue, targetType) : null, false);
                }
            }
        }

        if (hasPendingCheckBoxFilter)
        {
            Column.SetFilterValue(pendingCheckBoxFilterValue);
            hasPendingCheckBoxFilter = false;
            pendingCheckBoxFilterValue = null;
        }

        if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand && popup != null)
        {
            await popup.CloseAsync();
        }
        await Grid.ApplyFilter(Column, true);
    }

    [Parameter(CaptureUnmatchedValues = true)]
    public IReadOnlyDictionary<string, object> Attributes { get; set; } = new Dictionary<string, object>();

    [Parameter]
    public RadzenDataGridColumn<TItem> Column { get; set; } = default!;

    [Parameter]
    public int ColumnIndex { get; set; }

    [Parameter]
    public int RowIndex { get; set; }

    [Parameter]
    public RadzenDataGrid<TItem> Grid { get; set; } = default!;

    [Parameter]
    public string CssClass { get; set; } = string.Empty;

    [Parameter]
    public string Style { get; set; } = string.Empty;

    private string GetStyle()
    {
        var styles = (new List<string>() { Column.GetStyle(true, true), Style }).Distinct().ToList();

        if (Attributes?.TryGetValue("style", out var styleAttribute) == true)
        {
            var styleValue = Convert.ToString(styleAttribute);
            if (styleValue != null)
            {
                if (styleValue != null)
                {
                    styles.Add(styleValue);
                }
            }
        }

        var finalStyle = string.Join(";",
                styles
                .Select(x => x?.Trim().TrimEnd(';'))
                .Where(x => !string.IsNullOrEmpty(x))
            );

        return finalStyle;
    }

    private string getFilterIconCss(RadzenDataGridColumn<TItem> column)
    {
        var additionalStyle = Column.HasActiveFilter() ? "rz-grid-filter-active" : "";
        return $"notranslate rzi rz-grid-filter-icon {additionalStyle}";
    }

    protected override void OnParametersSet()
    {
        base.OnParametersSet();

        if (Column != null)
        {
            Column.headerCell = this;
        }
    }

    internal async Task CloseFilter()
    {
        if (popup != null)
        {
            await popup.CloseAsync(filterButton);
        }
    }

    async Task StartColumnResize(MouseEventArgs args)
    {
        if(args.Button == 0)
        {
            await Grid.StartColumnResize(args, ColumnIndex);
        }
    }

    async Task StartColumnResizeTouch(TouchEventArgs args)
    {
        if (args.ChangedTouches != null && args.ChangedTouches.Length > 0)
        {
            await Grid.StartColumnResize(args.ChangedTouches[0].ClientX, ColumnIndex);
        }
    }

    async Task StopColumnResize(EventArgs args)
    {
        if (args is MouseEventArgs mouseArgs && mouseArgs.Button != 0)
        {
            return;
        }
        await Grid.StopColumnResize(ColumnIndex);
    }

    internal async Task OpenFilter()
    {
        if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand && popup != null)
        {
            await popup.ToggleAsync(filterButton);
        }
        else
        {
            var jsRuntime = Grid.GetJSRuntime();
            if (jsRuntime != null)
            {
                await jsRuntime.InvokeVoidAsync("Radzen.togglePopup", filterButton, Column.GetColumnPopupID(), false, null, null, true, true);
            }
        }
    }

    async Task OnFilterPopupKeyPressed(KeyboardEventArgs args)
    {
        var key = args.Code ?? args.Key;
        if (key == "Escape")
        {
            await CloseFilter();
            await ClearFilter();
            var jsRuntime = Grid.GetJSRuntime();
            if (jsRuntime != null)
            {
                await jsRuntime.InvokeVoidAsync("Radzen.focusElement", Grid.GridId());
            }
        }
    }
}
