package com.fr.env;

import com.fr.base.AbstractEnv;
import com.fr.base.EnvException;
import com.fr.base.FRContext;
import com.fr.base.ModifiedTable;
import com.fr.base.Parameter;
import com.fr.base.StoreProcedureParameter;
import com.fr.base.TableData;
import com.fr.base.Utils;
import com.fr.base.remote.RemoteDeziConstants;
import com.fr.data.TableDataSource;
import com.fr.data.core.DataCoreUtils;
import com.fr.data.core.db.TableProcedure;
import com.fr.data.impl.Connection;
import com.fr.data.impl.EmbeddedTableData;
import com.fr.data.impl.storeproc.ProcedureDataModel;
import com.fr.data.impl.storeproc.StoreProcedure;
import com.fr.dav.DavXMLUtils;
import com.fr.dav.UserBaseEnv;
import com.fr.design.DesignerEnvManager;
import com.fr.design.ExtraDesignClassManager;
import com.fr.design.dialog.InformationWarnPane;
import com.fr.design.file.HistoryTemplateListPane;
import com.fr.design.fun.DesignerEnvProcessor;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrameFileDealerPane;
import com.fr.design.mainframe.loghandler.DesignerLogHandler;
import com.fr.file.CacheManager;
import com.fr.file.DatasourceManager;
import com.fr.file.DatasourceManagerProvider;
import com.fr.file.filetree.FileNode;
import com.fr.general.ComparatorUtils;
import com.fr.general.FRLogger;
import com.fr.general.IOUtils;
import com.fr.general.Inter;
import com.fr.general.LogRecordTime;
import com.fr.general.LogUtils;
import com.fr.general.http.HttpClient;
import com.fr.json.JSONArray;
import com.fr.json.JSONException;
import com.fr.json.JSONObject;
import com.fr.license.function.VT4FR;
import com.fr.regist.License;
import com.fr.share.ShareConstants;
import com.fr.stable.ArrayUtils;
import com.fr.stable.EncodeConstants;
import com.fr.stable.JavaCompileInfo;
import com.fr.stable.ProductConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.SvgProvider;
import com.fr.stable.file.XMLFileManagerProvider;
import com.fr.stable.project.ProjectConstants;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLTools;
import com.fr.stable.xml.XMLableReader;
import com.fr.web.ResourceConstants;

import javax.swing.*;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.awt.*;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.NoRouteToHostException;
import java.net.Socket;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.regex.Pattern;

public class RemoteEnv extends AbstractEnv {
    private static final int TIME_OUT = 30 * 1000;
    private static final int PLAIN_SOCKET_PORT = 80;
    private static final int SSL_PORT = 443;
    private static final int MAX_PER_ROUTE = 20;
    private static final int MAX_TOTAL = 100;
    private static final String REMOTE_PLUGIN = "remote_plugin.info";
    private static final String CERT_KEY = "javax.net.ssl.trustStore";
    private static final String PWD_KEY = "javax.net.ssl.trustStorePassword";
    private static final String HTTPS_PREFIX = "https:";
    private final static String[] FILE_TYPE = {"cpt", "frm", "form", "cht", "chart"};
    private String path;
    private String user;
    private String password;
    private Clock clock = null;
    private String userID;
    private Timer timer;
    private int licNotSupport = 0;
    private boolean isRoot = false;
    private Timer logTimer = null;
    private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
    private boolean isReadTimeOut = false;
    private String buildFilePath;

    public RemoteEnv() {
        this.clock = new Clock(this);
    }

    public RemoteEnv(String path, String userName, String password) {
        this();
        this.path = path;
        this.user = userName;
        this.password = password;
    }

    /**
     * 返回env配置路径
     */
    @Override
    public String getPath() {
        return this.path;
    }

    public void setPath(String s) {
        this.path = s;
    }

