package com.judong.chuanyiserver.config;

import com.alibaba.fastjson.JSONObject;
import com.judong.chuanyiserver.enums.ResultEnum;
import com.judong.chuanyiserver.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.openscada.opc.dcom.da.OPCSERVERSTATE;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.da.Server;

import java.util.concurrent.Executors;

/**
 * OPCserver工厂类
 */
@Slf4j
public class OpcServerDaPoolFactory {

    /**
     * 对象池
     */
    private static GenericKeyedObjectPool<String, Server> pool;
    /**
     * 对象池的参数设置
     */
    private static final GenericKeyedObjectPoolConfig config;

    /**
     * 对象池每个key最大实例化对象数
     */
    private final static int TOTAL_PERKEY = 10;
    /**
     * 对象池每个key最大的闲置对象数
     */
    private final static int IDLE_PERKEY = 3;

    static {
        config = new GenericKeyedObjectPoolConfig();
        config.setMaxTotalPerKey(TOTAL_PERKEY);
        config.setMaxIdlePerKey(IDLE_PERKEY);
        /** 支持jmx管理扩展 */
        config.setJmxEnabled(true);
        config.setJmxNamePrefix("myPoolProtocol");
        /** 保证获取有效的池对象 */
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
    }

    /**
     * 通过键创建对象，并添加的对象池中
     *
     * @param key
     * @throws Exception
     */
    public static void addServer(String key) throws Exception {
        pool.addObject(key);
    }

    /**
     * 从对象池中获取对象
     * 如果对象池中没有这个对象，则会创建这个对象
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static Server getServer(String key) throws Exception {
        if (pool == null) {
            init();
        }
        return pool.borrowObject(key);
    }

    /**
     * 归还对象
     *
     * @param key
     * @param server
     */
    public static void returnServer(String key, Server server) {
        if (pool == null) {
            init();
        }
        pool.returnObject(key, server);
    }

    /**
     * 获取池中闲置状态的实例数
     *
     * @return
     */
    public static Integer getNumIdle() {
        if (pool == null) {
            return 0;
        }
        return pool.getNumIdle();
    }

    /**
     * 获取池中在用实例的实例数
     *
     * @return
     */
    public static Integer getNumActive() {
        if (pool == null) {
            return 0;
        }
        return pool.getNumActive();
    }

    public static GenericKeyedObjectPool<String, Server> getPool() throws Exception {
        return pool;
    }

    /**
     * 关闭对象池
     */
    public synchronized static void close() {
        if (pool != null && !pool.isClosed()) {
            pool.close();
            pool = null;
        }
    }

    /**
     * 初始化对象池
     */
    public synchronized static void init() {
        if (pool != null) return;
        pool = new GenericKeyedObjectPool<String, Server>(new MyOpcServerDaPooledFactory(), config);
    }

    /**
     * 对象工厂
     */
    static class MyOpcServerDaPooledFactory extends BaseKeyedPooledObjectFactory<String, Server> {
        /**
         * 创建对象
         *
         * @param key
         * @return
         * @throws Exception
         */
        public Server create(String key) {
            JSONObject jsonObject = JSONObject.parseObject(key);
            final ConnectionInformation ci = new ConnectionInformation();
            ci.setHost(jsonObject.getString("ip")); // 安装opc电脑IP
            ci.setDomain(""); // 域，为空就行
            ci.setUser(jsonObject.getString("user")); // 电脑上自己建好的用户名
            ci.setPassword(jsonObject.getString("password")); // 用户名的密码
            // 使用MatrikonOPC Server的配置
//            ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305"); // MatrikonOPC的注册表ID，可以在“组件服务”里看到
            // 使用Stoner Simulation Server的配置
//             ci.setClsid("83BFA400-BC06-11d4-AEC4-00902760973D"); // MatrikonOPC的注册表ID，可以在“组件服务”里看到
            // 使用KepOPC Server的配置
//            ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // KEPServer的注册表ID，可以在“组件服务”里看到
            // 使用King OPC Server的配置
            ci.setClsid("20667E7E-A3EF-422F-AEDD-890D23746358"); // KEPServer的注册表ID，可以在“组件服务”里看到
            Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
            log.info("声明一个对象完成:" + server);
            try {
                server.connect();
                return server;
            } catch (Exception e) {
//                throw new CustomException(ResultEnum.SERVER_ERROR.getRespCode(), "连接不可用");
                server.dispose();
                throw new CustomException(ResultEnum.SERVER_ERROR.getRespCode(), e.getMessage());
            }
        }

        /**
         * 包装对象，将对象封装为PooledObject
         *
         * @param server
         * @return
         */
        public PooledObject<Server> wrap(Server server) {
            log.info("将对象:" + server + "包装为PooledObject");
            return new DefaultPooledObject<Server>(server);
        }

        /**
         * 验证对象是否有效
         * 在从对象池获取对象或归还对象到对象池时，会调用这个方法，判断对象是否有效，如果无效就会销毁。
         *
         * @param key
         * @param p
         * @return
         */
        public boolean validateObject(String key, PooledObject<Server> p) {
            Server server = p.getObject();
            if (null == server.getServerState()) {
                log.info("连接不可用");
                return false;
            }
            if (OPCSERVERSTATE.OPC_STATUS_RUNNING == server.getServerState().getServerState()) {
                log.info("连接可用");
                return true;
            }
            log.info("连接不可用");
            return false;
        }

        /**
         * 销毁
         * 当对象池检测到某个对象的空闲时间(idle)超时，或使用完对象归还到对象池之前被检测到对象已经无效时，就会调用这个方法销毁对象。
         *
         * @param key
         * @param p
         * @throws Exception
         */
        public void destroyObject(String key, PooledObject<Server> p)
                throws Exception {
            p.getObject().dispose();
            log.info("对象" + p.getObject() + "被摧毁了");
        }

        /**
         * 激活一个对象或者说启动对象的某些操作
         *
         * @param key
         * @param p
         * @throws Exception
         */
        public void activateObject(String key, PooledObject<Server> p)
                throws Exception {
            super.activateObject(key, p);
//            p.getObject().connect();
//            log.info("对象" + p.getObject() + "被激活了");
        }

        /**
         * 钝化一个对象
         *
         * @param key
         * @param p
         * @throws Exception
         */
        public void passivateObject(String key, PooledObject<Server> p)
                throws Exception {
            super.passivateObject(key, p);
            log.info("对象被钝化了");
        }
    }


}
