/* BDF - Copyright (c) 2010-2025 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package fr.exemole.bdfserver.servlets.instructions;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.instruction.BdfCommand;
import fr.exemole.bdfserver.api.instruction.BdfCommandParameters;
import fr.exemole.bdfserver.api.instruction.BdfCommandResult;
import fr.exemole.bdfserver.api.instruction.BdfInstruction;
import fr.exemole.bdfserver.api.instruction.BdfInstructionConstants;
import fr.exemole.bdfserver.api.instruction.OutputParameters;
import fr.exemole.bdfserver.api.interaction.Domain;
import fr.exemole.bdfserver.api.interaction.Domains;
import fr.exemole.bdfserver.api.interaction.InteractionConstants;
import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.commands.CoreBdfCommandProvider;
import fr.exemole.bdfserver.html.MessageHtmlProducer;
import fr.exemole.bdfserver.htmlproducers.CoreHtmlProducerProvider;
import fr.exemole.bdfserver.jsonproducers.CoreJsonProducerProvider;
import fr.exemole.bdfserver.tools.BdfServerUtils;
import fr.exemole.bdfserver.tools.instruction.BdfCommandParametersBuilder;
import fr.exemole.bdfserver.tools.instruction.BdfInstructionUtils;
import fr.exemole.bdfserver.tools.instruction.DefaultBdfParameters;
import fr.exemole.bdfserver.tools.instruction.OutputParametersBuilder;
import net.mapeadores.util.html.HtmlProducer;
import net.mapeadores.util.io.StreamProducer;
import net.mapeadores.util.json.JsonProducer;
import net.mapeadores.util.localisation.MessageLocalisation;
import net.mapeadores.util.logging.CommandMessage;
import net.mapeadores.util.logging.ErrorMessageException;
import net.mapeadores.util.logging.LogUtils;
import net.mapeadores.util.request.OutputInfo;
import net.mapeadores.util.request.RequestConstants;
import net.mapeadores.util.request.RequestMap;
import net.mapeadores.util.request.RequestUtils;
import net.mapeadores.util.servlets.ResponseHandler;
import net.mapeadores.util.servlets.handlers.HtmlResponseHandler;
import net.mapeadores.util.servlets.handlers.JsonResponseHandler;
import net.mapeadores.util.servlets.handlers.RedirectResponseHandler;
import net.mapeadores.util.servlets.handlers.ResponseHandlerFactory;
import net.mapeadores.util.servlets.handlers.StreamResponseHandler;


/**
 *
 * @author Vincent Calame
 */
public class DomainInstruction implements BdfInstruction {

    private final BdfServer bdfServer;
    private final RequestMap requestMap;
    private final BdfCommand bdfCommand;
    private final boolean noCommand;
    private final Domain domain;
    private final CommandMessage initErrorMessage;
    private BdfUser bdfUser;

    public DomainInstruction(BdfServer bdfServer, RequestMap requestMap, Domain domain) {
        this.bdfServer = bdfServer;
        this.requestMap = requestMap;
        this.domain = domain;
        String cmd = requestMap.getParameter(RequestConstants.COMMAND_PARAMETER);
        if (cmd != null) {
            this.noCommand = false;
            BdfCommandParameters bdfCommandParameters = BdfCommandParametersBuilder.build(cmd, domain, bdfServer, requestMap);
            BdfCommand bdfCmd = BdfServerUtils.getExtensionBdfCommand(bdfCommandParameters);
            if (bdfCmd == null) {
                bdfCmd = CoreBdfCommandProvider.UNIQUE_INSTANCE.getBdfCommand(bdfCommandParameters);
            }
            if (bdfCmd != null) {
                this.bdfCommand = bdfCmd;
                this.initErrorMessage = null;
            } else {
                this.bdfCommand = null;
                this.initErrorMessage = LogUtils.error("_ error.unknown.bdfcommand", cmd);
            }
        } else {
            this.noCommand = true;
            this.bdfCommand = null;
            this.initErrorMessage = null;
        }
    }

    @Override
    public short getBdfUserNeed() {
        return BdfInstructionConstants.MANDATORY_BDFUSER;
    }

