using System;
using Unity.Properties;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UIElements;

public class DataBindingTutorialUI : MonoBehaviour
{
    // Stylesheet to use for the UI, in production this would most likely be added to your .tss theme file
    // that is assigned to the panel, but for this tutorial we have an option way to access one during development
    [SerializeField]
    private StyleSheet stylesheet;

    // Data model that will be bound to and used to populate the UI elements
    private MyInventoryDataModel _inventoryDataModel;

    // Used to get a reference to the root of the visual tree
    private VisualElement _root;

    // Used as a container that we can applu a datasource to for binding data
    private VisualElement _formContainer;

    // Form Elements
    private DropdownField _dropdownField;
    private TextField _textField;
    private ListView _listView;
    private Button _button;

    private void OnEnable()
    {
        // When connecting to UI elements, using the OnEnable method will help keep null ref errors in check

        // Add your DataModel so you can link the elements to the data
        _inventoryDataModel = new MyInventoryDataModel();




        // Find and set the root document for the panel so we can add elements to its visual tree
        _root = GetComponent().rootVisualElement;




        // Create a container to hold the UI and isolate the datasource,
        // IF you have a single datasource for the entire Panel
        // then you could set the root datasource to your datamodel
        // But if you want this datasource isolated in the UI then an additional container is needed
        _formContainer = new VisualElement()
        {
            // When an element has a name you can use it with #Name in your stylesheets
            // This is optional, but it can be useful for debugging and is a good practice
            // since named elements in code only change if purposefully changed.
            // For this element it would be #FormLayout in the stylesheet
            name = "FormLayout",
        };

        // Set the datasource for the form to our datamodel
        _formContainer.dataSource = _inventoryDataModel;

        // Add the form container to the root element
        _root.Add(_formContainer);




        // Add the stylesheet to the form container element so it can be rendered (if it exists).
        // This is optional, but if you have a stylesheet you can use it to style the UI elements.
        // This could also be applied to the root element if you wanted the styles to apply to the entire panel
        if (stylesheet != null)
        {
            _formContainer.styleSheets.Add(stylesheet);
        }




        // Add a header with a clock using the ClockElement from the CustomBinding Examples
        VisualElement header = new ClockElement();
        header.name = "WindowHeader";
        _formContainer.Add(header);


        VisualElement dropDownContainer = new VisualElement
        {
            style =
            {
                flexDirection = FlexDirection.Row
            }
        };

        _formContainer.Add(dropDownContainer);

        _dropdownField = new DropdownField()
        {
            name = "ItemTypeDropdown",
            style =
            {
                flexGrow = 1,
            },
            label = "Select Item Type:",
        };

        // Making the Label look better is not the label as you would think,
        // its the labelElement style you need to change, we are doing here
        // because its easier than finding the USS selector in the child element
        _dropdownField.labelElement.style.unityTextAlign = TextAnchor.MiddleLeft;
        _dropdownField.labelElement.style.unityFontStyleAndWeight = FontStyle.Bold;
        _dropdownField.labelElement.style.marginRight = 0;
        _dropdownField.labelElement.style.paddingRight = 5;
        _dropdownField.labelElement.style.flexGrow = 0;

        // this is the one that make the dropdown label seam uselessly large
        _dropdownField.labelElement.style.minWidth = 100;

        // Bind the dropdown "choices" property (which expects a list of strings)
        // to the data models "ItemTypes" which IS a list of strings
        // and set the mode to read only (target being the dropdown)
        _dropdownField.SetBinding("choices", new DataBinding()
        {
            dataSourcePath = new PropertyPath("ItemTypes"),
            bindingMode = BindingMode.ToTarget // Read only setting
        });

        // Also, bind the selected value of the dropdown to a different property in the model.
        // This is the one that will be updated when the user selects an item type.
        // The model will then update the list based on the new filter value
        _dropdownField.SetBinding("value", new DataBinding()
        {
            dataSourcePath = new PropertyPath("SelectedItemType"),
            bindingMode = BindingMode.TwoWay // Read/Write setting
        });

        // Add the DropdownField to the UI
        dropDownContainer.Add(_dropdownField);


        // We will use a button to demonstrate capturing click events in a simple way
        Button formReset = new Button
        {
            text = "Reset",
            name = "ResetButton"
        };
        dropDownContainer.Add(formReset);
        // You could register a callback OR just use the one built into the button
        formReset.clicked += () =>
        {
            // We have a simple untracked 'property' that just calls a method when a value is set.
            // This is a super easy way to catch click events in a UI without having to register
            // a bunch of callbacks since the button has one built in
            _inventoryDataModel.RaiseResetFormEvent = true;
        };



        // Add a text field to filter the list items
        // This is a simple text field that will update the filter text property in the model
        _textField = new TextField
        {
            name = "FilterText",
        };

        _textField.SetBinding("value", new DataBinding()
        {
            dataSourcePath = new PropertyPath("FilterText"),
            bindingMode = BindingMode.ToSource  // Write Only setting
        });
        _formContainer.Add(_textField);




        // Add a list view to display the filtered items
        // This is a list view that will be updated by the data model
        _listView = new ListView
        {
            name = "ListView",
            // add some inline style to make it more visible
            showAlternatingRowBackgrounds = AlternatingRowBackground.All,
            // Use a hybrid approch to allow the use of a class to create the line items
            // this allows for advanced form views with multiple control elements at runtime
            makeItem = () =>
            {
                var lineItem = new MakeListViewLineItem();
                return lineItem;
            },
            fixedItemHeight = 71,
            showBorder = true,
            // Use an isolated method group to load data in the line items
            bindItem = Item
        };

        // Bind the list view data to the data models output
        _listView.SetBinding("itemsSource", new DataBinding() {dataSourcePath = new PropertyPath("Items")});

        _formContainer.Add(_listView);
        return;


        // This is only past the return because its an isolated method use to build list line items
        void Item(VisualElement e, int i) => BindItem(e as MakeListViewLineItem, i);
    }
    private void BindItem(MakeListViewLineItem makeListViewLineItem, int i)
    {
        var label = makeListViewLineItem.Q