var DataRetriever = Class.create({

    initialize: function(flowExecutionKey, baseErrorHandler) {

        this.ajaxBufferId = 'ajaxbuffer';
        this.ajaxBuffer = $(this.ajaxBufferId);

        this.baseUrl = 'flow.html?_flowExecutionKey=' + flowExecutionKey;

        this.baseErrorHandler = baseErrorHandler;
    },

    getData: function(urlParams, onDataRetrievedFn, onErrorRetrievedFn) {

        var url = this.baseUrl;

        for (var name in urlParams) {

            if (urlParams[name]) {
                url += '&' + name + '=' + encodeUriParameter(urlParams[name]);
            }
        }

        var callback = function(inst) {
            return function() {
                var responseModelJson = inst.ajaxBuffer.innerHTML;
                var onDataRetrieved = (onDataRetrievedFn) ? onDataRetrievedFn : inst.onDataRetrieved;
                var onErrorRetrieved = (onErrorRetrievedFn) ? onErrorRetrievedFn : inst.onErrorRetrieved;

                try {

                    var responseModel = responseModelJson.evalJSON();

                } catch(ex) {
                    onErrorRetrieved(responseModel);
                }

                if (responseModel.error) {

                    onErrorRetrieved(responseModel.error);
                } else {

                    onDataRetrieved(responseModel.data);
                }

            }
        }(this);

        ajaxTargettedUpdate(
            url,
            this.ajaxBufferId,
            null,
            callback,
            this.baseErrorHandler
        );
    },

    onDataRetrieved: function(data) {
        alert(data);
    },

    onErrorRetrieved: null,

    getItems : function (tenantId, itemName, firstResult) { /* Do Nothing*/ },

    onItemsRetrieved: null,

    getItemDetails: function(userName) { /* Do Nothing*/ },

    onItemDetailsRetrieved: null,

    isItemExist: function(userName) { /* Do Nothing*/ },

    onIsItemExistSuccess: null,

    getAssignedItems: function(itemName, searchName, tenantId, firstResult) { /* Do Nothing*/ },

    onAssignedItemsRetrieved: null,

    getAvailableItems: function(itemName, searchName, tenantId, firstResult) { /* Do Nothing*/ },

    onAvailableItemsRetrieved: null
});

var UserDataRetriever = Class.create(DataRetriever, {

    getItems: function(tenantId, userName, firstResult) {
        var urlParams = {
            _eventId : 'loadUsers',
            tenantId : tenantId,
            userName : userName,
            firstResult : firstResult
        };

        this.getData(urlParams, this.onItemsRetrieved);
    },

    isItemExist: function(userName, tenantId) {

        var name = UserUtil.getUserNameWithTenant(userName, tenantId);

        var urlParams = {
            _eventId : 'isUserExist',
            userName : name
        };

        this.getData(urlParams, this.onIsItemExistSuccess);
    },

    getItemDetails: function(userName) {
        var urlParams = {
            _eventId : 'getUserDetails',
            userName : userName
        };

        this.getData(urlParams, this.onItemDetailsRetrieved);
    },

    getAvailableRoles: function(userName, userRoles, roleName, firstResult) {
        var urlParams = {
            _eventId : 'getAvailableRoles',
            userRoles : '[' + userRoles.join(',') + ']',
            roleName : roleName,
            userName : userName,
            firstResult : firstResult
        };

        this.getData(urlParams, this.onRolesRetrieved);
    },

    getAvailableItems: function(user, userRoles, roleName, firstResult) {

        var name = UserUtil.getUserNameWithTenant(user.userName, user.tenantId);

        var urlParams = {
            _eventId : 'getAvailableRoles',
            userRoles : '[' + userRoles.join(',') + ']',
            roleName : roleName,
            userName : name,
            firstResult : firstResult
        };

        this.getData(urlParams, this.onAvailableItemsRetrieved);
    },

    onRolesRetrieved: function(data) {
        alert(data);
    }
});

