/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */
package com.queplix.core.client.app.rpc;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.queplix.core.client.app.vo.AccumulatedEntitiesRequestObject;
import com.queplix.core.client.app.vo.AdhocDeleteReportRequest;
import com.queplix.core.client.app.vo.AdhocPrintRequestObject;
import com.queplix.core.client.app.vo.AdhocSearchGridRecordsRequest;
import com.queplix.core.client.app.vo.BaseGridRequest;
import com.queplix.core.client.app.vo.ClearEntityRequestObject;
import com.queplix.core.client.app.vo.CustomizeGridRequestObject;
import com.queplix.core.client.app.vo.EmailComposeRequestObject;
import com.queplix.core.client.app.vo.EntityDeleteRequestObject;
import com.queplix.core.client.app.vo.EntityIDRequestObject;
import com.queplix.core.client.app.vo.EntityUpdateRequestObject;
import com.queplix.core.client.app.vo.FieldDataRequest;
import com.queplix.core.client.app.vo.FormatDataRequestObject;
import com.queplix.core.client.app.vo.LoginRequestObject;
import com.queplix.core.client.app.vo.MessageReadFlagRequest;
import com.queplix.core.client.app.vo.MetaData;
import com.queplix.core.client.app.vo.NewEntityRequestObject;
import com.queplix.core.client.app.vo.ParseDateRequestObject;
import com.queplix.core.client.app.vo.PortalCssRequest;
import com.queplix.core.client.app.vo.PrintFormRequestObject;
import com.queplix.core.client.app.vo.PrintGridRequestObject;
import com.queplix.core.client.app.vo.SaveAdhocReportRequestObject;
import com.queplix.core.client.app.vo.SavedSearchDeleteRequestObject;
import com.queplix.core.client.app.vo.SavedSearchObject;
import com.queplix.core.client.app.vo.chart.ChartRequestObject;

import java.util.ArrayList;

/**
 * Centralized application's remote calls provider.
 * All interactions with the server go through this class.
 *
 * @author Sultan Tezadov
 */
public class RPC {

    // ============ Request events
    public static interface RequestListener {
        public void onRequestStarted();

        public void onRequestFinished(boolean success);

        public void handleError(Throwable caught);
    }

    private static ArrayList requestListeners = new ArrayList();

    public static void addRequestListener(RequestListener listener) {
        requestListeners.add(listener);
    }

    protected static void notifyRequestStarted() {
        for(int i = 0; i < requestListeners.size(); i++) {
            ((RequestListener) requestListeners.get(i)).onRequestStarted();
        }
    }

    protected static void notifyRequestFinished(boolean success) {
        for(int i = 0; i < requestListeners.size(); i++) {
            ((RequestListener) requestListeners.get(i)).onRequestFinished(
                    success);
        }
    }

    private static void notifyError(Throwable caught) {
        for(int i = 0; i < requestListeners.size(); i++) {
            ((RequestListener) requestListeners.get(i)).handleError(caught);
        }
    }
    // ============ End of Request events

    // ============ QAsyncCallback class

    public static abstract class QAsyncCallback {
        public AsyncCallback callback = new AsyncCallback() {
            public void onFailure(Throwable caught) {
                notifyRequestFinished(false);
                handleError(caught);
                onRequestEnded(false, caught);
            }

            public void onSuccess(Object result) {
                notifyRequestFinished(true);
                onRequestEnded(true, result);
            }
        };

        /**
         * Called when error occured.
         * Reload if need custom error action
         *
         * @param error the error
         */
        protected void handleError(Throwable error) {
            notifyError(error);
        }

        public abstract void onRequestEnded(boolean success, Object result);
    }
    // ============ End of QAsyncCallback class

    private static final String LOGIN_SERVICE = "loginservice";
    private static final String DATA_SERVICE = "dataservice";
    private static final String CHART_SERVICE = "chartservice";

    private static RPC instance;

    private static DataServiceAsync dataService;
    private static LoginServiceAsync loginService;
    private static ChartServiceAsync chartService;

    /**
     * Singleton
     */
    protected RPC() {
    }

    public static RPC getRPC() {
        if(instance == null) {
            instance = new RPC();
        }
        return instance;
    }

    public void initLoginServiceAsync() {
        if(loginService == null) {
            loginService = (LoginServiceAsync) GWT.create(LoginService.class);
            ServiceDefTarget endpoint = (ServiceDefTarget) loginService;
            String moduleRelativeURL = GWT.getModuleBaseURL() + LOGIN_SERVICE;
            endpoint.setServiceEntryPoint(moduleRelativeURL);
        }
    }

    private LoginServiceAsync getLoginServiceAsync() {
        initLoginServiceAsync();
        return loginService;
    }

    public void initDataServiceAsync() {
        if(dataService == null) {
            dataService = (DataServiceAsync) GWT.create(DataService.class);
            ServiceDefTarget endpoint = (ServiceDefTarget) dataService;
            String moduleRelativeURL = GWT.getModuleBaseURL() + DATA_SERVICE;
            endpoint.setServiceEntryPoint(moduleRelativeURL);
        }
    }

