/**
 * This file is part of the Meeds project (https://meeds.io/).
 *
 * Copyright (C) 2020 - 2025 Meeds Association contact@meeds.io
 *
 * This program 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 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.gatein.pc.portlet.impl.deployment;

import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.gatein.common.io.IOTools;
import org.gatein.pc.portlet.container.ContainerPortletInvoker;
import org.gatein.pc.portlet.container.PortletContainer;
import org.gatein.pc.portlet.container.managed.LifeCycleStatus;
import org.gatein.pc.portlet.container.managed.ManagedObject;
import org.gatein.pc.portlet.container.managed.ManagedObjectEvent;
import org.gatein.pc.portlet.container.managed.ManagedObjectLifeCycleEvent;
import org.gatein.pc.portlet.container.managed.ManagedObjectRegistryEvent;
import org.gatein.pc.portlet.container.managed.ManagedObjectRegistryEventBroadcaster;
import org.gatein.pc.portlet.container.managed.ManagedObjectRegistryEventListener;
import org.gatein.pc.portlet.container.managed.ManagedPortletApplication;
import org.gatein.pc.portlet.container.managed.PortletApplicationRegistry;
import org.gatein.pc.portlet.impl.container.PortletApplicationLifeCycle;
import org.gatein.pc.portlet.impl.container.PortletContainerLifeCycle;
import org.gatein.pc.portlet.impl.deployment.staxnav.PortletApplicationMetaDataBuilder;
import org.gatein.pc.portlet.impl.metadata.PortletApplication10MetaData;

import jakarta.servlet.ServletContext;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class PortletApplicationDeployer implements PortletApplicationRegistry
{

   /** . */
   private PortletApplicationRegistry registry;

   /** . */
   protected final Log                                   log             = ExoLogger.getLogger(PortletApplicationDeployer.class);

   /** . */
   private Map<String, PortletApplicationDeployment> deploymentMap = new HashMap<String, PortletApplicationDeployment>();

   /** . */
   private final Map<String, PortletApplicationLifeCycle> applications = new HashMap<String, PortletApplicationLifeCycle>();

   /** . */
   private ContainerPortletInvoker containerPortletInvoker;

   /** . */
   private final ManagedObjectRegistryEventBroadcaster broadcaster;

   /** . */
   private boolean schemaValidated;

   /** Bridge managed object event to add/remove portlet container in portlet container invoker. */
   private final ManagedObjectRegistryEventListener bridgeToInvoker = new ManagedObjectRegistryEventListener()
   {
      public void onEvent(ManagedObjectRegistryEvent event)
      {
         if (event instanceof ManagedObjectEvent)
         {
            ManagedObjectEvent managedObjectEvent = (ManagedObjectEvent)event;
            ManagedObject managedObject = managedObjectEvent.getManagedObject();

            //
            if (managedObject instanceof PortletContainerLifeCycle)
            {
               PortletContainerLifeCycle portletContainerLifeCycle = (PortletContainerLifeCycle)managedObject;
               PortletContainer portletContainer = portletContainerLifeCycle.getPortletContainer();

               //
               if (managedObjectEvent instanceof ManagedObjectLifeCycleEvent)
               {
                  ManagedObjectLifeCycleEvent lifeCycleEvent = (ManagedObjectLifeCycleEvent)managedObjectEvent;

                  //
                  LifeCycleStatus status = lifeCycleEvent.getStatus();

                  //
                  if (status == LifeCycleStatus.STARTED)
                  {
                     containerPortletInvoker.addPortletContainer(portletContainer);
                  }
                  else
                  {
                     containerPortletInvoker.removePortletContainer(portletContainer);
                  }
               }
            }
         }
      }
   };

   public PortletApplicationDeployer(ContainerPortletInvoker containerPortletInvoker)
   {
      broadcaster = new ManagedObjectRegistryEventBroadcaster();
      broadcaster.addListener(bridgeToInvoker);

      //
      this.containerPortletInvoker = containerPortletInvoker;
   }

   public PortletApplicationDeployer()
   {
      this(null);
   }

   public boolean isSchemaValidated()
   {
      return schemaValidated;
   }

   public void setSchemaValidated(boolean schemaValidated)
   {
      this.schemaValidated = schemaValidated;
   }

   public ContainerPortletInvoker getContainerPortletInvoker()
   {
      return containerPortletInvoker;
   }

   public void setContainerPortletInvoker(ContainerPortletInvoker containerPortletInvoker)
   {
      this.containerPortletInvoker = containerPortletInvoker;
   }