var RoleDataRetriever = Class.create(DataRetriever, {

    getItems: function(tenantId, roleName, firstResult) {
        var urlParams = {
            _eventId : 'loadRoles',
            tenantId : tenantId,
            roleName : roleName,
            firstResult : firstResult
        };

        this.getData(urlParams, this.onItemsRetrieved);
    },

    isItemExist: function(roleName, tenantId) {

        var name = RoleUtil.getRoleNameWithTenant(roleName, tenantId);

        var urlParams = {
            _eventId : 'isRoleExist',
            roleName : name
        };

        this.getData(urlParams, this.onIsItemExistSuccess);
    },

    getItemDetails: function(roleName) {
        var urlParams = {
            _eventId : 'getRoleDetails',
            roleName : roleName
        };

        this.getData(urlParams, this.onItemDetailsRetrieved);
    },

    getRoleUsers: function(role, userName, tenantId, firstResult) {

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var urlParams = {
            _eventId : 'loadAssignedUsers',
            roleName : rn,
            userName : userName,
            tenantId : tenantId,
            firstResult : firstResult
        };


        var onUsersRetrieved = function(data) {
            for (var i=0; i<this.usersRetrievedListeners.length; i++) {
                this.usersRetrievedListeners[i].usersDataRetrieved(data);
            }
        }.bind(this);

        this.getData(urlParams, onUsersRetrieved);
    },

    getAssignedItems: function(role, userName, tenantId, firstResult) {

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var urlParams = {
            _eventId : 'loadAssignedUsers',
            roleName : rn,
            userName : userName,
            tenantId : tenantId,
            firstResult : firstResult
        };

        this.getData(urlParams, this.onAssignedItemsRetrieved);
    },

    getAvailableItems: function(role, userName, tenantId, firstResult) {

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var urlParams = {
            _eventId : 'loadAvailableUsers',
            roleName : rn,
            userName : userName,
            tenantId : tenantId,
            firstResult : firstResult
        };

        this.getData(urlParams, this.onAvailableItemsRetrieved);
    },

    addUsersRetrievedListener: function(usersRetrievedListener) {
        this.usersRetrievedListeners.push(usersRetrievedListener);
    },

    usersRetrievedListeners: []
});

var DataUpdater = Class.create({

    initialize: function(flowExecutionKey, baseErrorHandler) {

        this.ajaxBufferId = 'ajaxbuffer';
        this.ajaxBuffer = $(this.ajaxBufferId);

        this.baseUrl = 'flow.html?_flowExecutionKey=' + flowExecutionKey;

        this.baseErrorHandler = baseErrorHandler;
    },

    updateData: function(url, dataParams, onSuccessFn, onErrorFn) {
        var callback = function(inst) {
            return function() {
                var responseModelJson = inst.ajaxBuffer.innerHTML;
                var onSuccess = (onSuccessFn) ? onSuccessFn : inst.onSuccess;
                var onError = (onErrorFn) ? onErrorFn : inst.onError;

                try {

                    var responseModel = responseModelJson.evalJSON();
                } catch(ex) {

                    onError(responseModel);
                }

                if (responseModel.error) {

                    onError(responseModel.error)
                } else {

                    onSuccess(responseModel.data);
                }

            }
        }(this);

        var params = (dataParams) ? dataParams : {};

        ajaxTargettedUpdate(
            url,
            this.ajaxBufferId,
            null,
            callback,
            this.baseErrorHandler,
            this.createPostData(params)
        );
    },

    createPostData : function(params) {
        var data = appendPostData("", params);

        return data;
    },

    onSuccess : function(data) { /* Do Nothing*/ },
    onError : function(error) { /* Do Nothing*/ },

    deleteItem : function(error) { /* Do Nothing*/ },
    onDeleteItemSuccess : null,
    onDeleteItemError : null,

    updateItem : function() { /* Do Nothing*/ },
    onUpdateItemSuccess: null,
    onUpdateItemError: null,

    createItem : function() { /* Do Nothing*/ },
    onCreateItemSuccess: null,
    onCreateItemError: null,

    changeItems: function(itemName, tenantId, availableItemList, assignedItemList, assign, unassign) { /* Do Nothing*/ },
    onChangeItemsSuccess: null,
    onChangeItemsError: null

});

