/*
 * RiskScape™ Copyright New Zealand Institute for Earth Science Limited
 * (Earth Sciences New Zealand) is distributed for research purposes only
 * under the terms of AGPLv3.
 *
 * RiskScape™ Copyright 2026 New Zealand Institute for Earth Science
 * Limited (Earth Sciences New Zealand). All rights reserved. Source code
 * available under the AGPLv3.
 * 
 * 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 for RESEARCH PURPOSES ONLY, in the hope that it will
 * be useful for research and education initiatives.
 * 
 * If you are not a researcher, or you are a researcher who wishes to use this
 * program on terms other than AGPLv3 (including those who wish to restrict the
 * distribution of any source code created using this program), please contact:
 * https://riskscape.org.nz
 * 
 * This program is distributed 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/>.
 * 
 * By way of summary only, under the AGPLv3:
 *     • Permissions of this strongest copyleft license are conditioned
 *       on making available complete source code of licensed works and
 *       modifications, which include larger works using a licensed work,
 *       under the same license.
 *     • Copyright and license notices must be preserved.
 *     • Contributors provide an express grant of patent rights.
 *     • When a modified version is used to provide a service over a
 *       network, the complete source code of the modified version must be made
 *       available.
 */
package nz.org.riskscape.engine;

import java.net.URI;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

import org.geotools.api.filter.FilterFactory;

import nz.org.riskscape.engine.bind.BindingContext;
import nz.org.riskscape.engine.bind.ParameterBinder;
import nz.org.riskscape.engine.data.BookmarkResolvers;
import nz.org.riskscape.engine.function.FunctionFramework;
import nz.org.riskscape.engine.function.RiskscapeFunction;
import nz.org.riskscape.engine.i18n.Messages;
import nz.org.riskscape.engine.io.TupleStorage;
import nz.org.riskscape.engine.model.ModelFramework;
import nz.org.riskscape.engine.output.Format;
import nz.org.riskscape.engine.output.PipelineOutputStore;
import nz.org.riskscape.engine.output.PipelineOutputStores;
import nz.org.riskscape.engine.pipeline.PipelineExecutor;
import nz.org.riskscape.engine.pipeline.PipelineSteps;
import nz.org.riskscape.engine.plugin.ExtensionPoints;
import nz.org.riskscape.engine.plugin.Plugin;
import nz.org.riskscape.engine.resource.Resource;
import nz.org.riskscape.engine.resource.ResourceFactory;
import nz.org.riskscape.engine.types.TypeRegistry;
import nz.org.riskscape.problem.Problem;
import nz.org.riskscape.problem.ProblemSink;
import nz.org.riskscape.problem.ResultOrProblems;

/**
 * An Engine is the central part of the application that offers core services for doing data processing.
 */
public interface Engine extends IdentifiedLocator, AutoCloseable {

  /**
   * An extension of the Engine interface with extra methods that are only safe to call during engine bootstrapping
   */
  interface Writeable extends Engine {

    void registerCollection(IdentifiedCollection<?> newInstance);
    // TBD
  }

  /**
   * Null-equivalent to give to buildProject to signal you want to create an empty project
   */
  URI EMPTY_PROJECT_LOCATION = URI.create("null:null");

  BookmarkResolvers getBookmarkResolvers();

  PipelineSteps getPipelineSteps();

  /**
   * Get a filter factory.
   *
   * Note that this filter factory does not have access to Riskscape {@link RiskscapeFunction}s so will not be able to
   * create filter expressions that defer to them.
   *
   * @return filter factory
   */
  FilterFactory getFilterFactory();

  /**
   * @return a {@link TypeRegistry} used for storing meta-type information, i.e. classes of classes
   */
  TypeRegistry getTypeRegistry();

  /**
   * Factory able to convert {@link URI}s into a {@link Resource} able to access them.
   */
  ResourceFactory getResourceFactory();

  /**
   * A place for parts of the engine to use for storage
   */
  TupleStorage getTupleStorage();

  /**
   * A place for problems to go.  See interface on {@link ProblemSink} for usage.
   *
   * TODO some objects will be created with a sink, so best bet is to sit this early on in the engine's construction
   * so that it will be passed on to any child objects that reference the engine, e.g. during cli boot up.
   */
  ProblemSink getProblemSink();

  /**
   * {@link IdentifiedCollection} of {@link ParameterBinder}s to use for converting strings to model parameters.
   */
  IdentifiedCollection<ParameterBinder> getBinders();

  /**
   * {@link IdentifiedCollection} of {@link Format}s that may be used to write output files.
   */
  IdentifiedCollection<Format> getFormats();

  /**
   * @return messages that can be used for i18n
   */
  Messages getMessages();

  /**
   * @return a {@link PipelineExecutor} that can actually perform data processing for a {@link Project}
   */
  PipelineExecutor getPipelineExecutor();

  /**
   * @return version information for the Engine
   */
  BuildInfo getBuildInfo();

  /**
   * A place to record and query various diagnostics related to this {@link Engine} instance.
   */
  Diagnostics getDiagnostics();

  IdentifiedCollection<FunctionFramework> getFunctionFrameworks();

  /**
   * A generic look up method for features registered with the engine.
   * @param featureClass the API/SPI class for the feature
   * @return a {@link List}, in priority order, of the implementations of this feature
   */
  <T> List<T> getFeaturesOfType(Class<T> featureClass);

  /**
   * A collection of extension points that this engine was initialized with.  For a long time, we've dealt with
   * extensions in a pretty haphazard way, but I think we've got so many different ways and places now of 'plugging in'
   * that it's time to standardize.
   */
  ExtensionPoints getExtensionPoints();

  IdentifiedCollection<ModelFramework> getModelFrameworks();

  /**
   * @return the {@link PipelineOutputStore}s that are available to the engine
   */
  PipelineOutputStores getPipelineOutputStores();

  /**
   * Construct a project from resources at the given location.
   *
   * @param location a URI that points to the project - for now this can only be a project.ini file.
   * @param problems a place for partial-error problems related to constructing the project can go.  These can be errors
   * that stopped part of the project being built, but not enough to stop a result from being computed.
   * @return a built project, or problems describing why it couldn't be built.   Note that this result should be checked
   * for warnings, too - they aren't guaranteed to all go to the consumer.
   */
  ResultOrProblems<Project> buildProject(URI location, Consumer<Problem> problems);

  /**
   * @return an freshly minted empty project. The returned project is suitable for use as a project placeholder
   *         but shouldn't really be used for much more than that.
   */
  Project emptyProject();

  /**
   * @return The list of plugins that were used to construct and populate this engine
   */
  List<Plugin> getPlugins();

  /**
   * Test whether this engine is running with the beta plugin enabled.  This allows code that's not part of the
   * beta plugin to contribute beta features.
   */
  boolean isBetaPluginEnabled();

  /**
   * @return a {@link Path} to the user's RiskScape home directory. This is where settings files that customize the
   * user's installation of RiskScape are found.
   */
  Path getUserHomeDirectory();

  /**
   * @return a {@link Path} that temporary files can be stored for this engine instance. These are likely
   * to be cleaned up on engine shutdown.
   */
  Path getTempDirectory();

  /**
   * @return engine config (from settings.ini) matching the given key, or an empty list if no config found
   */
  default List<String> getSetting(String settingsKey) {
    return Collections.emptyList();
  }

  /**
   * Closes the engine and removes any temporary resources that it has created.
   */
  @Override
  void close();

  /**
   * @return a binding context against an empty project.
   */
  default BindingContext newBindingContext() {
    return emptyProject().newBindingContext();
  }
}