/*
   public void onEvent(WebAppEvent event)
   {
      if (event instanceof WebAppLifeCycleEvent)
      {
         WebAppLifeCycleEvent lifeCycleEvent = (WebAppLifeCycleEvent)event;
         String cp = event.getWebApp().getContextPath();
         switch (lifeCycleEvent.getType())
         {
            case WebAppLifeCycleEvent.ADDED:
               log.debug("Going to install war file" + cp);
               try
               {
                  add(event.getWebApp());
                  log.debug("Installed war file" + cp);
               }
               catch (DeploymentException e)
               {
                  log.error("Could not deploy war file " + cp, e);
               }
               break;
            case WebAppLifeCycleEvent.REMOVED:
               try
               {
                  log.debug("Going to uninstall war file" + cp);
                  remove(event.getWebApp());
                  log.debug("Uninstalled war file" + cp);
               }
               catch (Throwable e)
               {
                  log.error("Uninstalled war file " + cp + " with an error", e);
               }
               break;
         }
      }
   }
*/

   public final PortletApplicationDeployment add(ServletContext webApp) throws DeploymentException
   {
      //
      PortletApplication10MetaData metaData = buildPortletApplicationMetaData(webApp);
      if (metaData != null)
      {
         PortletApplicationDeployment deployment = createPortletApplicationDeployment(webApp, metaData);
         deploymentMap.put(webApp.getContextPath(), deployment);
         deployment.install();

         //
         PortletApplicationLifeCycle portletApplicationLifeCycle = deployment.getPortletApplicationLifeCycle();
         applications.put(portletApplicationLifeCycle.getId(), portletApplicationLifeCycle);

         //
         return deployment;
      }
      else
      {
         return null;
      }
   }

   public final void remove(ServletContext webApp)
   {
      PortletApplicationDeployment deployment = null;
      try {
        deployment = deploymentMap.remove(webApp.getContextPath());
      } catch (Exception e) {
        // Nothing to do, this could happen when Wildfly is stopping
      }
      if (deployment != null)
      {
         PortletApplicationLifeCycle portletApplicationLifeCycle = deployment.getPortletApplicationLifeCycle();
         applications.remove(portletApplicationLifeCycle.getId());
         deployment.uninstall();
      }
   }

   protected PortletApplicationDeployment createPortletApplicationDeployment(ServletContext webApp, PortletApplication10MetaData metaData) {
       return new PortletApplicationDeployment(broadcaster, webApp, metaData);
   }

   protected PortletApplication10MetaData buildPortletApplicationMetaData(ServletContext webApp) throws DeploymentException
   {
      URL url;
      try
      {
         url = webApp.getResource("/WEB-INF/portlet.xml");
      }
      catch (MalformedURLException e)
      {
         throw new DeploymentException("Could not read portlet.xml deployment descriptor", e);
      }
      if (url != null)
      {
         InputStream in = null;
         try
         {
            in = IOTools.safeBufferedWrapper(url.openStream());
            PortletApplicationMetaDataBuilder builder = new PortletApplicationMetaDataBuilder();
            builder.setSchemaValidation(schemaValidated);
            return builder.build(in);
         }
         catch (Exception e)
         {
            if (e instanceof DeploymentException)
            {
               throw new DeploymentException("Could not deploy " + url + ":" + e.getMessage(), e.getCause());
            }
            else
            {
               throw new DeploymentException("Unexpected exception with portlet.xml " + url, e);
            }
         }
         finally
         {
            IOTools.safeClose(in);
         }
      }
      return null;
   }

   public Collection<? extends ManagedPortletApplication> getManagedPortletApplications()
   {
      return applications.values();
   }

   public ManagedPortletApplication getManagedPortletApplication(String id)
   {
      return applications.get(id);
   }

   public void addListener(ManagedObjectRegistryEventListener listener)
   {
      broadcaster.addListener(listener);
   }

   public void removeListener(ManagedObjectRegistryEventListener listener)
   {
      broadcaster.removeListener(listener);
   }
}
