/*
 * Aipo is a groupware program developed by TOWN, Inc.
 * Copyright (C) 2004-2015 TOWN, Inc.
 * http://www.aipo.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aimluck.eip.modules.screens;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.apache.jetspeed.om.profile.Profile;
import org.apache.jetspeed.om.profile.ProfileLocator;
import org.apache.jetspeed.om.registry.Parameter;
import org.apache.jetspeed.om.registry.PortletEntry;
import org.apache.jetspeed.om.registry.base.BaseParameter;
import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.portal.Portlet;
import org.apache.jetspeed.portal.PortletConfig;
import org.apache.jetspeed.portal.PortletControl;
import org.apache.jetspeed.portal.PortletInstance;
import org.apache.jetspeed.portal.PortletSet;
import org.apache.jetspeed.services.JetspeedSecurity;
import org.apache.jetspeed.services.Profiler;
import org.apache.jetspeed.services.Registry;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.persistence.PersistenceManager;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
import org.apache.jetspeed.services.security.PortalResource;
import org.apache.jetspeed.services.statemanager.SessionState;
import org.apache.jetspeed.util.template.JetspeedLink;
import org.apache.jetspeed.util.template.JetspeedLinkFactory;
import org.apache.jetspeed.util.template.JetspeedTool;
import org.apache.turbine.util.RunData;
import org.apache.velocity.context.Context;

import com.aimluck.commons.field.ALStringField;
import com.aimluck.commons.utils.ALStringUtil;
import com.aimluck.eip.common.ALApplication;
import com.aimluck.eip.services.social.ALApplicationService;
import com.aimluck.eip.services.social.gadgets.ALUserPref;
import com.aimluck.eip.services.social.model.ALApplicationGetRequest;
import com.aimluck.eip.util.ALCommonUtils;
import com.aimluck.eip.util.ALLocalizationUtils;

/**
 * カスタマイズの一覧を処理するクラスです。
 * 
 */
public class PortletCustomizeFormScreen extends ALVelocityScreen {

  /** logger */
  private static final JetspeedLogger logger = JetspeedLogFactoryService
    .getLogger(PortletCustomizeFormScreen.class.getName());

