package basextest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.basex.api.client.LocalSession;
import org.basex.api.client.Session;
import org.basex.core.Command;
import org.basex.core.Context;
import org.basex.core.MainOptions;
import org.basex.core.cmd.Add;
import org.basex.core.cmd.CreateDB;
import org.basex.core.cmd.CreateUser;
import org.basex.core.cmd.Grant;
import org.basex.core.cmd.Open;
import org.basex.core.cmd.Replace;
import org.basex.core.cmd.Set;
import org.basex.core.parse.Commands;

public class BaseXTest {

    private final static Logger s_Logger = Logger.getLogger(BaseXTest.class.getName());
    private static boolean goOn = true;

    private final String adminPassword = "admin";
    private final String userName = "USER";
    private final String userPassword = "USER_PASSWD";

    private class BaseXExecutor {

        private final List<Command> commands;

        BaseXExecutor(List<Command> _Commands) {

            commands = _Commands;
        }

        void executeAdmin() {
            execute("admin", adminPassword);
        }

        void executeUser() {
            execute(userName, userPassword);
        }

        void execute(final String _userName, final String _password) {

            final CountDownLatch latch = new CountDownLatch(1);
            final Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try (final Session session = new LocalSession(rootContext, _userName, _password)) {
                        for (Command command : commands) {
                            session.execute(command);
                        }
                    }
                    catch (IOException ex) {
                        s_Logger.log(Level.SEVERE, "BaseXExecutor#execute(" + _userName + "," + _password + ")#run", ex);
                    }
                    finally {
                        latch.countDown();
                    }
                }
            });
            t.start();
            try {
                latch.await();
            }
            catch (InterruptedException ex) {
                s_Logger.log(Level.SEVERE, "BaseXExecutor#execute(" + _userName + "," + _password + ")#latch.await", ex);
            }
        }
    }

    private class Writer implements Runnable {

        private final String name;
        private int size = 0;

        Writer(final int _index) {
            name = _index + "_document";
            s_Logger.log(Level.INFO, "new Writer created, doc name : {0}", name);

            List<Command> commands = new ArrayList<>();
            commands.add(new CreateDB(name));
            commands.add(new Grant(Commands.CmdPerm.READ, userName, name));
            commands.add(new Grant(Commands.CmdPerm.WRITE, userName, name));
            new BaseXExecutor(commands).executeAdmin();
        }

        private String getXml() {
            size++;
            StringBuilder builder = new StringBuilder();
            builder.append("<root><elem1>").append(size).append("</elem1></root>");
            return builder.toString();
        }

        private void store() {
            List<Command> commands = new ArrayList<>();
            commands.add(new Open(name));
            commands.add(new Add(name, getXml()));
            new BaseXExecutor(commands).executeUser();
        }

        private void updateMetaStore() {
            List<Command> commands = new ArrayList<>();
            commands.add(new Open("STORE"));
            commands.add(new Replace(name, getXml()));
            new BaseXExecutor(commands).executeAdmin();
        }

        private void openStore() {
            List<Command> commands = new ArrayList<>();
            commands.add(new Open("STORE"));
            commands.add(new Add(name, getXml()));
            new BaseXExecutor(commands).executeAdmin();
        }

        @Override
        public void run() {
            openStore();
            while (goOn) {
                store();
                updateMetaStore();
            }
        }
    }

    private final Context rootContext;

    BaseXTest() {
        rootContext = new Context();
        firstTimeConfig();
    }

    private void startTest() {
        for (int i = 0; i < 50; i++) {
            Thread writer = new Thread(new Writer(i), "Thread_" + i);
            writer.start();
        }
    }

    private void firstTimeConfig() {
        s_Logger.log(Level.INFO, "Set Options");
        List<Command> commands = new ArrayList<>();
        commands.add(new Set(MainOptions.AUTOFLUSH, false));
        commands.add(new Set(MainOptions.ADDCACHE, false));
        commands.add(new Set(MainOptions.INTPARSE, true));
        commands.add(new Set(MainOptions.STRIPNS, true));
        commands.add(new Set(MainOptions.UPDINDEX, false));
        commands.add(new Set(MainOptions.TEXTINDEX, false));
        commands.add(new Set(MainOptions.ATTRINDEX, true));
        commands.add(new CreateDB("STORE"));
        commands.add(new CreateUser(userName, userPassword));
        new BaseXExecutor(commands).executeAdmin();
    }

    public static void main(String[] args) throws IOException {
        BaseXTest test = new BaseXTest();
        test.startTest();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        bufferedReader.readLine();
        goOn = false;
    }
}