    @Override
    public ResponseHandler runInstruction(BdfUser bdfUser) {
        ResponseHandler responseHandler = run(bdfUser);
        if (responseHandler instanceof JsonResponseHandler) {
            ((JsonResponseHandler) responseHandler).giveAccess(requestMap);
        }
        return responseHandler;
    }

    private ResponseHandler run(BdfUser bdfUser) {
        this.bdfUser = bdfUser;
        if (noCommand) {
            return getResponseHandler(null);
        }
        if (initErrorMessage != null) {
            return getMessageHandler(initErrorMessage);
        }
        if (requestMap.isTrue(RequestConstants.TEST_PARAMETER)) {
            return getMessageHandler(bdfCommand.testCommand(bdfUser));
        }
        BdfCommandResult bdfCommandResult;
        if (bdfCommand.needSynchronisation()) {
            synchronized (bdfServer) {
                bdfCommandResult = bdfCommand.doCommand(bdfUser);
            }
        } else {
            bdfCommandResult = bdfCommand.doCommand(bdfUser);
        }
        return getResponseHandler(bdfCommandResult);
    }

    private ResponseHandler getResponseHandler(BdfCommandResult bdfCommandResult) {
        String redirect = requestMap.getParameter(RequestConstants.REDIRECT_PARAMETER);
        if ((redirect != null) && (redirect.length() > 0)) {
            return new RedirectResponseHandler(redirect);
        }
        OutputInfo outputInfo = OutputInfo.buildFromRequest(requestMap, getDefaultPage(domain));
        switch (outputInfo.getType()) {
            case OutputInfo.JSON_TYPE:
                return getJsonResponseHandler(outputInfo, bdfCommandResult);
            case OutputInfo.PAGE_TYPE:
                return getPageResponseHandler(outputInfo, bdfCommandResult);
            case OutputInfo.STREAM_TYPE:
                return getStreamResponseHandler(outputInfo, bdfCommandResult);
            default:
                return getHtmlMessageHandler(bdfCommandResult, LogUtils.error("_ error.empty.page"));
        }
    }

    private ResponseHandler getJsonResponseHandler(OutputInfo outputInfo, BdfCommandResult bdfCommandResult) {
        JsonProducer jsonProducer = getJsonProducer(outputInfo, bdfCommandResult);
        return JsonResponseHandler.build(jsonProducer, requestMap);
    }

    private ResponseHandler getPageResponseHandler(OutputInfo outputInfo, BdfCommandResult bdfCommandResult) {
        if (!outputInfo.hasOutput()) {
            return getHtmlMessageHandler(bdfCommandResult, LogUtils.error("_ error.empty.page"));
        }
        String page = outputInfo.getOutput();
        if ((bdfCommandResult != null) && (bdfCommandResult.isError())) {
            String errorPage = requestMap.getParameter(InteractionConstants.PAGE_ERROR_PARAMNAME);
            if (errorPage != null) {
                page = errorPage;
            }
        }
        OutputParameters outputParameters = buildOutputParameters(page, bdfCommandResult);
        try {
            HtmlProducer htmlProducer = BdfServerUtils.getExtensionHtmlProducer(outputParameters);
            if (htmlProducer == null) {
                htmlProducer = CoreHtmlProducerProvider.UNIQUE_INSTANCE.getHtmlProducer(outputParameters);
            }
            if (htmlProducer == null) {
                return getHtmlMessageHandler(bdfCommandResult, LogUtils.error("_ error.unknown.page", page));
            }
            return HtmlResponseHandler.init(htmlProducer);
        } catch (ErrorMessageException eme) {
            return getHtmlMessageHandler(bdfCommandResult, eme.getErrorMessage());
        }
    }

    private ResponseHandler getStreamResponseHandler(OutputInfo outputInfo, BdfCommandResult bdfCommandResult) {
        if ((bdfCommandResult != null) && (bdfCommandResult.isError())) {
            return getHtmlMessageHandler(null, bdfCommandResult.getCommandMessage());
        }
        if (!outputInfo.hasOutput()) {
            return getHtmlMessageHandler(bdfCommandResult, LogUtils.error("_ error.empty.mandatoryparameter", RequestConstants.STREAM_PARAMETER));
        }
        String output = outputInfo.getOutput();
        OutputParameters outputParameters = buildOutputParameters(output, bdfCommandResult);
        try {
            StreamProducer streamProducer = BdfServerUtils.getExtensionStreamProducer(outputParameters);
            if (streamProducer == null) {
                return getHtmlMessageHandler(bdfCommandResult, LogUtils.error("_ error.unknown.parametervalue", RequestConstants.STREAM_PARAMETER, output));
            }
            return StreamResponseHandler.init(streamProducer);
        } catch (ErrorMessageException eme) {
            return getHtmlMessageHandler(bdfCommandResult, eme.getErrorMessage());
        }
    }