    /**
     * 当前设计环境的用户名，用于远程设计
     */
    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
        clearUserID();
    }

    public String getPassword() {
        return password;
    }

    // 修复密码中包含特殊字符，无法登录的问题
    private String getEncodedPassword() {
        try {
            return URLEncoder.encode(password, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return password;
        }
    }

    public void setPassword(String password) {
        this.password = password;
        clearUserID();
    }

    public Clock getClock() {
        return this.clock;
    }

    public void setClock(Clock clock) {
        this.clock = clock;
    }

    private void clearUserID() {
        this.userID = null;
    }

    public void setThreadLocal(String value) {
        synchronized (this) {
            threadLocal.set(value);
        }

    }

    public String getThreadLocal() {
        return threadLocal.get();
    }

    /**
     * 所有与服务器端交互前,都要调用这个方法生成UserID
     */
    private String createUserID() throws EnvException {
        // 如果登录之后userID还是null
        if (this.userID == null) {
            if (!VT4FR.RemoteDesign.support() && licNotSupport <= 0) {
                licNotSupport++;
                JOptionPane.showMessageDialog(null, Inter.getLocText("FR-Lic_does_not_Support_Remote"));
            }
            throw new EnvException(Inter.getLocText("Env-Invalid_User_and_Password"));
        }

        return this.userID;
    }

    private HttpClient createHttpMethod(HashMap<String, String> para) throws EnvException, UnsupportedEncodingException {
        return createHttpMethod(para, false);
    }

    /**
     * 根据nameValuePairs,也就是参数对,生成PostMethod
     */
    private HttpClient createHttpMethod(HashMap<String, String> para, boolean isSignIn) throws EnvException, UnsupportedEncodingException {
        String methodPath = this.path;
        if (!isSignIn) {
            methodPath = methodPath + "?id=" + createUserID();
        }
        return new HttpClient(methodPath, para);
    }

    /**
     * 根据nameValuePairs,也就是参数对,生成PostMethod,不同之处在于,参数拼在path后面,不是method.addParameters
     */
    private HttpClient createHttpMethod2(HashMap<String, String> para) throws EnvException {
        String methodPath = path + '?' + "id=" + createUserID();
        return new HttpClient(methodPath, para, true);
    }


     /*
      * Read the response body.
	  * 拿出InputStream中所有的Byte,转换成ByteArrayInputStream的形式返回
      *
	  * 这样做的目的是确保method.releaseConnection
	  *
	  * TODO 但如果不做method.releaseConnection,有多大危害呢?不确定...
	  */

    /**
     * execute method之后,取返回的inputstream
     */
    private ByteArrayInputStream execute4InputStream(HttpClient client) throws Exception {
        setHttpsParas();
        try {
            int statusCode = client.getResponseCode();
            if (statusCode != HttpURLConnection.HTTP_OK) {
                //数据加载太多，屏蔽掉
                //doWithTimeOutException();
                throw new EnvException("Method failed: " + statusCode);
            }
        } catch (Exception e) {
            FRContext.getLogger().info("Connection reset ");
        }
        InputStream in = client.getResponseStream();
        if (in == null) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            Utils.copyBinaryTo(in, out);

            // 看一下传过来的byte[]是不是DesignProcessor.INVALID,如果是的话,就抛Exception
            byte[] bytes = out.toByteArray();
            // carl：格式一致传中文
            String message = new String(bytes, EncodeConstants.ENCODING_UTF_8);
            if (ComparatorUtils.equals(message, RemoteDeziConstants.NO_SUCH_RESOURCE)) {
                return null;
            } else if (ComparatorUtils.equals(message, RemoteDeziConstants.INVALID_USER)) {
                throw new EnvException(RemoteDeziConstants.INVALID_USER);
            } else if (ComparatorUtils.equals(message, RemoteDeziConstants.FILE_LOCKED)) {
                JOptionPane.showMessageDialog(null, Inter.getLocText("FR-Remote_File_is_Locked"));
                return null;
            } else if (message.startsWith(RemoteDeziConstants.RUNTIME_ERROR_PREFIX)) {
            }
            return new ByteArrayInputStream(bytes);
        } finally {
            synchronized (this) {
                in.close();
                out.close();
                client.release();
            }
        }
    }

    private void doWithTimeOutException() {
        boolean isNotNeedTip = ComparatorUtils.equals(getThreadLocal(), "HEART_BEAT") || ComparatorUtils.equals(getThreadLocal(), "LOG_MESSAGE");
        if (!isReadTimeOut && !isNotNeedTip) {
            isReadTimeOut = true;
            JOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), Inter.getLocText(new String[]{"Data", "read_time_out"}));
            isReadTimeOut = false;
        }
        FRContext.getLogger().info("Connection reset ");
    }


    /**
     * nameValuePairs,这个参数要接着this.path,拼成一个URL,否则服务器端req.getParameter是无法得到的
     *
     * @param bytes 数据
     * @return 是否成功提交
     * @throws Exception 异常
     */
    private boolean postBytes2Server(byte[] bytes, HashMap<String, String> para) throws Exception {
        HttpClient client = createHttpMethod2(para);
        client.setContent(bytes);
        execute4InputStream(client);

        return true;
    }

    /**
     * 把InputStream转成一段String
     *
     * @param in InputStream输入流
     * @return 转换后的字符串
     */
    public static String stream2String(InputStream in) {
        if (in == null) {
            return null;
        }
        BufferedReader br;
        try {
            br = new BufferedReader(new InputStreamReader(in, EncodeConstants.ENCODING_UTF_8));
        } catch (UnsupportedEncodingException e) {
            br = new BufferedReader(new InputStreamReader(in));
        }
        StringBuilder sb = new StringBuilder();
        String line;

        try {
            while ((line = br.readLine()) != null) {
                if (sb.length() > 0) {
                    sb.append('\n');
                }
                sb.append(line);
            }
        } catch (IOException e) {
            FRContext.getLogger().error(e.getMessage(), e);
        }

        return sb.toString();
    }

    /**
     * 测试连接服务器
     *
     * @return 测试连接成功返回true
     * @throws Exception 异常
     */
    public boolean testServerConnection() throws Exception {
        return testConnection(true, true, DesignerContext.getDesignerFrame());
    }

    /**
     * 测试当前配置是否正确
     *
     * @return 链接是否成功
     * @throws Exception 异常
     */
    public boolean testServerConnectionWithOutShowMessagePane() throws Exception {
        return testConnection(false, true, DesignerContext.getDesignerFrame());
    }

    /**
     * 主要用于在环境配置面板中的测试连接按钮时，不要注册进远程环境
     *
     * @param messageParentPane 弹框的依赖的面板
     * @return 是否测试连接成功
     * @throws Exception 异常
     */
    public boolean testConnectionWithOutRegisteServer(Component messageParentPane) throws Exception {
        return testConnection(true, false, messageParentPane);
    }


    private boolean testConnection(boolean needMessage, boolean isRegisteServer, Component parentComponent) throws Exception {
        extraChangeEnvPara();

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "test_server_connection");
        para.put("user", user);
        para.put("password", getEncodedPassword());

        if (path.startsWith("https") && (!DesignerEnvManager.getEnvManager().isHttps())) {
            return false;
        }

        HttpClient client = createHttpMethod(para, true);

        String res = stream2String(execute4InputStream(client));
        if (res == null) {
            if (needMessage) {
                JOptionPane.showMessageDialog(parentComponent, Inter.getLocText("Datasource-Connection_failed"));
            }
            return false;
        } else if (ComparatorUtils.equals(res, "true")) {
            if (!clock.connected && isRegisteServer) {
                //服务器中断又重新启动之后，重新向远程服务器注册
                register2Server();
            }
            return true;
        } else if (ComparatorUtils.equals(res, "invalid username or password.")) {
            JOptionPane.showMessageDialog(parentComponent,
                    Inter.getLocText(new String[]{"Datasource-Connection_failed", "Registration-User_Name", "Password", "Error"}, new String[]{",", "", "", "!"})
                    , Inter.getLocText("FR-Server-All_Error"), JOptionPane.ERROR_MESSAGE);
            return false;
        } else if (res.contains("RegistEditionException")) {
            if (needMessage) {
                JOptionPane.showMessageDialog(parentComponent, Inter.getLocText(new String[]{"Datasource-Connection_failed", "Version-does-not-support"}, new String[]{",", "!"}));
            } else {
                FRLogger.getLogger().info(Inter.getLocText(new String[]{"Datasource-Connection_failed", "Version-does-not-support"}, new String[]{",", "!"}));
            }
            return false;
        } else if (ComparatorUtils.equals(res, "war not support remote design.")) {
            if (needMessage) {
                JOptionPane.showMessageDialog(parentComponent, Inter.getLocText(new String[]{"Datasource-Connection_failed", "NS-war-remote"}, new String[]{",", "!"}));
            } else {
                FRLogger.getLogger().info(Inter.getLocText(new String[]{"Datasource-Connection_failed", "NS-war-remote"}, new String[]{",", "!"}));
            }
            return false;
        } else {
            throw new EnvException(res);
        }
    }

    private void extraChangeEnvPara() {
        //在env连接之前, 加载一下不依赖env的插件. 看看需不需要改变参数.
        DesignerEnvProcessor envProcessor = ExtraDesignClassManager.getInstance().getSingle(DesignerEnvProcessor.XML_TAG);
        if (envProcessor != null) {
            this.path = envProcessor.changeEnvPathBeforeConnect(user, password, path);
        }
    }

    private void setHttpsParas() {
        if (path.startsWith(HTTPS_PREFIX) && System.getProperty(CERT_KEY) == null) {
            DesignerEnvManager envManager = DesignerEnvManager.getEnvManager();
            System.setProperty(CERT_KEY, envManager.getCertificatePath());
            System.setProperty(PWD_KEY, envManager.getCertificatePass());
        }
    }

    private void register2Server() {
        try {
            SignIn.signIn(this);
        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
        }
    }

    /**
     * 心跳访问，用来更新当前用户的访问时间
     *
     * @throws Exception
     */
    public void heartBeatConnection() throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "heart_beat");
        para.put("user", user);

        HttpClient client = createHttpMethod(para);
        execute4InputStream(client);

        //这做法不好, 30秒刷一次, 刷新的时候会重新构建树, 构建完会把子节点都收缩起来, 效果太差.
        //为什么不保存刷新前树的伸缩状态, 因为刷新后的树和刷新前的树的结构未必是一致的.

        //服务器通知客户端更新左上角文件树面板
