/*
 * Copyright (C) 2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package vip.justlive.oxygen.web.result;

import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.IContext;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import org.thymeleaf.templateresource.ITemplateResource;
import org.thymeleaf.templateresource.StringTemplateResource;
import vip.justlive.oxygen.core.bean.Bean;
import vip.justlive.oxygen.core.util.base.ClassUtils;
import vip.justlive.oxygen.core.util.base.HttpHeaders;
import vip.justlive.oxygen.core.util.base.ResourceBundle;
import vip.justlive.oxygen.core.util.net.aio.ChannelContext;
import vip.justlive.oxygen.core.util.template.Templates;
import vip.justlive.oxygen.web.WebConfigKeys;
import vip.justlive.oxygen.web.http.Request;
import vip.justlive.oxygen.web.http.Response;
import vip.justlive.oxygen.web.router.RoutingContext;

/**
 * thymeleaf 视图处理
 *
 * @author wubo
 */
@Bean
public class ThymeleafViewResultHandler implements ResultHandler {

  private static final boolean SUPPORTED = ClassUtils.isPresent("org.thymeleaf.Thymeleaf");

  private final String suffix;
  private ThymeleafResolver resolver;

  public ThymeleafViewResultHandler() {
    this.suffix = WebConfigKeys.VIEW_SUFFIX_THYMELEAF.getValue();
    if (SUPPORTED) {
      resolver = new ThymeleafResolver();
    }
  }

  @Override
  public boolean support(Result result) {
    if (SUPPORTED && result != null && ViewResult.class.isAssignableFrom(result.getClass())) {
      ViewResult data = (ViewResult) result;
      return data.getPath() != null && data.getPath().endsWith(suffix);
    }
    return false;
  }

  @Override
  public void apply(RoutingContext ctx, Result result) {
    ViewResult data = (ViewResult) result;
    Response response = ctx.response();
    response.setContentType(HttpHeaders.TEXT_HTML);
    response.write(resolver.resolve(ctx.request(), data.getPath(), data.getData()));
  }

  static class ThymeleafResolver {

    private final TemplateEngine templateEngine;

    ThymeleafResolver() {
      this.templateEngine = new TemplateEngine();
      ResourceTemplateResolver templateResolver = new ResourceTemplateResolver(
          WebConfigKeys.VIEW_PREFIX_THYMELEAF.getValue());
      templateResolver.setCacheable(WebConfigKeys.VIEW_CACHE.castValue(boolean.class));
      this.templateEngine.setTemplateResolver(templateResolver);
    }

    String resolve(Request request, String path, Map<String, Object> data) {
      IContext context;
      Locale locale = ResourceBundle.currentThreadLocale();
      if (request.getAttribute(Request.ORIGINAL_REQUEST) instanceof ChannelContext) {
        context = new Context(locale, data);
      } else {
        context = new WebContentBuilder().build(request, locale, data);
      }
      return templateEngine.process(path, context);
    }

    static class WebContentBuilder {

      WebContext build(Request request, Locale locale, Map<String, Object> data) {
        HttpServletRequest req = (HttpServletRequest) request
            .getAttribute(Request.ORIGINAL_REQUEST);
        HttpServletResponse resp = (HttpServletResponse) request
            .getAttribute(Response.ORIGINAL_RESPONSE);
        return new WebContext(req, resp, req.getServletContext(), locale, data);
      }
    }

    /**
     * 资源模板解析
     */
    static class ResourceTemplateResolver extends StringTemplateResolver {

      private final String prefix;

      ResourceTemplateResolver(String prefix) {
        super();
        this.prefix = prefix;
        setName("Oxygen-Thymeleaf3");
      }

      @Override
      protected ITemplateResource computeTemplateResource(IEngineConfiguration configuration,
          String ownerTemplate, String template, Map<String, Object> templateResolutionAttributes) {
        if (isCacheable()) {
          return new StringTemplateResource(Templates.cachedTemplate(prefix + template));
        }
        return new StringTemplateResource(Templates.template(prefix + template));
      }
    }
  }
}