    private DataServiceAsync getDataServiceAsync() {
        initDataServiceAsync();
        return dataService;
    }

    private ChartServiceAsync getChartServiceAsync() {
        if(chartService == null) {
            chartService = (ChartServiceAsync) GWT.create(ChartService.class);
            ServiceDefTarget endpoint = (ServiceDefTarget) chartService;
            String moduleRelativeURL = GWT.getModuleBaseURL() + CHART_SERVICE;
            endpoint.setServiceEntryPoint(moduleRelativeURL);
        }
        return chartService;
    }

    public void login(LoginRequestObject lro, QAsyncCallback callback) {
        notifyRequestStarted();
        getLoginServiceAsync().login(lro, callback.callback);
    }

    public void logout(QAsyncCallback callback) {
        notifyRequestStarted();
        getLoginServiceAsync().logout(callback.callback);
    }

    public void keepSessionAlive(QAsyncCallback callback) {
        notifyRequestStarted();
        getLoginServiceAsync().keepSessionAlive(callback.callback);
    }

    /**
     * This method can be used for "local search" if multipleFormSearchRequest contains only one entity constraint
     *
     * @param multipleFormSearchRequest multiple forms request
     * @param callback                  callback object
     */
    public void searchWithMultipleFormConstraints(
            AccumulatedEntitiesRequestObject multipleFormSearchRequest,
            QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().searchWithMultipleFormConstraints(
                multipleFormSearchRequest, callback.callback);
    }

    public void getEntityData(EntityIDRequestObject request,
                              QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().getEntityData(request, callback.callback);
    }

    public void getLockForEditRecord(EntityIDRequestObject request,
                                     QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().getLockForEditRecord(request, callback.callback);
    }

    public void unlockAfterEditRecord(EntityIDRequestObject request,
                                      QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().unlockAfterEditRecord(request, callback.callback);
    }

    public void updateRecord(EntityUpdateRequestObject request,
                             QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().updateRecord(request, callback.callback);
    }

    public void deleteRecord(EntityDeleteRequestObject request,
                             QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().deleteRecord(request, callback.callback);
    }

    public void createEntity(NewEntityRequestObject request,
                             QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().createEntity(request, callback.callback);
    }

    public void printForm(PrintFormRequestObject request,
                          QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().printForm(request, callback.callback);
    }

    public void printGrid(PrintGridRequestObject request,
                          QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().printGrid(request, callback.callback);
    }

    public void saveSearch(SavedSearchObject search, QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().saveSearch(search, callback.callback);
    }

    public void loadSavedSearches(QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().loadSavedSearches(callback.callback);
    }

    public void deleteSavedSearch(SavedSearchDeleteRequestObject request,
                                  QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().deleteSavedSearch(request, callback.callback);
    }

    public void insertRecord(EntityUpdateRequestObject request,
                             QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().insertRecord(request, callback.callback);
    }

    public void getMoreData(FieldDataRequest request, QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().getMoreData(request, callback.callback);
    }

    public void parseDate(ParseDateRequestObject request,
                          QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().parseDate(request, callback.callback);
    }

    public void formatDate(FormatDataRequestObject request,
                           QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().formatDate(request, callback.callback);
    }

    public void customizeGrid(CustomizeGridRequestObject request,
                              QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().customizeGrid(request, callback.callback);
    }

    public void customEntitiesFieldsSearch(
            AdhocSearchGridRecordsRequest request, QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().customEntitiesFieldsSearch(request,
                callback.callback);
    }

    public void customEntitiesFieldsReport(AdhocPrintRequestObject request,
                                           QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().customEntitiesFieldsReport(request,
                callback.callback);
    }

    public void saveReport(SaveAdhocReportRequestObject request,
                           QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().saveReport(request, callback.callback);
    }

    public void loadReports(BaseGridRequest gridRequest,
                            QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().loadReports(gridRequest, callback.callback);
    }

    public void loadReport(EntityIDRequestObject request,
                           QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().loadReport(request, callback.callback);
    }

    public void deleteReport(AdhocDeleteReportRequest reportId,
                             QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().deleteReport(reportId, callback.callback);
    }

    public void saveSettings(MetaData metaData, QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().saveSettings(metaData, callback.callback);
    }

    public void clearEntity(ClearEntityRequestObject request,
                            QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().clearEntity(request, callback.callback);
    }

    public void buildChart(ChartRequestObject request,
                           QAsyncCallback callback) {
        notifyRequestStarted();
        getChartServiceAsync().buildChart(request, callback.callback);
    }

    public void sendEmail(EmailComposeRequestObject request,
                          QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().sendEmail(request, callback.callback);
    }
    
    public void retrievePortalCss(PortalCssRequest request, 
                                  QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().retrievePortalCss(request, callback.callback);
    }
    
    public void setMessageReadFlag(MessageReadFlagRequest request,
                                   QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().setMessageReadFlag(request, callback.callback);
    }
    
    public void hasUnreadMessages(QAsyncCallback callback) {
        notifyRequestStarted();
        getDataServiceAsync().hasUnreadMessages(callback.callback);
    }
}
