/**
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.exoplatform.portal.webui.application;

import java.util.Random;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.exoplatform.application.gadget.Gadget;
import org.exoplatform.application.gadget.GadgetRegistryService;
import org.exoplatform.commons.utils.PropertyManager;
import org.exoplatform.portal.application.PortalRequestContext;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.model.ApplicationState;
import org.exoplatform.portal.config.model.ApplicationType;
import org.exoplatform.portal.config.model.Properties;
import org.exoplatform.portal.pom.data.ModelDataStorage;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.web.application.ApplicationMessage;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.core.UIComponent;
import org.exoplatform.webui.core.UIPortletApplication;
import org.exoplatform.webui.event.Event;
import org.exoplatform.webui.event.EventListener;
import org.exoplatform.webui.exception.MessageException;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Created by The eXo Platform SAS Author : dang.tung tungcnw@gmail.com May 06, 2008
 */
@ComponentConfig(template = "system:/groovy/portal/webui/application/UIGadget.gtmpl", events = { @EventConfig(listeners = UIGadget.SaveUserPrefActionListener.class) })
/**
 * This class represents user interface gadgets, it using UIGadget.gtmpl for
 * rendering UI in eXo. It mapped to Application model in page or container.
 */
public class UIGadget extends UIComponent {

    /** The storage id. */
    private String storageId;

    /** The storage name. */
    private String storageName;

    /** . */
    private ApplicationState<org.exoplatform.portal.pom.spi.gadget.Gadget> state;

    /** . */
    private String gadgetId;

    private Properties properties_;

    private JSONObject metadata_;

    private String url_;

    private final Logger log = LoggerFactory.getLogger(UIGadget.class);

    public static final String PREF_KEY = "_pref_gadget_";

    public static final String PREF_NO_CACHE = "_pref_no_cache_";

    public static final String PREF_DEBUG = "_pref_debug_";

    public static final String HOME_VIEW = "home";

    public static final String CANVAS_VIEW = "canvas";

    public static final String METADATA_GADGETS = "gadgets";

    public static final String METADATA_USERPREFS = "userPrefs";

    public static final String METADATA_MODULEPREFS = "modulePrefs";

    public static final String METADATA_ERROR = "error";

    public static final String RPC_RESULT = "result";

    public static final String METADATA_USERPREFS_TYPE = "dataType";

    public static final String METADATA_USERPREFS_TYPE_HIDDEN = "hidden";

    public static final String METADATA_USERPREFS_TYPE_LIST = "list";

    public String view = HOME_VIEW;

    public static String SAVE_PREF_FAIL = "UIGadget.savePrefFail";

    /**
     * Initializes a newly created <code>UIGadget</code> object
     */
    public UIGadget() {
        // That value will be overriden when it is mapped onto a data storage
        storageName = UUID.randomUUID().toString();
    }

    public String getStorageId() {
        return storageId;
    }

    public void setStorageId(String storageId) {
        this.storageId = storageId;
    }

    public String getStorageName() {
        return storageName;
    }

    public void setStorageName(String storageName) {
        this.storageName = storageName;
    }

    public String getId() {
        return storageName;
    }

    public String getStandaloneURL() {
        PortalRequestContext portalRC = Util.getPortalRequestContext();
        HttpServletRequest request = portalRC.getRequest();
        StringBuilder urlBuilder = new StringBuilder(request.getScheme());
        urlBuilder.append("://").append(request.getServerName()).append(":").append(request.getServerPort());
        urlBuilder.append(request.getContextPath()).append("/standalone/").append(storageId);

        HttpServletResponse response = portalRC.getResponse();
        return response.encodeURL(urlBuilder.toString());
    }

    public ApplicationState<org.exoplatform.portal.pom.spi.gadget.Gadget> getState() {
        return state;
    }