var UserDataUpdater = Class.create(DataUpdater, {

    deleteItem: function(user) {
        var url = this.baseUrl + '&_eventId=delete';

        var un = UserUtil.getUserNameWithTenant(user.userName, user.tenantId);
        var data = {'userName' : un};

        this.updateData(url, data, this.onDeleteItemSuccess, this.onDeleteItemError);
    },

    updateItem: function(originalUser, newUser) {
        var url = this.baseUrl + '&_eventId=update';

        var un = UserUtil.getUserNameWithTenant(originalUser.userName, originalUser.tenantId);
        var data = {
            'userName' : un,
            'userDetails' : Object.toJSON(newUser)
        };

        this.updateData(url, data, this.onUpdateItemSuccess, this.onUpdateItemError);
    },

    createItem: function(user) {
        var url = this.baseUrl + '&_eventId=create';

        var data = {
            'userDetails':Object.toJSON(user)
        };

        this.updateData(url, data, this.onCreateItemSuccess, this.onCreateItemError);
    }
});

var RoleDataUpdater = Class.create(DataUpdater, {

    deleteItem: function(role) {
        var url = this.baseUrl + '&_eventId=delete';

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var data = {'roleName' : rn};

        this.updateData(url, data, this.onDeleteItemSuccess, this.onDeleteItemError);
    },

    updateItem: function(originalRole, newRole) {
        var url = this.baseUrl + '&_eventId=update';

        var rn = RoleUtil.getRoleNameWithTenant(originalRole.roleName, originalRole.tenantId);
        var data = {
            'roleName' : rn,
            'roleDetails' : Object.toJSON(newRole)
        };

        this.updateData(url, data, this.onUpdateItemSuccess, this.onUpdateItemError);
    },

    createItem: function(role) {
        var url = this.baseUrl + '&_eventId=create';

        var data = {
            'roleDetails':Object.toJSON(role)
        };

        this.updateData(url, data, this.onCreateItemSuccess, this.onCreateItemError);
    },

    changeItems: function(role, tenantId, availableItemList, assignedItemList, assign, unassign) {

        var url = this.baseUrl + '&_eventId=updateRoleUsers';

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var data = {
            roleName : rn,
            assignUsers : '[' + assign.join(',') + ']',
            unassignUsers : '[' + unassign.join(',') + ']',
            availableUserList : Object.toJSON({
                userName : availableItemList.searchWord,
                firstResult : availableItemList.firstResult
            }),
            assignedUserList : Object.toJSON({
                userName : assignedItemList.searchWord,
                firstResult : assignedItemList.firstResult
            })
        };

        if (tenantId) {

            data.tenantId = tenantId;
        }
        
        this.updateData(url, data, this.onChangeItemsSuccess, this.onChangeItemsError);
    },

    initEdit: function(role) {
        var url = this.baseUrl + '&_eventId=initEdit';

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var data = {'roleName' : rn};

        this.updateData(url, data, this.onInitEditSuccess, this.onInitEditError);
    },

    onInitEditSuccess : null,
    onInitEditError : null,

    cancelEdit: function(role) {
        if (role) {
            var url = this.baseUrl + '&_eventId=cancelEdit';

            var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
            var data = {'roleName' : rn};

            this.updateData(url, data, this.onCancelEditSuccess, this.onCancelEditError);
        }
    },

    onCancelEditSuccess : null,
    onCancelEditError : null,

    initChangeUsers: function(role) {
        var url = this.baseUrl + '&_eventId=initChangeUsers';

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var data = {'roleName' : rn};

        this.updateData(url, data, this.onInitChangeUsersSuccess, this.onInitChangeUsersError);
    },

    onInitChangeUsersSuccess : null,
    onInitChangeUsersError : null,

    revertUsersChanges: function(role) {
        var url = this.baseUrl + '&_eventId=revertUsersChanges';

        var rn = RoleUtil.getRoleNameWithTenant(role.roleName, role.tenantId);
        var data = {'roleName' : rn};

        this.updateData(url, data, this.onRevertUsersChangesSuccess, this.onRevertUsersChangesError);
    },

    onRevertUsersChangesSuccess : null,
    onRevertUsersChangesError : null
});