    private JsonProducer getJsonProducer(OutputInfo outputInfo, BdfCommandResult bdfCommandResult) {
        if ((bdfCommandResult != null) && (bdfCommandResult.isError())) {
            return getDefaultJsonProducer(null, bdfCommandResult.getCommandMessage());
        }
        String json;
        if (outputInfo.hasOutput()) {
            json = outputInfo.getOutput();
        } else {
            json = InteractionConstants.DEFAULT_JSON;
        }
        if (json.equals(InteractionConstants.DEFAULT_JSON)) {
            if (bdfCommandResult != null) {
                return getDefaultJsonProducer(bdfCommandResult, null);
            } else {
                return getDefaultJsonProducer(null, LogUtils.error("_ error.exception.unabletodefinejson"));
            }
        } else {
            OutputParameters outputParameters = buildOutputParameters(json, bdfCommandResult);
            try {
                JsonProducer jsonProducer = BdfServerUtils.getExtensionJsonProducer(outputParameters);
                if (jsonProducer == null) {
                    jsonProducer = CoreJsonProducerProvider.UNIQUE_INSTANCE.getJsonProducer(outputParameters);
                }
                if (jsonProducer == null) {
                    return getDefaultJsonProducer(bdfCommandResult, LogUtils.error("_ error.unknown.json", json));
                }
                return jsonProducer;
            } catch (ErrorMessageException eme) {
                return getDefaultJsonProducer(bdfCommandResult, eme.getErrorMessage());
            }
        }
    }

    private JsonProducer getDefaultJsonProducer(BdfCommandResult bdfCommandResult, CommandMessage errorMessage) {
        MessageLocalisation messageLocalisation = bdfServer.getL10nManager().getMessageLocalisation(bdfUser);
        return BdfInstructionUtils.getDefaultJsonProducer(messageLocalisation, bdfCommandResult, errorMessage);
    }

    private ResponseHandler getMessageHandler(CommandMessage commandMessage) {
        if (RequestUtils.isJsonDefined(requestMap)) {
            MessageLocalisation messageLocalisation = bdfServer.getL10nManager().getMessageLocalisation(bdfUser);
            return ResponseHandlerFactory.getJsonInstance(messageLocalisation, requestMap, commandMessage);
        } else {
            return getHtmlMessageHandler(null, commandMessage);
        }
    }

    private ResponseHandler getHtmlMessageHandler(BdfCommandResult bdfCommandResult, CommandMessage commandMessage) {
        return HtmlResponseHandler.init(MessageHtmlProducer.init(new DefaultBdfParameters(bdfServer, bdfUser))
                .addMessages(commandMessage)
                .setBdfCommandResult(bdfCommandResult));
    }

    private OutputParameters buildOutputParameters(String output, BdfCommandResult bdfCommandResult) {
        return OutputParametersBuilder.build(output, domain, bdfServer, requestMap, bdfUser, bdfCommandResult);
    }

    private static String getDefaultPage(Domain domain) {
        if (domain.getSecondPart().isEmpty()) {
            return null;
        }
        switch (domain.getFirstPart()) {
            case Domains.ADMINISTRATION:
            case Domains.ALBUM:
            case Domains.CONFIGURATION:
            case Domains.CORPUS:
            case Domains.MAILING:
            case Domains.EXPORTATION:
            case Domains.EDITION:
            case Domains.SELECTION:
            case Domains.SPHERE:
            case Domains.THESAURUS:
            case Domains.MAIN:
            case Domains.MISC:
            case Domains.ADDENDA:
            case Domains.IMPORTATION:
            case Domains.PIOCHE:
                return domain.getSecondPart();
            default:
                return null;
        }
    }

}
