package cn.myapps.runtime.common.controller;

import cn.myapps.common.controller.ResourceNotFoundException;
import cn.myapps.common.data.DataPackage;
import cn.myapps.common.model.api.ApiConfig;
import cn.myapps.designtime.api.service.ApiDesignTimeService;
import cn.myapps.designtime.common.service.DesignTimeServiceManager;
import cn.myapps.runtime.dynaform.document.ejb.Document;
import cn.myapps.runtime.dynaform.form.ejb.ValidateMessage;
import cn.myapps.runtime.macro.runner.IRunner;
import cn.myapps.runtime.macro.runner.JavaScriptFactory;
import de.odysseus.staxon.json.JsonXMLConfig;
import de.odysseus.staxon.json.JsonXMLConfigBuilder;
import de.odysseus.staxon.json.JsonXMLInputFactory;
import de.odysseus.staxon.xml.util.PrettyXMLEventWriter;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

/**
 * 自定义api模块
 */
@Component
public class CustomizeController extends AbstractRuntimeController{

    /**
     *  自定义api
     * @param response
     */
    @RequestMapping("/defapi/**")
    public void execute(HttpServletResponse response){
        try {
            String requestURI = request.getRequestURI();
            String contextPath = request.getContextPath();
            Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            String apiUrl = requestURI.replace(contextPath+"/defapi", "");
            String applicationId = request.getParameter("applicationId");
            ApiDesignTimeService service = DesignTimeServiceManager.apiDesignTimeService();
            DataPackage<ApiConfig> apiConfigs = service.query(applicationId, "", 1, Integer.MAX_VALUE);
            PathMatcher matcher = new AntPathMatcher();
            ApiConfig apiConfig = null;
            for(Iterator<ApiConfig> iterator = apiConfigs.getDatas().iterator(); iterator.hasNext();){
                ApiConfig api = iterator.next();
                String url = api.getRequestUrl().replaceAll("\\{[^\\}]+\\}", "**");
                if(matcher.match(url, apiUrl) && count(apiUrl, "/") == count(url, "/")){
                    apiConfig = api;
                    break;
                }
            }
            if(apiConfig == null){
                throw new ResourceNotFoundException("Api Not Found");
            }
            IRunner runner = JavaScriptFactory.getInstance(session.getId(), applicationId);
            runner.initBSFManager(new Document(), getParams(), getUser(), new ArrayList<ValidateMessage>());

            Object result = runner.run(apiConfig,"Api(" + apiConfig.getId() + ")." + apiConfig.getName(), apiConfig.getResponseScript());
            if(result != null){
                if("json".equals(apiConfig.getResponseType())){
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType("application/json; charset=utf-8");
                    JSONObject json = JSONObject.fromObject(result);
                    PrintWriter out = null;
                    try {
                        out = response.getWriter();
                        out.append(json.toString());
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (out != null) {
                            out.close();
                        }
                    }
                } else if("xml".equals(apiConfig.getResponseType())){
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType("application/xml; charset=utf-8");
                    JSONObject json = JSONObject.fromObject(result);
                    String xml =json2xml(json.toString());
                    String simpleName = result.getClass().getSimpleName();
                    xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><"+simpleName+">"+xml+"</"+simpleName+">";
                    PrintWriter out = null;
                    try {
                        out = response.getWriter();
                        out.append(xml);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (out != null) {
                            out.close();
                        }
                    }
                } else if("binary".equals(apiConfig.getResponseType())){
                    response.setContentType("application/x-download; charset=utf-8");
                    File file = (File) result;
                    response.setHeader("Content-Disposition", "attachment;filename=\"" + java.net.URLEncoder.encode(file.getName(), "utf-8") + "\"");
                    InputStream inputStream = new FileInputStream(file);
                    ServletOutputStream fileOutputStream = response.getOutputStream();
                    byte[] bytes = new byte[1024*1024];
                    int length = 0;
                    while((length=inputStream.read(bytes))!=-1){
                        fileOutputStream.write(bytes,0,length);
                    }
                    fileOutputStream.write(bytes, 0, length);
                    fileOutputStream.close();
                } else {
                    PrintWriter out = null;
                    try {
                        out = response.getWriter();
                        out.append((String) result);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (out != null) {
                            out.close();
                        }
                    }
                }
            }
        } catch (Exception e){
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = null;
            try {
                out = response.getWriter();
                out.append(e.getMessage());
            } catch (IOException ex) {
                ex.printStackTrace();
            } finally {
                if (out != null) {
                    out.close();
                }
            }
        }
    }

    /**
     * json转xml
     * @param json
     * @return
     */
    private static String json2xml(String json) {
        StringReader input = new StringReader(json);
        StringWriter output = new StringWriter();
        JsonXMLConfig config = new JsonXMLConfigBuilder().multiplePI(false).repairingNamespaces(false).build();
        try {
            XMLEventReader reader = new JsonXMLInputFactory(config).createXMLEventReader(input);
            XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(output);
            writer = new PrettyXMLEventWriter(writer);
            writer.add(reader);
            reader.close();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                output.close();
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(output.toString().length()>=38){//remove <?xml version="1.0" encoding="UTF-8"?>
            return output.toString().substring(39);
        }
        return output.toString();
    }

    /**
     * 统计字符出现次数
     * @param content
     * @param target
     * @return
     */
    private static int count(String content, String target) {
        int count = 0;
        int index = 0;
        try{
            while (true) {
                index = content.indexOf(target, index + 1);
                if (index > 0) {
                    count++;
                } else {
                    break;
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }
        return count;
    }
}
