/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.catalina.tribes.demos;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;

import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.ManagedChannel;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.group.Response;
import org.apache.catalina.tribes.group.RpcCallback;
import org.apache.catalina.tribes.group.RpcChannel;

public class EchoRpcTest implements RpcCallback, Runnable {

    Channel channel;
    int count;
    String message;
    long pause;
    RpcChannel rpc;
    int options;
    long timeout;
    String name;

    public EchoRpcTest(Channel channel, String name, int count, String message, long pause, int options, long timeout) {
        this.channel = channel;
        this.count = count;
        this.message = message;
        this.pause = pause;
        this.options = options;
        this.rpc = new RpcChannel(name.getBytes(StandardCharsets.UTF_8), channel, this);
        this.timeout = timeout;
        this.name = name;
    }

    /**
     * If the reply has already been sent to the requesting thread, the rpc callback can handle any data that comes in
     * after the fact.
     *
     * @param msg    Serializable
     * @param sender Member
     */
    @Override
    public void leftOver(Serializable msg, Member sender) {
        System.out.println("Received a left over message from [" + sender.getName() + "] with data [" + msg + "]");
    }

    /**
     * @param msg    Serializable
     * @param sender Member
     *
     * @return Serializable - null if no reply should be sent
     */
    @Override
    public Serializable replyRequest(Serializable msg, Member sender) {
        System.out.println("Received a reply request message from [" + sender.getName() + "] with data [" + msg + "]");
        return "Reply(" + name + "):" + msg;
    }

    @Override
    public void run() {
        long counter = 0;
        while (counter < count) {
            String msg = message + " cnt=" + (++counter);
            try {
                System.out.println("Sending [" + msg + "]");
                long start = System.currentTimeMillis();
                Response[] resp = rpc.send(channel.getMembers(), msg, options, Channel.SEND_OPTIONS_DEFAULT, timeout);
                System.out.println("Send of [" + msg + "] completed. Nr of responses=" + resp.length + " Time:" +
                        (System.currentTimeMillis() - start) + " ms.");
                for (Response response : resp) {
                    System.out.println("Received a response message from [" + response.getSource().getName() +
                            "] with data [" + response.getMessage() + "]");
                }
                Thread.sleep(pause);
            } catch (Exception x) {
                // Ignore
            }
        }
    }

    public static void usage() {
        System.out.println("Tribes RPC tester.");
        System.out.println("Usage:\n\t" + "java EchoRpcTest [options]\n\t" + "Options:\n\t\t" +
                "[-mode all|first|majority]  \n\t\t" + "[-debug]  \n\t\t" + "[-count messagecount]  \n\t\t" +
                "[-timeout timeoutinms]  \n\t\t" + "[-stats statinterval]  \n\t\t" +
                "[-pause nrofsecondstopausebetweensends]  \n\t\t" + "[-message message]  \n\t\t" +
                "[-name rpcname]  \n\t\t" + "[-break (halts execution on exception)]\n" + "\tChannel options:" +
                ChannelCreator.usage() + "\n\n" + "Example:\n\t" + "java EchoRpcTest -port 4004\n\t" +
                "java EchoRpcTest -bind 192.168.0.45 -port 4005\n\t" +
                "java EchoRpcTest -bind 192.168.0.45 -port 4005 -mbind 192.168.0.45 -count 100 -stats 10\n");
    }

    public static void main(String[] args) throws Exception {
        long pause = 3000;
        int count = 1000000;
        int stats = 10000;
        String name = "EchoRpcId";
        int options = RpcChannel.ALL_REPLY;
        long timeout = 15000;
        String message = "EchoRpcMessage";
        if (args.length == 0) {
            usage();
            System.exit(1);
        }
        for (int i = 0; i < args.length; i++) {
            if ("-threads".equals(args[i])) {
                // Not used
            } else if ("-count".equals(args[i])) {
                count = Integer.parseInt(args[++i]);
                System.out.println("Sending " + count + " messages.");
            } else if ("-pause".equals(args[i])) {
                pause = Long.parseLong(args[++i]) * 1000;
            } else if ("-break".equals(args[i])) {
                // Not used
            } else if ("-stats".equals(args[i])) {
                stats = Integer.parseInt(args[++i]);
                System.out.println("Stats every " + stats + " message");
            } else if ("-timeout".equals(args[i])) {
                timeout = Long.parseLong(args[++i]);
            } else if ("-message".equals(args[i])) {
                message = args[++i];
            } else if ("-name".equals(args[i])) {
                name = args[++i];
            } else if ("-mode".equals(args[i])) {
                if ("all".equals(args[++i])) {
                    options = RpcChannel.ALL_REPLY;
                } else if ("first".equals(args[i])) {
                    options = RpcChannel.FIRST_REPLY;
                } else if ("majority".equals(args[i])) {
                    options = RpcChannel.MAJORITY_REPLY;
                }
            } else if ("-debug".equals(args[i])) {
                // Not used
            } else if ("-help".equals(args[i])) {
                usage();
                System.exit(1);
            }
        }


        ManagedChannel channel = (ManagedChannel) ChannelCreator.createChannel(args);
        EchoRpcTest test = new EchoRpcTest(channel, name, count, message, pause, options, timeout);
        channel.start(Channel.DEFAULT);
        Runtime.getRuntime().addShutdownHook(new Shutdown(channel));
        test.run();

        System.out.println("System test complete, sleeping to let threads finish.");
        Thread.sleep(60 * 1000 * 60);
    }

    public static class Shutdown extends Thread {
        ManagedChannel channel = null;

        public Shutdown(ManagedChannel channel) {
            this.channel = channel;
        }

        @Override
        public void run() {
            System.out.println("Shutting down...");
            SystemExit exit = new SystemExit(5000);
            exit.setDaemon(true);
            exit.start();
            try {
                channel.stop(Channel.DEFAULT);

            } catch (Exception x) {
                x.printStackTrace();
            }
            System.out.println("Channel stopped.");
        }
    }

    public static class SystemExit extends Thread {
        private long delay;

        public SystemExit(long delay) {
            this.delay = delay;
        }

        @Override
        public void run() {
            try {
                sleep(delay);
            } catch (Exception x) {
                x.printStackTrace();
            }
            System.exit(0);

        }
    }
}