sap.ui.define([
], function () {
    "use strict";

    /**
     * Constructor for SmartTableChartCommon
     * @param {sap.ui.core.Control} oControl - The SmartTable or SmartChart control
     * @param {object} oController - The controller instance
     * @param {object} oFactory - The ControlStateWrapperFactory instance
     * @param {string} sInitializationEvent - The initialization event name (e.g., "initialise")
     * @param {object} [mExtensions] - Optional extension functions for control-specific state handling
     * @param {function} [mExtensions.fnExtendGetUiState] - Function to extend UiState during getState
     *   Called with (oStateUiState, oControlUiState) where modifications to oStateUiState add to the state
     * @param {function} [mExtensions.fnExtendSetUiState] - Function to extend UiState during setState
     *   Called with (oStateUiState, oControlUiState) where oStateUiState contains the state to apply
     * @returns {object}
     */

    // deals with state of SFB itself, without SVM (see SmartVariantManagementWrapper) and go-button
    // (does not contain a state from SFB point of view - however, we remember whether it was pressed once
    // to restore data - this information is not considered being part of SFB)
    function SmartTableChartCommon(oControl, oController, oFactory, sInitializationEvent, mExtensions) {
        var oSmartControl = oControl;
        var oVariantManagementControl, bVariantManagementActive;
        var oSmartControlStateWrapper;
        var oVariantManagementStateWrapper;
        var oVariantManagementInitializedResolve;
        var fnResolveReady;

        var oVariantManagementInitializedPromise = new Promise(function(resolve){
            oVariantManagementInitializedResolve = resolve;
        });

        var oReadyPromise = new Promise(function(resolve) {
            fnResolveReady = resolve;
        });

        // Initialize control state wrapper and check if control is already initialized
        oSmartControlStateWrapper = getControlWrapper(oSmartControl);
        if (!oSmartControl.isInitialised()) {
            // In case Smart Chart/Table is not yet initialized
            // listen to the initialize event & then initialize the wrapper
            oSmartControl.attachEvent(sInitializationEvent, fnControlInitialized);
        } else {
            fnControlInitialized();
        }

        // creates a simple wrapper for a SmartTable or SmartChart (a control using UIState) (ignoring initialization and vm)
        function getControlWrapper(oControl) {
            var bIsApplying = false;		// avoid forwarding change event when caused by us applying a state
            function fnGetState() {
                var oControlUiState = oControl.getUiState(); // unfortunately not serializable, but a managed object
                var oStateUiState = {
                    oPresentationVariant: oControlUiState.getPresentationVariant(),
                    oSelectionVariant: oControlUiState.getSelectionVariant()
                };

                // Allow extension to add control-specific properties (e.g., tableSettings for SmartTable)
                if (mExtensions && mExtensions.fnExtendGetUiState) {
                    mExtensions.fnExtendGetUiState(oStateUiState, oControlUiState);
                }

                return {
                    oUiState: oStateUiState
                };
            }

            function fnSetState(oState) {
                if (oState) {
                    if (!oState.oUiState) {
                        // Legacy handling for state without additional level oUiState (created with 1.99.0 - 1.99.3). Could be relevant on LR and OP, and independent of VM
                        oState = {
                            oUiState: oState
                        };
                    }
                    bIsApplying = true;
                    // don't create UiState (managed object) from scratch, but fetch it from control and only apply known properties from state - thus, if any other properties would be added, we don't
                    // interfere with them
                    var oControlUiState = oControl.getUiState();
                    if (!oControlUiState.getProperty("variantName")){
                            oControlUiState.setPresentationVariant(oState.oUiState.oPresentationVariant);
                            oControlUiState.setSelectionVariant(oState.oUiState.oSelectionVariant);

                            // Allow extension to set control-specific properties (e.g., tableSettings for SmartTable)
                            if (mExtensions && mExtensions.fnExtendSetUiState) {
                                mExtensions.fnExtendSetUiState(oState.oUiState, oControlUiState);
                            }

                            oControl.setUiState(oControlUiState);
                    }
                    bIsApplying = false;
                } else {
                    // set standard variant to overcome "implicit personalization" (esp. for OP when navigating to different object in discovery mode)
                    // todo: check interference with LR (assumption: no interference, should only be called when no vm - neither page nor control level)
                    oControl.setCurrentVariantId("");
                }
            }

            function fnAttachStateChanged(fnHandler) {
                oControl.attachUiStateChange(function () {
                    if (!bIsApplying) {
                        fnHandler();
                    }
                });
            }

            function fnGetLocalId() {
                return oController.getView().getLocalId(oControl.getId());
            }

            return {
                getState: fnGetState,
                setState: fnSetState,
                getLocalId: fnGetLocalId,
                bVMConnection: oControl && oControl.getSmartVariant && !!oControl.getSmartVariant(),
                attachStateChanged: fnAttachStateChanged,
                onAfterVariantInitialise: oVariantManagementInitializedResolve
            };
        }

        function fnGetState() {
            if (oVariantManagementStateWrapper) {
                // Variant management is active
                return oVariantManagementStateWrapper.getState();
            }

            // No variant management - return control state directly
            return oSmartControlStateWrapper.getState();
        }

        function fnSetState(oState) {
            if (!oState) {
                // If no state provided, set standard variant on control
                if (bVariantManagementActive) {
                    oVariantManagementStateWrapper.setState(oState);
                } else {
                    oSmartControlStateWrapper.setState(oState);
                }
                return;
            }

            // Map legacy states - before separating VM state from state of managed control, inner state information
            // (presentation variant and selection variant) were put as oUiState on the same level as VM state
            // information (variant id and dirty indicator), now they are contained in map managedControlStates
            if (oState.oUiState && !oState.managedControlStates) {
                oState.managedControlStates = Object.create(null);
                oState.managedControlStates[oSmartControlStateWrapper.getLocalId()] = {oUiState: oState.oUiState};
            }
            if (oState.bVariantModified !== undefined) {
                oState.modified = oState.bVariantModified;
            }
            if (oState.sVariantId !== undefined) {
                oState.variantId = oState.sVariantId;
            }

            if (bVariantManagementActive) {
                // In case VariantManagement is configured
                oVariantManagementStateWrapper.setState(oState);
            } else {
                // SmartTable/Chart shall be used
                oSmartControlStateWrapper.setState(oState);
            }
        }

        function fnControlInitialized() {
            // deal with VM
            // 3 possibilities with respect to VM
            // a) no VM (relevant for this control) at all
            // b) control managed as part of page wide variant management
            // c) control creates own VM
            // In first two cases, we just need a wrapper for the control's state itself (which is either be used for appState directly or being passed to a VM wrapper from caller). In the third case
            // however, although the latter would also be possible, it would lead to recreating the same logic at several places (LR, ALP (as long as not unified), and OP, and for SmartTable and SmartChart
            // in each case) -thus, we better create and return a vm wrapper here, passing the inner wrapper (the same, we would create in the other cases).
            // Additionally, this simplifies the adaptation to the change of format (formerly, variant data and control data were mixed on same level)

            // to identify the situation, check properties smartVariant and useVariantManagement. (In LR/ALP, we could directly check component settings controlling these properties, but not
            // in OP, as they could be different per section. Checking control's properties would also incorporate UI changes, however, these properties are anyway not allowed to be changed.)
            // values:	smartVariant	useVariantManagement
            // a)			null			false				on LR currently: truthy (id of VM) and true - but PageVM is not created, and thus being ignored => adapt how properties are set in xml
            // b)			truthy 			true				smartVariant = id of pageVM
            // c)			null			true
            if (!oSmartControl.getSmartVariant() && oSmartControl.getUseVariantManagement()) {
                oVariantManagementControl = oSmartControl.getVariantManagement();
                oSmartControlStateWrapper.bVMConnection = true;
                bVariantManagementActive = true;
                if (oVariantManagementControl) {
                    // VariantManagement is already initialized
                    oVariantManagementStateWrapper = oFactory.getControlStateWrapper(oVariantManagementControl, {
                        managedControlWrappers: [ oSmartControlStateWrapper ]
                    });
                    fnResolveReady();
                } else {
                    // Wait for VariantManagement to be initialized
                    oSmartControl.attachAfterVariantInitialise(fnControlInitialized);
                }
            } else {
                // No variant management or page-level variant management - ready immediately
                fnResolveReady();
            }
        }

        function fnAttachStateChanged(fnHandler) {
            if (bVariantManagementActive) {
                oVariantManagementStateWrapper.attachStateChanged(fnHandler);
            } else {
                oSmartControlStateWrapper.attachStateChanged(fnHandler);
            }
        }

        return {
            getState: fnGetState,
            setState: fnSetState,
            attachStateChanged: fnAttachStateChanged,
            bVMConnection: oSmartControl && oSmartControl.getSmartVariant && !!oSmartControl.getSmartVariant(),
            oVariantManagementInitializedPromise: oVariantManagementInitializedPromise,
            onAfterVariantInitialise: oVariantManagementInitializedResolve,
            oReadyPromise: oReadyPromise
        };
    }

    return SmartTableChartCommon;
});