//        try {
//            if (ComparatorUtils.equals(stream2String(execute4InputStream(method)), "true")) {
//                DesignerFrameFileDealerPane.getInstance().refresh();
//            }
//        } catch (Exception e) {
//            FRLogger.getLogger().error(e.getMessage());
//        }
    }

    /**
     * 返回描述该运行环境的名字
     *
     * @return 描述环境名字的字符串
     */
    public String getEnvDescription() {
        return Inter.getLocText("Env-Remote_Server");
    }

    /**
     * 登录,返回userID
     */
    public void signIn() throws Exception {
        if (clock != null && clock.connected) {
            return;
        }
        String remoteVersion = getDesignerVersion();
        if (StringUtils.isBlank(remoteVersion) || ComparatorUtils.compare(remoteVersion, ProductConstants.DESIGNER_VERSION) < 0) {
            throw new Exception("version not match");
        }
        clearUserID();
        startLogTimer();
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "r_sign_in");
        para.put("user", user);
        para.put("password", getEncodedPassword());

        simulaRPC(para, true);

        //neil:调用Clock方法，10秒向服务器发送一个字节，确保没掉线
        if (clock == null) {
            Clock clock = new Clock(this);
            setClock(clock);
        }
        clock.start();

        // 远程登录的心跳访问, 防止设计器强制关闭而没有Logout
        if (timer != null) {
            timer.cancel();
        }
        timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                try {
                    RemoteEnv.this.setThreadLocal("HEART_BEAT");
                    RemoteEnv.this.heartBeatConnection();
                } catch (Exception e) {
                    FRContext.getLogger().error("Server may be disconnected.", e);
                }
            }
        }, RemoteDeziConstants.HEARTBEAT_DELAY, RemoteDeziConstants.HEARTBEAT_DELAY);
    }


    private void startLogTimer() {
        if (logTimer != null) {
            logTimer.cancel();
        }

        logTimer = new Timer();
        logTimer.schedule(new TimerTask() {
            public void run() {
                try {
                    RemoteEnv.this.setThreadLocal("LOG_MESSAGE");
                    FRContext.getCurrentEnv().printLogMessage();
                } catch (Exception e) {
                    FRLogger.getLogger().info(e.getMessage());
                }
            }
        }, 10000, 10000);
    }

    private void stopLogTimer() {
        if (logTimer != null) {
            logTimer.cancel();
            logTimer = null;
        }
    }

    /**
     * 根据userID sign out
     *
     * @return 成功签出返回true
     * @throws Exception
     */
    public boolean signOut() throws Exception {
        if (userID == null) {
            return true;
        }
        stopLogTimer();
        // richer:登出的时候就把定时发送的时钟停掉
        clock.stop();
        // richer:把轮训使用的定时器也去掉
        timer.cancel();

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "r_sign_out");
        para.put("id", userID);

        return simulaRPC(para, false
        );
    }

    protected boolean simulaRPC(HashMap<String, String> para, boolean isSignIn) throws Exception {
        HttpClient client = createHttpMethod(para, isSignIn);

        // execute method取到input stream再转成String
        String resJSON = null;
        try {
            resJSON = stream2String(execute4InputStream(client));
        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
        }

        if (resJSON == null) {
            return false;
        }
        if (resJSON.contains("RegistEditionException")) {
            JOptionPane.showMessageDialog(null, Inter.getLocText("FR-Lic_does_not_Support_Remote"));
            return false;
        }
        try {
            JSONObject jo = new JSONObject(resJSON);

            if (isSignIn) {
                if (jo.has("id")) {
                    userID = jo.getString("id");
                }
                if (jo.has("isRoot")) {
                    isRoot = jo.getBoolean("isRoot");
                }

                if (userID != null) {
                    return true;
                }
            } else {
                if (jo.has("res")) {
                    return jo.getBoolean("res");
                }
            }
            String exception = jo.getString("exp");
            if (exception != null) {
                throw new EnvException(exception);
            }
        } catch (JSONException je) {
            // 返回的resJSON不是JSON格式的,那就直接返回resJSON作为userID
            return true;
        }

        return true;
    }

    protected boolean doLockOperation(String[] filePathes, String cmd) throws Exception {
        if (filePathes == null || filePathes.length == 0) {
            return true;
        }

        JSONArray ja = new JSONArray(filePathes);
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", cmd);
        para.put("pathes", ja.toString());

        return simulaRPC(para, false);
    }

    /**
     * 取路径filePath下面文件的lock
     * <p/>
     * 处于同一个原子操作,要么拿到所有的锁,要么一个锁也没有拿到
     */
    public boolean getLock(String[] filePathes) throws Exception {
        return doLockOperation(filePathes, "design_get_lock");
    }

    /**
     * 解锁文件
     *
     * @param filePathes 文件路径
     * @return 成功解锁返回true
     * @throws Exception
     */
    public boolean releaseLock(String[] filePathes) throws Exception {
        return doLockOperation(filePathes, "design_release_lock");
    }

    /**
     * 当前Env下,tplPath目录下是否存在模板
     *
     * @param reportPath 路径
     * @return 是否存在
     */
    @Override
    public boolean isTemplateExist(String reportPath) throws Exception {
        if (reportPath == null) {
            return false;
        }

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_report_exist");
        para.put("report_path", reportPath);

        HttpClient client = createHttpMethod(para);
        InputStream input = execute4InputStream(client);

        return ComparatorUtils.equals(stream2String(input), "true");
    }

    /**
     * 解锁当前模板，用于远程设计。当远程设计某张模板 时，在解锁之前改模板处于锁定状态
     *
     * @param tplPath 路径
     * @throws Exception
     */
    @Override
    public void unlockTemplate(String tplPath) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_close_report");
        para.put(RemoteDeziConstants.TEMPLATE_PATH, tplPath);
        HttpClient client = createHttpMethod(para);

        InputStream input = execute4InputStream(client);
        String info = Utils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8);

        FRContext.getLogger().error(info);
    }

    public class Bytes2ServerOutputStream extends OutputStream {
        private ByteArrayOutputStream out = new ByteArrayOutputStream();
        private HashMap<String, String> nameValuePairs;

        public Bytes2ServerOutputStream(HashMap<String, String> nameValuePairs) {
            this.nameValuePairs = nameValuePairs;
        }

        public HashMap<String, String> getNameValuePairs() {
            return nameValuePairs;
        }

        public ByteArrayOutputStream getOut() {
            return out;
        }

        public OutputStream getZipOutputStream() throws Exception {
            return IOUtils.toZipOut(out);
        }

        /**
         * post ro Server 提交到服务器
         *
         * @return 是否提交成功
         */
        public boolean post2Server() {
            try {
                return postBytes2Server(out.toByteArray(), nameValuePairs);
            } catch (Exception e) {
                FRContext.getLogger().error(e.getMessage(), e);
                return false;
            }
        }

        /**
         * 刷新数出流，并提交
         *
         * @throws IOException
         */
        public void flush() throws IOException {
            super.flush();
            post2Server();
        }

        /**
         * 将指定字节写入输入流数组
         *
         * @param b 写入的字节
         * @throws IOException
         */
        @Override
        public void write(int b) throws IOException {
            out.write(b);

        }
    }

    /**
     * 测试数据连接是否能够正确的连接上
     *
     * @param database 数据连接
     * @return 如果能正确的连接到数据库则返回true
     * @throws Exception 无法正确连接到数据库则抛出此异常
     *                   TODO alex_ENV 个人以为,这里应该是测试所有Connection的连接,所以Connection与TableData接口的关联需要思考
     */
    @Override
    public boolean testConnection(com.fr.data.impl.Connection database) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 把database写成xml文件到out
        DavXMLUtils.writeXMLFileDatabaseConnection(database, out);

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_test_con");

        InputStream input = postBytes2ServerB(out.toByteArray(), para);

        if (input == null) {
            return false;
        }

        return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
    }

    /**
     * ben:取schema
     */
    @Override
    public String[] getTableSchema(com.fr.data.impl.Connection database) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        DavXMLUtils.writeXMLFileDatabaseConnection(database, out);
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_get_schema");
        InputStream input = postBytes2ServerB(out.toByteArray(), para);
        if (input == null) {
            return null;
        }
        return DavXMLUtils.readXMLFileSchema(input);
    }

    /**
     * b:分别取Table,View,Procedure,实际应用时更有意义
     */
    @Override
    public TableProcedure[] getTableProcedure(com.fr.data.impl.Connection database, String type, String schema) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DavXMLUtils.writeXMLFileDatabaseConnection(database, out);
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_get_tables");
        para.put("__type__", type);
        para.put("__dbschema__", schema);
        InputStream input = postBytes2ServerB(out.toByteArray(), para);
        if (input == null) {
            return new TableProcedure[0];
        }
        return DavXMLUtils.readXMLSQLTables(input);
    }

    public List getProcedures(com.fr.data.impl.Connection datasource, String[] schemas, boolean isOracle, boolean isOracleSysSpace) throws Exception {
        HashMap schemaTableProcedureMap = new HashMap();
        List sqlTableObjs = new ArrayList();
        TableProcedure[] sqlTableObj = null;
        int len = schemas.length;
        if (len > 0) {
            for (int i = 0; i < len; i++) {
                String schema = schemas[i];
                sqlTableObj = this.getTableProcedure(datasource, TableProcedure.PROCEDURE, schema);
                if (sqlTableObj == null) {
                    sqlTableObj = new TableProcedure[0];
                }
                sqlTableObjs.add(sqlTableObj);
                schemaTableProcedureMap.put(schema, sqlTableObj);
            }
        } else {
            sqlTableObj = this.getTableProcedure(datasource, TableProcedure.PROCEDURE, null);
            if (sqlTableObj == null) {
                sqlTableObj = new TableProcedure[0];
            }
            sqlTableObjs.add(sqlTableObj);
            schemaTableProcedureMap.put(null, sqlTableObj);
        }
        DataCoreUtils.putProcedureMap(datasource, schemaTableProcedureMap);
        return sqlTableObjs;
    }

    /**
     * 在当前路径下新建文件夹
     *
     * @param folderPath 文件名
     * @return 成功创建返回true
     * @throws Exception
     */
    @Override
    public boolean createFolder(String folderPath) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_create_folder");
        para.put("folder_path", folderPath);

        HttpClient client = createHttpMethod(para);
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return false;
        }

        return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
    }

    /**
     * 新建一个文件
     *
     * @param filePath ：目标文件相对路径
     * @return 成功新建返回true
     * @throws Exception
     */
    public boolean createFile(String filePath) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_create_file");
        para.put("file_path", filePath);

        HttpClient client = createHttpMethod(para);
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return false;
        }

        return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
    }

    public boolean renameFile(String newPath, String oldPath) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_rename_file");
        para.put("newPath", newPath);
        para.put("oldPath", oldPath);

        HttpClient client = createHttpMethod(para);
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return false;
        }

        return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
    }

    /**
     * 判断文件是否存在
     *
     * @param filePath ：目标文件相对路径
     * @return 文件是否存在
     * @throws Exception
     */
    @Override
    public boolean fileExists(String filePath) throws Exception {
        if (filePath == null) {
            return false;
        }

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_file_exists");
        para.put("file_path", filePath);

        HttpClient client = createHttpMethod(para);
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return false;
        }

        return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
    }

    /**
     * 判断文件是否锁住
     *
     * @param filePath 文件路径
     * @return 文件被锁住了，返回true
     * @throws Exception
     */
    public boolean fileLocked(String filePath) throws Exception {
        if (filePath == null) {
            return false;
        }

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_file_locked");
        para.put("file_path", filePath);

        HttpClient client = createHttpMethod(para);
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return false;
        }

        return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
    }


    /**
     * 注册环境，用于检测是否启动定时器，主要用于本地环境来监测远程
     *
     * @param env 用户环境
     */
    public void registerUserEnv(UserBaseEnv env) {
    }

    /**
     * 用于检测用户环境
     * ，启动定时器
     */
    public void startUserCheckTimer() {
    }


    /**
     * 停止定时器
     */
    public void stopUserCheckTimer() {
    }

    /**
     * 删除文件
     *
     * @param filePath 文件地址
     * @return 删除成功返回true
     */
    public boolean deleteFile(String filePath) {
        if (filePath == null) {
            return false;
        }
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "delete_file");
            para.put("file_path", filePath);

            HttpClient client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);

            if (input == null) {
                return false;
            }

            return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
        }
        return false;
    }

    /**
     * 远程设计器设计时，假如开了权限就不可预览了。这边放一个全局的map来开后门
     *
     * @param key   键值
     * @param value 值
     * @return 如果写入成功，返回true
     * @throws Exception
     */
    public boolean writePrivilegeMap(String key, String value) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "write_privilege_map");
        para.put("current_user", this.user);
        para.put("current_password", this.password);
        para.put("key", key);
        para.put("value", value);

        HttpClient client = createHttpMethod(para); //jim ：加上user，远程设计点击预览时传递用户角色信息
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return false;
        }

        return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
    }

    /**
     * DataSource中去除当前角色没有权限访问的数据源
     */
    public void removeNoPrivilegeConnection() {
        DatasourceManagerProvider dm = DatasourceManager.getProviderInstance();

        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fs_remote_design");
            para.put("cmd", "env_get_role");
            para.put("currentUsername", this.getUser());
            para.put("currentPwd", this.getPassword());

            HttpClient client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);
            JSONArray ja = new JSONArray(stream2String(input));
            ArrayList<String> toBeRemoveTDName = new ArrayList<String>();
            for (int i = 0; i < ja.length(); i++) {
                String toBeRemoveConnName = (String) ((JSONObject) ja.get(i)).get("name");
                dm.removeConnection(toBeRemoveConnName);
                Iterator it = dm.getTableDataNameIterator();
                while (it.hasNext()) {
                    String tdName = (String) it.next();
                    TableData td = dm.getTableData(tdName);
                    td.registerNoPrivilege(toBeRemoveTDName, toBeRemoveConnName, tdName);
                }
            }

            for (int i = 0; i < toBeRemoveTDName.size(); i++) {
                dm.removeTableData(toBeRemoveTDName.get(i));
            }
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
        }
    }

    /**
     * 列出WEB-INF目录下指定路径的文件夹与文件
     *
     * @param rootFilePath 指定目录
     * @return WEB-INF目录下指定路径的文件夹与文件
     * @throws Exception
     */
    @Override
    public FileNode[] listFile(String rootFilePath) throws Exception {
        return listFile(rootFilePath, false);
    }

    /**
     * 列出WEB-INF上层目录下指定路径的文件夹与文件
     *
     * @param rootFilePath 指定目录
     * @return WEB-INF上层目录下指定路径的文件夹与文件
     * @throws Exception
     */
    @Override
    public FileNode[] listReportPathFile(String rootFilePath) throws Exception {
        return listFile(rootFilePath, true);
    }

    private FileNode[] listFile(String rootFilePath, boolean isWebReport) throws Exception {
        FileNode[] fileNodes;

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fs_remote_design");
        para.put("cmd", "design_list_file");
        para.put("file_path", rootFilePath);
        para.put("currentUserName", this.getUser());
        para.put("currentUserId", this.createUserID());
        para.put("isWebReport", isWebReport ? "true" : "false");

        HttpClient client = createHttpMethod(para);
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return new FileNode[0];
        }

        // 远程环境下左侧目录树暂不需要打开xlsx，xls文件
        fileNodes = DavXMLUtils.readXMLFileNodes(input);
        ArrayList<FileNode> al = new ArrayList<FileNode>();
        for (int i = 0; i < fileNodes.length; i++) {
            al.add(fileNodes[i]);
        }

        FileNode[] fileNodes2 = new FileNode[al.size()];
        for (int i = 0; i < al.size(); i++) {
            fileNodes2[i] = al.get(i);
        }

        return fileNodes2;
    }

    @Override
    public FileNode[] filterFilesByKeywords(String searchKey) {
        try {
            FileNode[] fileNodes;
            HashMap<String, String> para = new HashMap<>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "search_file");
            para.put("searchKey", searchKey);
            HttpClient client = createHttpMethod(para);
            InputStream input = null;
            input = execute4InputStream(client);
            if (input == null) {
                return new FileNode[0];
            }
            fileNodes = DavXMLUtils.readXMLFileNodes(input);
            return fileNodes;
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
        }
        return new FileNode[0];
    }


    /**
     * 列出目标目录下所有cpt文件或文件夹
     *
     * @param rootFilePath 指定目录
     * @return 列出目标目录下所有cpt文件或文件夹
     * @throws Exception
     */
    public FileNode[] listCpt(String rootFilePath) throws Exception {
        return listCpt(rootFilePath, false);
    }

    /**
     * 列出目标目录下所有cpt文件或文件夹
     *
     * @param rootFilePath 指定目录
     * @param recurse      是否递归查找其子目录
     * @return 列出目标目录下所有cpt文件或文件夹
     * @throws Exception
     */
    public FileNode[] listCpt(String rootFilePath, boolean recurse) {
        List<FileNode> fileNodeList = new ArrayList<FileNode>();
        try {
            listAll(rootFilePath, fileNodeList, new String[]{"cpt"}, recurse);
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage(), e);
        }
        return fileNodeList.toArray(new FileNode[fileNodeList.size()]);
    }

    private void listAll(String rootFilePath, List<FileNode> nodeList, String[] fileTypes, boolean recurse) throws Exception {
        FileNode[] fns = listFile(rootFilePath);
        for (FileNode fileNode : fns) {
            if (isAcceptFileType(fileNode, fileTypes)) {
                nodeList.add(fileNode);
            } else if (fileNode.isDirectory()) {
                if (recurse) {
                    listAll(rootFilePath + File.separator + fileNode.getName(), nodeList, fileTypes, true);
                } else {
                    nodeList.add(fileNode);
                }
            }
        }
    }

    private boolean isAcceptFileType(FileNode fileNode, String[] fileTypes) {
        for (String fileType : fileTypes) {
            if (fileNode.isFileType(fileType)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取指定数据集的参数
     *
     * @param tableData 数据集
     * @return 数据集的参数
     * @throws Exception 获取参数失败则抛出此异常
     */
    public Parameter[] getTableDataParameters(TableData tableData) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

//        把tableData写成xml文件到out
        DavXMLUtils.writeXMLFileTableData(tableData, out);

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_td_pars");
        InputStream input = postBytes2ServerB(out.toByteArray(), para);

        if (input == null) {
            return new Parameter[0];
        }
        return DavXMLUtils.readXMLParameters(input);
    }


    /**
     * 获取存储过程中的参数
     *
     * @param storeProcedure 存储过程
     * @return 返回存储过程中的所有参数组成的数组
     * @throws Exception 如果获取参数失败则抛出此异常
     */
    public Parameter[] getStoreProcedureParameters(StoreProcedure storeProcedure) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 把tableData写成xml文件到out
        DavXMLUtils.writeXMLFileStoreProcedureAndSource(storeProcedure, out);
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_sp_pars");
        InputStream input = postBytes2ServerB(out.toByteArray(), para);

        if (input == null) {
            return new Parameter[0];
        }
        return DavXMLUtils.readXMLParameters(input);
    }

    @Override
    public EmbeddedTableData previewTableData(Object tableData, Map parameterMap, int rowCount) throws Exception {
        return previewTableData(null, tableData, parameterMap, rowCount);
    }

    /**
     * 根据指定的参数生成一个实际可预览的数据集
     *
     * @param tableData    带参数的数据集
     * @param parameterMap 参数键值对
     * @param rowCount     需要获取的行数
     * @return 实际的二维数据集
     * @throws Exception 如果生成数据失败则抛出此异常
     */
    public EmbeddedTableData previewTableData(TableDataSource dataSource, Object tableData, java.util.Map parameterMap, int rowCount) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 把tableData写成xml文件到out
        DavXMLUtils.writeXMLFileTableDataAndSource((TableData) tableData, out);

        // 把parameterMap转成JSON格式的字符串
        JSONObject jo = new JSONObject(parameterMap);
        String jsonParameter = jo.toString();
        jsonParameter = URLEncoder.encode(jsonParameter, EncodeConstants.ENCODING_UTF_8);
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_preview_td");
        para.put("pars", jsonParameter);
        para.put("rowcount", String.valueOf(rowCount));
        InputStream input = postBytes2ServerB(out.toByteArray(), para);

        if (input == null) {
            return null;
        }

        return (EmbeddedTableData) DavXMLUtils.readXMLTableData(input);
    }

    /**
     * 根据指定的参数生成一个实际可预览的数据集
     *
     * @param tableData    带参数的数据集
     * @param parameterMap 参数键值对
     * @param start        开始
     * @param end          结尾
     * @param cols         列名
     * @param colIdx       列序号
     * @return 实际的二位数据条
     * @throws Exception 异常
     */
    public Object previewTableData(Object tableData, java.util.Map parameterMap, int start, int end, String[] cols, int[] colIdx) throws Exception {
        return previewTableData(tableData, parameterMap, -1);
    }

    @Override
    public Object previewTableData(TableDataSource dataSource, Object tableData, Map parameterMap, int start, int end, String[] cols, int[] colIdx) throws Exception {
        return previewTableData(dataSource, tableData, parameterMap, -1);
    }

    /**
     * nameValuePairs,这个参数要接着this.path,拼成一个URL,否则服务器端req.getParameter是无法得到的
     *
     * @param bytes 数据
     * @param para  参数
     * @return 从服务器端得到InputStream
     * @throws Exception 异常
     */
    public InputStream postBytes2ServerB(byte[] bytes, HashMap<String, String> para) throws Exception {
        HttpClient client = createHttpMethod2(para);
        client.setContent(bytes);
        return execute4InputStream(client);
    }

    /**
     * Read XML.<br>
     * The method will be invoked when read data from XML file.<br>
     * May override the method to read the data that you saved.
     */
    public void readXML(XMLableReader reader) {
        if (reader.isChildNode()) {
            String tmpVal;
            if ("DIR".equals(reader.getTagName())) {
                if ((tmpVal = reader.getAttrAsString("path", null)) != null) {
                    this.path = tmpVal;
                }
                if ((tmpVal = reader.getAttrAsString("user", null)) != null) {
                    this.user = tmpVal;
                }
                if ((tmpVal = reader.getAttrAsString("password", null)) != null) {
                    this.password = tmpVal;
                }
            }
        }
    }

    /**
     * Write XML.<br>
     * The method will be invoked when save data to XML file.<br>
     * May override the method to save your own data.
     *
     * @param writer the PrintWriter.
     */
    public void writeXML(XMLPrintWriter writer) {
        writer.startTAG("DIR").attr("path", this.path).attr("user", this.user).attr("password", this.password).end();
    }


    public static class Clock {

        private static final long CONNECT_INTERVAL = 3000L;
        private boolean connected = false;

        private RemoteEnv remoteEnv;

        public Clock(RemoteEnv remoteEnv) {
            this.remoteEnv = remoteEnv;
        }

        /**
         * 开始连接
         */
        public void start() {
            if (connected) {
                return;
            }
            connected = true;

            new Thread(new Runnable() {
                @Override
                public void run() {
                    // richie:连续三次尝试连接都没有响应才判定为丢失连接
                    while (connected) {
                        try {
                            attemptConnect();
                        } catch (Exception ex) {
                            try {
                                attemptConnect();
                            } catch (Exception ee) {
                                try {
                                    attemptConnect();
                                } catch (Exception exc) {
                                    stop();
                                    if (exc instanceof NoRouteToHostException) {
                                        //网络问题导致的连接中断
                                        if (JOptionPane.showConfirmDialog(null, Inter.getLocText("FR-Remote_Connect2Server_Again"), UIManager.getString("OptionPane.titleText"), JOptionPane.YES_NO_OPTION)
                                                == JOptionPane.OK_OPTION) {
                                            //调用重新连接服务器的方法
                                            connectedAgain();
                                        }
                                    } else {
                                        //服务器关闭引起的连接中断
                                        if (JOptionPane.showConfirmDialog(null, Inter.getLocText("FR-Remote_Re_Connect_to_Server"), UIManager.getString("OptionPane.titleText"), JOptionPane.YES_NO_OPTION)
                                                == JOptionPane.OK_OPTION) {
                                            //调用重新连接服务器的方法
                                            connectedAgain();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }).start();
        }

        /**
         * 服务器连接中断后重新连接
         */
        private void connectedAgain() {
            try {
                if (!remoteEnv.testServerConnectionWithOutShowMessagePane()) {
                    JOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), Inter.getLocText(new String[]{"Datasource-Connection_failed", "check_communication"},
                            new String[]{",", "!"}));
                    DesignerFrameFileDealerPane.getInstance().refresh();
                    return;
                }
                String remoteVersion = remoteEnv.getDesignerVersion();
                if (StringUtils.isBlank(remoteVersion) || ComparatorUtils.compare(remoteVersion, ProductConstants.DESIGNER_VERSION) < 0) {
                    String infor = Inter.getLocText("FR-Server_Version_Tip");
                    String moreInfo = Inter.getLocText("FR-Server_Version_Tip_MoreInfo");
                    FRLogger.getLogger().log(Level.WARNING, infor);
                    new InformationWarnPane(infor, moreInfo, Inter.getLocText("FR-Designer_Tooltips")).show();
                    return;
                }
                SignIn.signIn(remoteEnv);
                HistoryTemplateListPane.getInstance().getCurrentEditingTemplate().refreshToolArea();
            } catch (Exception em) {
                FRContext.getLogger().error(em.getMessage(), em);
            }
        }

        /**
         * 停止连接
         */
        public void stop() {
            connected = false;
        }

        private void attemptConnect() throws Exception {
            Thread.sleep(CONNECT_INTERVAL);
            Pattern pattern = Pattern.compile("[/:]+");
            String[] strs = pattern.split(remoteEnv.path);

            String shost = strs[1];//host,如：192.168.100.195
            int sport = Integer.parseInt(strs[2]);//端口,如：8080

            Socket socket = new Socket(shost, sport);
            //OOBBINLINE：是否支持发送一个字节的TCP紧急数据,false表示服务器不用处理这个数据
            socket.setOOBInline(false);
            socket.sendUrgentData(0xFF);
            socket.close();
        }
    }

    /**
     * 读报表运行环境所需的配置文件,如datasource.xml, config.xml,这些文件都保存在WEB-INF/resources目录下面
     *
     * @param resourceName 配置文件的名字，如datasource.xml
     * @return 输入流
     * @throws Exception
     */
    @Override
    public InputStream readResource(String resourceName) throws Exception {
        return readBean(resourceName, ProjectConstants.RESOURCES_NAME);
    }


    /**
     * 读取路径下的svg文件
     *
     * @param path 制定路径,是基于报表目录下resource文件夹路径
     * @return 读到的文件
     */
    public File[] readPathSvgFiles(String path) {
        String cataloguePath = StableUtils.pathJoin(new String[]{CacheManager.getProviderInstance().getCacheDirectory().getPath(), SvgProvider.SERVER, path});

        //检查缓存文件保存的目录下serversvgs文件夹是否存在 ，先用来暂存服务器读过来的svg文件
        File catalogue = new File(cataloguePath);
        if (!catalogue.exists()) {
            catalogue.mkdirs();
        }

        ArrayList<File> fileArray = new ArrayList<>();
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "design_read_svgfile");
            para.put("resourcePath", path);
            para.put("current_uid", this.createUserID());
            para.put("currentUsername", this.getUser());

            HttpClient client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);
            JSONArray ja = new JSONArray(stream2String(input));
            for (int i = 0; i < ja.length(); i++) {
                JSONObject jsonObject = (JSONObject) ja.get(i);
                String svgFileName = (String) jsonObject.get("svgfileName");
                String svgfileContent = (String) jsonObject.get("svgfileContent");
                File file = new File(StableUtils.pathJoin(new String[]{cataloguePath, svgFileName}));
                InputStream in = new ByteArrayInputStream(svgfileContent.getBytes(EncodeConstants.ENCODING_UTF_8));
                FileOutputStream out = new FileOutputStream(file);
                IOUtils.copyBinaryTo(in, out);
                fileArray.add(file);
            }
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
        }
        return fileArray.toArray(new File[fileArray.size()]);
    }


    /**
     * 写svg文件
     *
     * @param svgFile svg文件
     * @return 是否写入成功
     * @throws Exception 异常
     */
    public boolean writeSvgFile(SvgProvider svgFile) throws Exception {
        testServerConnection();

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "svginit");
        para.put("cmd", "design_save_svg");
        para.put("filePath", svgFile.getFilePath());
        para.put("current_uid", this.createUserID());
        para.put("currentUsername", this.getUser());

        // 通过ByteArrayOutputStream将svg写成字节流
        Bytes2ServerOutputStream out = new Bytes2ServerOutputStream(para);
        OutputStreamWriter outWriter = new OutputStreamWriter(out, "UTF-8");
        StreamResult result = new StreamResult(outWriter);

        Source source = new DOMSource(svgFile.getSvgDocument());
        try {
            Transformer xformer = TransformerFactory.newInstance().newTransformer();
            try {
                xformer.transform(source, result);
            } catch (TransformerException ex) {
                FRContext.getLogger().error(ex.getMessage());
            }

        } catch (TransformerConfigurationException ex) {
            FRContext.getLogger().error(ex.getMessage());
            return false;
        }

        try {
            HttpClient client = createHttpMethod2(out.getNameValuePairs());
            client.setContent(out.getOut().toByteArray());
            String res = stream2String(execute4InputStream(client));
            if (StringUtils.isNotEmpty(res)) {
                JOptionPane.showMessageDialog(null, Inter.getLocText("FR-Already_exist") + res);
                return false;
            }
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
            return false;
        }

        return true;
    }

    /**
     * 写报表运行环境所需的配置文件
     *
     * @param mgr 管理各个资源文件的管理器
     * @return 写入xml成功返回true
     * @throws Exception 写入xml错误则抛出此异常
     */
    @Override
    public boolean writeResource(XMLFileManagerProvider mgr) throws Exception {
        testServerConnection();

        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_save_resource");
        para.put("resource", mgr.fileName());
        para.put("class_name", mgr.getClass().getName());
        para.put("current_uid", this.createUserID());
        para.put("currentUsername", this.getUser());

        // alex:通过ByteArrayOutputStream将mgr写成字节流
        Bytes2ServerOutputStream out = new Bytes2ServerOutputStream(para);
        XMLTools.writeOutputStreamXML(mgr, out);

        try {
            HttpClient client = createHttpMethod2(out.getNameValuePairs());
            client.setContent(out.getOut().toByteArray());
            String res = stream2String(execute4InputStream(client));
            if (StringUtils.isNotEmpty(res)) {
                JOptionPane.showMessageDialog(null, Inter.getLocText("FR-Already_exist") + res);
                return false;
            }
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
            return false;
        }

        return true;
    }

    /**
     * 读取文件
     *
     * @param beanPath 文件名
     * @param prefix   当前Env下得工程分类，如reportlets，lib等
     * @return InputStream  输入流
     */
    public InputStream readBean(String beanPath, String prefix)
            throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fs_remote_design");
        para.put("cmd", "design_open");
        para.put(RemoteDeziConstants.PREFXI, prefix);
        para.put("resource", beanPath);

        HttpClient client = createHttpMethod(para);
        //        return Utils.toZipIn(execute4InputStream(method));
        //Utils.toZipIn这边有bug，远程连接的时候datasource.xml不能读取，先还原了
        return execute4InputStream(client);
    }

    /**
     * 写文件
     *
     * @param beanPath 文件名
     * @param prefix   当前Env下得工程分类，如reportlets，lib等
     * @return OutputStream  输出流
     */
    public OutputStream writeBean(String beanPath, String prefix)
            throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fs_remote_design");
        para.put("cmd", "design_save_report");
        para.put(RemoteDeziConstants.PREFXI, prefix);
        para.put(RemoteDeziConstants.TEMPLATE_PATH, beanPath);

        return new Bytes2ServerOutputStream(para);
    }

    /**
     * 返回数据库表的列名
     *
     * @param selectedName 所选择数据库名
     * @param schema       数据库模式，用于存储过程
     * @param tableName    所选择数据库名
     */
    @Override
    public String[] getColumns(String selectedName, String schema, String tableName) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_columns");
        para.put("dsName", selectedName);
        para.put("schema", schema);
        para.put("tableName", tableName);

        HttpClient client = createHttpMethod2(para);
        InputStream input = execute4InputStream(client);

        if (input == null) {
            return null;
        }

        String colums = stream2String(input);
        if (StringUtils.isEmpty(colums)) {
            return null;
        }
        return colums.split("\\.");
    }

    /**
     * 返回模板文件路径
     */
    @Override
    public String getWebReportPath() {
        return getPath().substring(0, getPath().lastIndexOf("/"));
    }

    @Override
    public String getProcedureText(String connectionName, String databaseName) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_get_procedure_text");
        para.put("procedure_name", databaseName);
        para.put("connectionName", connectionName);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        InputStream input = postBytes2ServerB(out.toByteArray(), para);
        if (input == null) {
            return StringUtils.EMPTY;
        }
        return DavXMLUtils.readXMLProcedureText(input);
    }

    @Override
    public StoreProcedureParameter[] getStoreProcedureDeclarationParameters(String connectionName, String databaseName, String parameterDefaultValue) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_get_sp_parameters");
        para.put("__name__", databaseName);
        para.put("__default_value__", parameterDefaultValue);
        para.put("connectionName", connectionName);

        InputStream input = postBytes2ServerB(out.toByteArray(), para);
        if (input == null) {
            return new StoreProcedureParameter[0];
        }
        return DavXMLUtils.readXMLStoreProcedureParameters(input);
    }

    /**
     * 获取datasource.xml文件的修改表
     */
    public ModifiedTable getDataSourceModifiedTables(String type) {
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "get_datasource_modified_tables");
            para.put("type", type);

            HttpClient client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);
            if (input == null) {
                return new ModifiedTable();
            }
            return DavXMLUtils.readXMLModifiedTables(input);
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
        }
        return new ModifiedTable();
    }


    /**
     * 写修改表
     *
     * @param modifiedTable 修改表
     * @param type          操作类型，是数据连接还是服务器数据集
     * @return 写入成功返回true
     */
    public boolean writeDataSourceModifiedTables(ModifiedTable modifiedTable, String type) {

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 把tableData写成xml文件到out
        DavXMLUtils.writeXMLModifiedTables(modifiedTable, out);
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "update_modifytable_to_server");
            para.put("type", type);

            InputStream input = postBytes2ServerB(out.toByteArray(), para);

            if (input == null) {
                return false;
            }

            return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
        }
        return false;
    }

    public String[] getProcedureColumns(StoreProcedure storeProcedure, java.util.Map parameterMap) throws Exception {
        String[] columns;
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "list_sp");
        HttpClient client = createHttpMethod(para);
        try {
            InputStream input = execute4InputStream(client);

            if (input == null) {
                return ArrayUtils.EMPTY_STRING_ARRAY;
            }

            columns = DavXMLUtils.readXMLSPColumns(input);
            return columns;
        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
        }

        return new String[0];
    }

    ;

    public String[] getProcedureColumns(String name) throws Exception {
        String[] columns;
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "list_sp_columns_name");
        para.put("name", name);
        HttpClient client = createHttpMethod(para);
        try {
            InputStream input = execute4InputStream(client);
            if (input == null) {
                return ArrayUtils.EMPTY_STRING_ARRAY;
            }
            columns = DavXMLUtils.readXMLSPColumns(input);
            return columns;
        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
        }
        return new String[0];

    }

    /**
     * 输出日志信息
     *
     * @throws Exception
     */
    public void printLogMessage() throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "get_log_message");

        InputStream input = postBytes2ServerB(out.toByteArray(), para);
        if (input == null) {
            return;
        }
        LogRecordTime[] records = LogUtils.readXMLLogRecords(input);
        for (LogRecordTime logRecordTime : records) {
            DesignerLogHandler.getInstance().printRemoteLog(logRecordTime);
        }
    }

    public String getUserID() {
        return userID;
    }


    //TODO:

    /**
     * 预览存储过程
     *
     * @param storeProcedure 存储过程
     * @param parameterMap   参数map
     * @param rowCount       行数
     * @return 返回取到的存储过程
     */
    @Override
    public ProcedureDataModel[] previewProcedureDataModel(StoreProcedure storeProcedure, Map parameterMap, int rowCount) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 把tableData写成xml文件到out
        DavXMLUtils.writeXMLFileStoreProcedureAndSource(storeProcedure, out);

        // 把parameterMap转成JSON格式的字符串
        JSONObject jo = new JSONObject(parameterMap);
        String jsonParameter = jo.toString();

        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "list_sp");
            para.put("pars", jsonParameter);

            InputStream input = postBytes2ServerB(out.toByteArray(), para);
            if (input == null) {
                return null;
            }

            TableData[] tableDatas = DavXMLUtils.readXMLTableDataArray(input);
            if (tableDatas == null || tableDatas.length == 0) {
                return new ProcedureDataModel[0];
            }
            ProcedureDataModel[] procedureDataModels = new ProcedureDataModel[tableDatas.length];
            for (int i = 0; i < tableDatas.length; i++) {
                if (tableDatas[i] instanceof EmbeddedTableData) {
                    procedureDataModels[i] = ((EmbeddedTableData) tableDatas[i]).trans2ProcedureDataModel();
                }
            }
            return procedureDataModels;


        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
        }
        return new ProcedureDataModel[0];
    }


    public String getAppName() {
        return "WebReport";
    }

    /**
     * 是否为Oracle数据连接
     *
     * @param database 数据连接
     * @return 是返回true
     * @throws Exception
     */
    public boolean isOracle(Connection database) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DavXMLUtils.writeXMLFileDatabaseConnection(database, out);
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_get_isOracle");
        InputStream input = postBytes2ServerB(out.toByteArray(), para);
        if (input == null) {
            return true;
        }
        return DavXMLUtils.readXMLBoolean(input);
    }

    public String[] getSupportedTypes() {
        return FILE_TYPE;
    }

    /**
     * 在模板面板中是否支持增加打开所在文件夹、重命名、删除三个工具栏选项
     *
     * @return 不支持返回false
     */
    public boolean isSupportLocalFileOperate() {
        return false;
    }

    /**
     * 判断是否有文件夹权限
     *
     * @param path 路径
     * @return 有权限则返回true
     */
    public boolean hasFileFolderAllow(String path) {
        HttpClient client = null;
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fs_remote_design");
            para.put("cmd", "design_filefolder_allow");
            para.put("current_uid", this.createUserID());
            para.put(RemoteDeziConstants.TEMPLATE_PATH, path);

            client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);

            if (input == null) {
                return false;
            }
            return Boolean.valueOf(IOUtils.inputStream2String(input, EncodeConstants.ENCODING_UTF_8));
        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
            return false;
        }

    }

    /**
     * 是否是管理员身份
     *
     * @return 是则返回true
     */
    public boolean isRoot() {
        return isRoot;
    }

    /**
     * 是否为压缩包部署
     *
     * @return 是则返回true
     */
    @Override
    public boolean isPackDeploy() {
        return false;
    }

    @Override
    public String getDesignerVersion() throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_get_designer_version");
        para.put("user", user);
        para.put("password", getEncodedPassword());

        HttpClient client = createHttpMethod(para, true);
        try {
            return stream2String(execute4InputStream(client));
        } catch (Exception e) {
            FRLogger.getLogger().error(e.getMessage());
        }
        return null;
    }

    public InputStream getDataSourceInputStream(String filePath) throws Exception {
        return readBean(filePath, "datasource");
    }


    @Override
    public ArrayList getAllRole4Privilege(boolean isFS) {
        ArrayList allRoleList = new ArrayList();
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "get_all_role");
            para.put("isFS", String.valueOf(isFS));

            HttpClient client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);
            JSONArray ja = new JSONArray(stream2String(input));
            for (int i = 0; i < ja.length(); i++) {
                String roleName = (String) ((JSONObject) ja.get(i)).get("name");
                allRoleList.add(roleName);
            }
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
        }
        return allRoleList;
    }

    @Override
    public String getLicName() {
        return License.FILE_NAME;
    }

    @Override
    public void setLicName(String licName) {
        //do nth
    }


    /**
     * 获取当前env的build文件路径
     */
    public String getBuildFilePath() {
        return StringUtils.isEmpty(buildFilePath) ? ResourceConstants.BUILD_PATH : buildFilePath;
    }

    /**
     * 设置当前env的build文件路径
     */
    public void setBuildFilePath(String buildFilePath) {
        this.buildFilePath = buildFilePath;
    }

    /**
     * 编译Java源代码，方便二次开发的进行
     *
     * @param sourceText 源代码
     * @return 编译信息，有可能是成功信息，也有可能是出错或者警告信息
     */
    public JavaCompileInfo compilerSourceCode(String sourceText) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_compile_source_code");
        InputStream in = postBytes2ServerB(sourceText.getBytes(EncodeConstants.ENCODING_UTF_8), para);
        BufferedReader br = new BufferedReader(new InputStreamReader(in, EncodeConstants.ENCODING_UTF_8));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        JSONObject jo = new JSONObject(sb.toString());
        JavaCompileInfo info = new JavaCompileInfo();
        info.parseJSON(jo);
        return info;
    }


    @Override
    public String pluginServiceAction(String serviceID, String req) throws Exception {
        HashMap<String, String> para = new HashMap<String, String>();
        para.put("op", "fr_remote_design");
        para.put("cmd", "design_get_plugin_service_data");
        para.put("serviceID", serviceID);
        para.put("req", req);
        HttpClient client = createHttpMethod(para); //jim ：加上user，远程设计点击预览时传递用户角色信息
        InputStream inputStream = execute4InputStream(client);
        return IOUtils.inputStream2String(inputStream);
    }

    /**
     * 远程不启动，使用虚拟服务
     *
     * @param serviceID
     */
    @Override
    public void pluginServiceStart(String serviceID) {
    }

    @Override
    public File[] loadREUFile() throws Exception {
        File target = new File(CacheManager.getProviderInstance().getCacheDirectory(),
                ShareConstants.DIR_SHARE_CACHE);
        StableUtils.deleteFile(target);
        StableUtils.mkdirs(target);
        File cacheDir = null;
        File zip = null;
        OutputStream out = null;
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "design_read_reufile");
            para.put("current_uid", this.createUserID());
            para.put("currentUsername", this.getUser());

            HttpClient client = createHttpMethod(para);
            InputStream input = client.getResponseStream();//拿到服务端传过来的整个共享文件夹的压缩文件的文件流
            zip = new File(StableUtils.pathJoin(CacheManager.getProviderInstance().getCacheDirectory().getAbsolutePath()), "share.zip");
            cacheDir = new File(StableUtils.pathJoin(CacheManager.getProviderInstance().getCacheDirectory().getAbsolutePath()), ShareConstants.DIR_SHARE_CACHE);
            StableUtils.deleteFile(cacheDir);
            StableUtils.mkdirs(cacheDir);
            StableUtils.makesureFileExist(zip);
            out = new FileOutputStream(zip);
            IOUtils.copyBinaryTo(input, out);//放到本地缓存目录下

            IOUtils.unzip(zip, cacheDir.getAbsolutePath(), EncodeConstants.ENCODING_GBK);//先解压到临时目录
            if (cacheDir.exists() && cacheDir.isDirectory()) {
                return cacheDir.listFiles(new FilenameFilter() {
                    public boolean accept(File file, String s) {
                        return s.endsWith("reu");
                    }
                });
            }

        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
        } finally {
            if (out != null) {
                out.flush();
                out.close();
            }
            StableUtils.deleteFile(zip);
        }
        return new File[0];
    }

    @Override
    public boolean installREUFile(File reuFile) {
        if (reuFile == null) {
            return false;
        }
        File tempFile = new File(CacheManager.getProviderInstance().getCacheDirectory(), "temp_remote");
        IOUtils.unzip(reuFile, tempFile.getAbsolutePath());
        String shareXMLName = StableUtils.pathJoin(tempFile.getAbsolutePath(), ShareConstants.NAME_XML_MODULE);
        String helpXMLName = StableUtils.pathJoin(tempFile.getAbsolutePath(), ShareConstants.NAME_XML_HELP);
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "design_install_reufile");
            para.put("current_uid", this.createUserID());
            para.put("currentUsername", this.getUser());
            para.put("reuFileName", reuFile.getName());

            HttpClient client = createHttpMethod(para);
            client.setContent(IOUtils.inputStream2Bytes(new FileInputStream(new File(shareXMLName))));
            InputStream input = execute4InputStream(client);
            client.release();
            para.put("isComplete", "true");
            HttpClient client1 = createHttpMethod(para);
            client1.setContent(IOUtils.inputStream2Bytes(new FileInputStream(new File(helpXMLName))));
            InputStream input1 = execute4InputStream(client1);
            return ComparatorUtils.equals(stream2String(input), "true") && ComparatorUtils.equals(stream2String(input1), "true");
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public boolean removeREUFilesByName(String fileName) {
        if (StringUtils.isEmpty(fileName)) {
            return true;
        }
        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "fr_remote_design");
            para.put("cmd", "design_remove_reufile");
            para.put("current_uid", this.createUserID());
            para.put("currentUsername", this.getUser());
            para.put("reuFileName", fileName);

            HttpClient client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);
            return ComparatorUtils.equals(stream2String(input), "true");
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public String getSharePath() {
        return StringUtils.EMPTY;
    }

    public void doWhenServerShutDown() {

    }

    @Override
    public boolean isLocalEnv() {

        return false;
    }

    @Override
    public boolean hasPluginServiceStarted(String key) {

        return true;
    }

    @Override
    public JSONArray getPluginStatus() {

        try {
            HashMap<String, String> para = new HashMap<String, String>();
            para.put("op", "plugin");
            para.put("cmd", "get_status");
            para.put("current_uid", this.createUserID());
            para.put("currentUsername", this.getUser());

            HttpClient client = createHttpMethod(para);
            InputStream input = execute4InputStream(client);
            return new JSONArray(stream2String(input));
        } catch (Exception e) {
            return JSONArray.create();
        }
    }

    public String post(HashMap<String, String> para, boolean isSignIn) {
        try {
            HttpClient client = createHttpMethod(para, isSignIn);
            return stream2String(execute4InputStream(client));
        } catch (Exception e) {
            FRContext.getLogger().error(e.getMessage());
            return StringUtils.EMPTY;
        }
    }
}