    public void setState(ApplicationState<org.exoplatform.portal.pom.spi.gadget.Gadget> state) {
        if (state != null) {
            try {
                DataStorage ds = getApplicationComponent(DataStorage.class);
                String gadgetId = ds.getId(state);

                //
                this.gadgetId = gadgetId;
                this.state = state;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        } else {
            this.gadgetId = null;
            this.state = null;
        }

    }

    /**
     * Gets name of gadget application
     *
     * @return the string represents name of gadget application
     */
    public String getApplicationName() {
        return gadgetId;
    }

    /**
     * Gets Properties of gadget application such as locationX, locationY in desktop page
     *
     * @return all properties of gadget application
     * @see org.exoplatform.portal.config.model.Application
     * @see org.exoplatform.portal.config.model.Properties
     */
    public Properties getProperties() {
        if (properties_ == null)
            properties_ = new Properties();
        return properties_;
    }

    /**
     * Sets Properties of gadget application such as locationX, locationY in desktop page
     *
     * @param properties Properties that is the properties of gadget application
     * @see org.exoplatform.portal.config.model.Properties
     * @see org.exoplatform.portal.config.model.Application
     */
    public void setProperties(Properties properties) {
        this.properties_ = properties;
    }

    @Deprecated
    public String getMetadata() {
        try {
            if (metadata_ == null) {
                String strMetadata = GadgetUtil.fetchGagdetRpcMetadata(getUrl());
                metadata_ = new JSONObject(strMetadata);
            }
            JSONObject obj = metadata_.getJSONArray(METADATA_GADGETS).getJSONObject(0);
            String token = GadgetUtil.createToken(this.getUrl(), new Random().nextLong());
            obj.put("secureToken", token);
            return metadata_.toString();
        } catch (JSONException e) {
            return null;
        }
    }

    /**
     * Get metadata of this gadget application This metadata contains both metadata of gadget specification (.xml file) and a
     * token from container
     *
     * @return String represent metadata of gadget
     */
    public String getRpcMetadata() {
        try {
            if (metadata_ == null) {
                metadata_ = fetchRpcMetadata();
            }
            String token = GadgetUtil.createToken(this.getUrl(), new Random().nextLong());
            metadata_.put("secureToken", token);
            return metadata_.toString();
        } catch (JSONException e) {
            return null;
        }
    }

    private JSONObject fetchRpcMetadata() {
        try {
            String gadgetUrl = getUrl();
            String strMetadata = GadgetUtil.fetchGagdetRpcMetadata(gadgetUrl);
            JSONObject metadata = new JSONArray(strMetadata).getJSONObject(0).getJSONObject(UIGadget.RPC_RESULT)
                    .getJSONObject(gadgetUrl);

            return metadata;
        } catch (JSONException e) {
            return null;
        }
    }

    /**
     * Check if content of gadget has {@code <UserPref>} ? (Content is parsed from gadget specification in .xml file)
     *
     * @return boolean
     */
    public boolean isSettingUserPref() {
        try {
            if (metadata_ != null) {
                JSONObject userPrefs = metadata_.getJSONObject(METADATA_USERPREFS);
                JSONArray names = userPrefs.names();
                int count = names.length();
                if (count > 0) {
                    for (int i = 0; i < count; i++) {
                        JSONObject o = (JSONObject) userPrefs.get(names.get(i).toString());
                        String prefType = o.optString(METADATA_USERPREFS_TYPE);
                        if (!(METADATA_USERPREFS_TYPE_HIDDEN.equalsIgnoreCase(prefType) || METADATA_USERPREFS_TYPE_LIST
                                .equalsIgnoreCase(prefType)))
                            return true;
                    }
                    return false;
                }
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean isLossData() {
        try {
            DataStorage service = getApplicationComponent(DataStorage.class);
            service.load(state, ApplicationType.GADGET);
            if (getApplication() == null) {
                throw new Exception();
            }

            if (metadata_ == null) {
                metadata_ = fetchRpcMetadata();
            }

            if (metadata_ == null || metadata_.has(METADATA_ERROR)) {
                throw new Exception();
            }
        } catch (Exception e) {
            return true;
        }
        return false;
    }

    /**
     * Gets GadgetApplication by GadgedRegistryService
     *
     * @return Gadget Application
     * @throws Exception
     */
    private Gadget getApplication() {
        try {
            GadgetRegistryService gadgetService = getApplicationComponent(GadgetRegistryService.class);
            return gadgetService.getGadget(gadgetId);
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * Gets Url of gadget application, it saved before by GadgetRegistryService
     *
     * @return url of gadget application, such as "http://www.google.com/ig/modules/horoscope.xml"
     */
    public String getUrl() {
        if (url_ == null) {
            Gadget gadget = getApplication();
            url_ = GadgetUtil.reproduceUrl(gadget.getUrl(), gadget.isLocal());
        }
        return url_;
    }

    public boolean isNoCache() {
        return PropertyManager.isDevelopping();
    }

    public boolean isDebug() {
        return PropertyManager.isDevelopping();
    }

    public String getView() {
        if (view != null)
            return view;
        return HOME_VIEW;
    }

    public void setView(String view) {
        this.view = view;
    }

    /**
     * Gets user preference of gadget application
     *
     * @return the string represents user preference of gadget application
     * @throws Exception when can't convert object to string
     */
    public String getUserPref() throws Exception {
        DataStorage service = getApplicationComponent(DataStorage.class);
        org.exoplatform.portal.pom.spi.gadget.Gadget pp = service.load(state, ApplicationType.GADGET);
        return pp != null ? pp.getUserPref() : null;
    }

    public void addUserPref(String addedUserPref) throws Exception {
        DataStorage service = getApplicationComponent(DataStorage.class);
        org.exoplatform.portal.pom.spi.gadget.Gadget gadget = new org.exoplatform.portal.pom.spi.gadget.Gadget();

        //
        gadget.addUserPref(addedUserPref);

        //
        state = service.save(state, gadget);

        // WARNING :
        // This is used to force a state save and it should not be copied else where to make things
        // convenient as this could lead to a severe performance degradation
        ModelDataStorage mds = getApplicationComponent(ModelDataStorage.class);
        mds.save();
    }

    /**
     * Initializes a newly created <code>SaveUserPrefActionListener</code> object
     */
    public static class SaveUserPrefActionListener extends EventListener<UIGadget> {
        public void execute(Event<UIGadget> event) throws Exception {
            UIGadget uiGadget = event.getSource();
            WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();

            //
            try {
                uiGadget.addUserPref(event.getRequestContext().getRequestParameter("userPref"));
                Util.getPortalRequestContext().setResponseComplete(true);
            } catch (Exception e) {
                UIPortletApplication uiPortlet = uiGadget.getAncestorOfType(UIPortletApplication.class);
                context.addUIComponentToUpdateByAjax(uiPortlet);
                context.setAttribute(UIGadget.SAVE_PREF_FAIL, true);
                throw new MessageException(new ApplicationMessage("UIDashboard.msg.ApplicationNotExisted", null,
                        ApplicationMessage.ERROR));
            }

            //
            if (uiGadget.isLossData()) {
                /*
                 * UIPortalApplication uiApp = Util.getUIPortalApplication(); uiApp.addMessage(new
                 * ApplicationMessage("UIDashboard.msg.ApplicationNotExisted", null)); PortalRequestContext pcontext =
                 * Util.getPortalRequestContext(); pcontext.addUIComponentToUpdateByAjax(uiApp.getUIPopupMessages());
                 */
                return;
            }

            // event.getRequestContext().setResponseComplete(true);
        }
    }
}
