/*
* Copyright (C) 2016-2022 ActionTech.
* based on code by MyCATCopyrightHolder Copyright (c) 2013, OpenCloudDB/MyCAT.
* License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
*/
package com.actiontech.dble.performance;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author shenzhw
 */
public class TestSelectPerf {

    private static AtomicInteger finshiedCount = new AtomicInteger();
    private static AtomicInteger failedCount = new AtomicInteger();
    private static LinkedList<StringItem> sqlTemplateItems;
    private static long minId;
    private static long maxId;
    private static int executeTimes;

    public static void addFinshed(int count) {
        finshiedCount.addAndGet(count);
    }

    public static void addFailed(int count) {
        failedCount.addAndGet(count);
    }

    private static Connection getCon(String url, String user, String passwd)
            throws SQLException {
        Connection theCon = DriverManager.getConnection(url, user, passwd);
        return theCon;
    }

    private static SelectJob createQueryJob(Connection con) {
        SelectJob job = null;
        if (sqlTemplateItems != null) {
            job = new UserTableSelectJob(con, sqlTemplateItems, executeTimes,
                    finshiedCount, failedCount);
        } else {
            job = new TravelRecordSelectJob(con, minId, maxId, executeTimes,
                    finshiedCount, failedCount);
        }
        return job;
    }

    private static void doTest(String url, String user, String password,
                               int threadCount, long minId, long maxId, int executetimes,
                               boolean outmidle) {
        final CopyOnWriteArrayList<Thread> threads = new CopyOnWriteArrayList<Thread>();
        final CopyOnWriteArrayList<SelectJob> jobs = new CopyOnWriteArrayList<SelectJob>();
        for (int i = 0; i < threadCount; i++) {
            try {
                Connection con = getCon(url, user, password);
                System.out.println("create thread " + i);
                SelectJob job = createQueryJob(con);
                Thread thread = new Thread((Runnable) job);
                threads.add(thread);
                jobs.add(job);
            } catch (Exception e) {
                System.out.println("failed create thread " + i + " err "
                        + e.toString());
            }
        }
        System.out.println("success create thread count: " + threads.size());
        for (Thread thread : threads) {
            thread.start();
        }
        System.out.println("all thread started,waiting finsh...");
        long start = System.currentTimeMillis();
        boolean notFinished = true;
        int remainThread = 0;
        while (notFinished) {
            notFinished = false;
            remainThread = 0;
            for (Thread thread : threads) {
                if (thread.isAlive()) {
                    notFinished = true;
                    remainThread++;
                }
            }
            if (remainThread < threads.size() / 2) {
                System.out
                        .println("warning many test threads finished ,qps may NOT Accurate ,alive threads:"
                                + remainThread);
            }
            if (outmidle) {
                report(jobs);
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        report(jobs);
        System.out.println("finished all,total time :"
                + (System.currentTimeMillis() - start) / 1000);
    }

    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        if (args.length < 5) {
            System.out
                    .println("input param,format: [jdbcurl] [user] [password]  [threadpoolsize]  [executetimes] [minId-maxId|sqlfile] [repeat]");
            System.out
                    .println("jdbc:mysql://localhost:8066/TESTDB test test 10  10000 1-1000000  1 ");
            System.out
                    .println("jdbc:mysql://localhost:8066/TESTDB test test 10  10000 file=mytempate.sql  1");

            return;
        }
        int threadCount = 0;
        String url = args[0];
        String user = args[1];
        String password = args[2];
        threadCount = Integer.parseInt(args[3]);

        int repeate = 1;

        executeTimes = Integer.parseInt(args[4]);
        System.out.println("execute sql times:" + executeTimes);
        String param5 = args[5];
        if (param5.contains("file=")) {
            String sqlFile = args[5].substring(args[5].indexOf('=') + 1);
            java.util.Properties pros = RandomDataValueUtil
                    .loadFromPropertyFile(sqlFile);
            String sqlTemplate = pros.getProperty("sql");
            sqlTemplateItems = RandomDataValueUtil
                    .parselRandVarTemplateString(sqlTemplate);
        } else {
            minId = Integer.parseInt((args[5].split("-"))[0]);
            maxId = Integer.parseInt((args[5].split("-"))[1]);
            System.out.println("concerent threads:" + threadCount);

            System.out.println("maxId:" + maxId);

        }
        if (args.length > 6) {
            repeate = Integer.parseInt(args[6]);
            System.out.println("repeat test times:" + repeate);
        }
        for (int i = 0; i < repeate; i++) {
            try {
                doTest(url, user, password, threadCount, minId, maxId,
                        executeTimes, repeate < 2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    public static void report(CopyOnWriteArrayList<SelectJob> jobs) {
        double tps = 0;
        long maxTTL = 0;
        long minTTL = Integer.MAX_VALUE;
        long ttlCount = 0;
        long ttlSum = 0;
        DecimalFormat df = new DecimalFormat("0.00");
        for (SelectJob job : jobs) {
            double jobTps = job.getTPS();
            if (jobTps > 0) {
                tps += job.getTPS();
                if (job.getMaxTTL() > maxTTL) {
                    maxTTL = job.getMaxTTL();
                }
                if (job.getMinTTL() < minTTL) {
                    minTTL = job.getMinTTL();
                }
                ttlCount += job.getValidTTLCount();
                ttlSum += job.getValidTTLSum();
            }
        }
        double avgSum = (ttlCount > 0) ? (ttlSum + 0.0) / ttlCount : 0;
        System.out.println("finishend:" + finshiedCount.get() + " failed:"
                + failedCount.get() + " qps:" + df.format(tps) + ",query time min:"
                + minTTL + "ms,max:" + maxTTL + "ms,avg:" + df.format(avgSum));
    }
}

interface SelectJob {

    double getTPS();

    long getValidTTLSum();

    long getValidTTLCount();

    long getMinTTL();

    long getMaxTTL();

}