  /**
   * 
   * @param rundata
   * @param context
   * @throws Exception
   */
  @Override
  protected void doOutput(RunData rundata, Context context) throws Exception {
    JetspeedRunData jdata = (JetspeedRunData) rundata;

    // 修正 ：タブのカスタマイズボタンが押されたときに，
    // 常にレイアウト画面を表示するように変更した．
    jdata.cleanupFromCustomization();

    if (jdata.getUser() == null) {
      return;
    }

    if (jdata.getProfile() == null) {
      return;
    }

    // read some parameters
    String editMediaType = jdata.getParameters().getString("mtype");
    String resetStack = jdata.getParameters().getString("reset");
    String peid = jdata.getParameters().getString("js_peid");

    // get the customization state for this page
    // SessionState customizationState = jdata.getPageSessionState();

    // this will be the profile we are editing
    Profile profile = null;

    // the "reset" parameter's presence signals the start of customization
    if ((resetStack != null)
      && ((resetStack.equalsIgnoreCase("on")) || (resetStack
        .equalsIgnoreCase("1")))) {
      // clear out any prior customization state
      jdata.cleanupFromCustomization();
    }

    // if we have not yet setup for customization, do so now
    if (jdata.getCustomizedProfile() == null) {
      ProfileLocator locator = (ProfileLocator) jdata.getProfile().clone();

      if (editMediaType != null) {
        locator.setMediaType(editMediaType);
      }

      // get a profile to edit
      profile = (Profile) Profiler.getProfile(locator).clone();
      jdata.setCustomizedProfile(profile);
    }

    // we are continuing an on-going customization
    else {
      // get the profile we are working on
      profile = jdata.getCustomizedProfile();
    }

    // Get js_peid parmameter.
    // If it does not exist, we will customize the root of the profile
    if (peid == null) {
      // use the id of the root set of the profile
      peid = profile.getRootSet().getID();
      jdata.setJs_peid(peid);
    }

    // find the portlet within the profile with this peid %%% isn't there a
    // better way to do this? -ggolden
    Portlet found = null;
    Stack<Portlet> sets = new Stack<Portlet>();
    sets.push(profile.getRootSet());

    while ((found == null) && (sets.size() > 0)) {
      PortletSet set = (PortletSet) sets.pop();

      if (set.getID().equals(peid)) {
        found = set;
      } else {
        Enumeration<?> en = set.getPortlets();
        while ((found == null) && en.hasMoreElements()) {
          Portlet p = (Portlet) en.nextElement();

          // unstack the controls to find the real PortletSets
          Portlet real = p;
          while (real instanceof PortletControl) {
            real = ((PortletControl) p).getPortlet();
          }

          if (real instanceof PortletSet) {
            if (real.getID().equals(peid)) {
              found = real;
            } else {
              // we'll explore this set afterwards
              sets.push(real);
            }
          } else if (p.getID().equals(peid)) {
            found = p;
          }
        }
      }
    }

    if (found != null) {
      PortalResource portalResource = new PortalResource(found);
      try {
        JetspeedLink jsLink = JetspeedLinkFactory.getInstance(rundata);
        portalResource.setOwner(jsLink.getUserName());
        JetspeedLinkFactory.putInstance(jsLink);
      } catch (Exception e) {
        logger.warn("[Customize]", e);
        portalResource.setOwner(null);
      }

      if (!JetspeedSecurity.checkPermission(
        (JetspeedUser) jdata.getUser(),
        portalResource,
        JetspeedSecurity.PERMISSION_CUSTOMIZE)) {
        logger.warn("User "
          + jdata.getUser().getUserName()
          + " has no customize permission for portlet with id "
          + peid);
        jdata
          .setMessage("Sorry, you have no customize permission for this portlet");
        return;
      }

      PortletInstance instance = PersistenceManager.getInstance(found, rundata);
      context.put("portlet_instance", PersistenceManager.getInstance(
        found,
        rundata));

      // retrieve the portlet parameters
      PortletEntry entry =
        (PortletEntry) Registry.getEntry(Registry.PORTLET, found.getName());
      // save the entry in the session
      List<Parameter> params = new ArrayList<Parameter>();
      Iterator<?> i = entry.getParameterNames();
      while (i.hasNext()) {
        String name = (String) i.next();
        Parameter param = entry.getParameter(name);
        // filter some "system" and hidden parameters
        if ((!param.isHidden()) && (name.charAt(0) != '_')) {
          // check the user role
          if (JetspeedSecurity.checkPermission(
            (JetspeedUser) rundata.getUser(),
            new PortalResource(entry, param),
            JetspeedSecurity.PERMISSION_CUSTOMIZE)) {
            // Implementation of clone() is missing so we have do it "by hand"
            Parameter clone = new BaseParameter();
            clone.setName(param.getName());
            clone.setTitle(param.getTitle());
            clone.setDescription(param.getDescription());
            clone.setType(param.getType());
            String value;
            if (instance.getAttribute(name, null) != null) {
              value = instance.getAttribute(name);
            } else if (found.getPortletConfig().getInitParameter(name) != null) {
              value = found.getPortletConfig().getInitParameter(name);
            } else {
              value = param.getValue();
            }
            clone.setValue(ALStringUtil.sanitizing(value));
            params.add(clone);
          }
        }
      }

      Collections.sort(params, new Comparator<Parameter>() {
        @Override
        public int compare(Parameter a, Parameter b) {
          Parameter p1 = a;
          Parameter p2 = b;
          return (p1.getName()).compareTo(p2.getName());
        }
      });

      // retrieve the app parameters from xml
      PortletConfig pc = found.getPortletConfig();
      if ("GadgetsTemplate".equals(pc.getName())) {
        String appId = pc.getInitParameter("aid");
        ALApplication app =
          ALApplicationService.get(new ALApplicationGetRequest().withAppId(
            appId).withIsFetchXml(true));
        List<List<Map.Entry<String, String>>> enumsList =
          new ArrayList<List<Map.Entry<String, String>>>();
        List<ALUserPref> userPrefs = app.getUserPrefs();
        Collections.sort(userPrefs, new Comparator<ALUserPref>() {
          @Override
          public int compare(ALUserPref a, ALUserPref b) {
            ALUserPref p1 = a;
            ALUserPref p2 = b;
            return (p1.getName()).compareTo(p2.getName());

          }
        });
        for (ALUserPref userPref : userPrefs) {
          String name = "pref-" + userPref.getName();
          Parameter clone = new BaseParameter();
          clone.setName(name);
          clone.setTitle(userPref.getDisplayName());
          clone.setDescription(null);
          boolean hidden = false;
          boolean list = false;
          switch (userPref.getType()) {
            case ENUM:
              List<Map.Entry<String, String>> enums = userPref.getEnums();
              enumsList.add(enums);
              clone.setType("enum");
              break;
            case LIST:
              clone.setType("list");
              list = true;
              break;
            case BOOL:
              clone.setType("boolean");
              break;
            case HIDDEN:
              clone.setType("hidden");
              hidden = true;
              break;
            default:
              clone.setType(null);
          }
          String value;
          if (instance.getAttribute(name, null) != null) {
            value = instance.getAttribute(name);
          } else if (found.getPortletConfig().getInitParameter(name) != null) {
            value = found.getPortletConfig().getInitParameter(name);
          } else {
            value = userPref.getDefault();
            if (list) {
              value = value.replace("|", ",");
            }
          }
          clone.setValue(ALStringUtil.sanitizing(value));
          if (!hidden) {
            params.add(clone);
          }
        }
        context.put("enums", enumsList);
      }

      // get the customization state for this page
      SessionState customizationState =
        ((JetspeedRunData) rundata).getPageSessionState();
      customizationState.setAttribute("customize-parameters", params);

      // populate the customizer context
      context.put("data", rundata);
      context.put("parameters", params);
      context.put("portlet", found);
      context.put("jetspeed", new JetspeedTool(rundata));
      context.put("l10n", ALLocalizationUtils.createLocalization(rundata));
      context.put("utils", new ALCommonUtils());
      context.put("js_peid", peid);

      String currentTitle =
        profile.getDocument().getEntryById(found.getID()).getTitle();
      if (currentTitle == null
        && found.getPortletConfig().getMetainfo() != null) {
        currentTitle = found.getPortletConfig().getMetainfo().getTitle();
      }
      context.put("current_title", new ALStringField(currentTitle));

      String layout_template = "portlets/html/customizer-portlet.vm";
      setTemplate(rundata, context, layout_template);
    }

    // 理由 ：レイアウト設定のカスタマイズ画面を表示するときに，
    // 最大化画面の情報がセッションに残っていた．
    // 対策 ： セッション内の js_peid を削除する．
    jdata.getUser().removeTemp("js_peid");
  }

  /**
   * @return
   */
  @Override
  protected String getPortletName() {
    return "Customize";
  }

}
