package basextest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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.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 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);

            try (final Session adminSession = new LocalSession(rootContext, "admin", adminPassword)) {
                adminSession.execute(new CreateDB(name));
                adminSession.execute(new Grant(Commands.CmdPerm.READ, userName, name));
                adminSession.execute(new Grant(Commands.CmdPerm.WRITE, userName, name));
            }
            catch (IOException ex) {
                s_Logger.log(Level.SEVERE, null, ex);
            }
        }

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

        private void store() {
            try (final Session userSession = new LocalSession(rootContext, userName, userPassword)) {
                userSession.execute(new Open(name));
                userSession.execute(new Add(name, getXml()));
            }
            catch (IOException ex) {
                s_Logger.log(Level.SEVERE, null, ex);
            }
        }

        private void updateMetaStore() {
            try (final Session adminSession = new LocalSession(rootContext, "admin", adminPassword)) {
                adminSession.execute(new Open("STORE"));
                adminSession.execute(new Replace(name, getXml()));
            }
            catch (Exception _Ex) {
                s_Logger.log(Level.WARNING, "Cannot update doc in STORE for [" + name + "] | Exception is ", _Ex);
            }
        }

        @Override
        public void run() {
            try (final Session adminSession = new LocalSession(rootContext, "admin", adminPassword)) {
                adminSession.execute(new Open("STORE"));
                adminSession.execute(new Add(name, getXml()));
            }
            catch (Exception _Ex) {
                s_Logger.log(Level.WARNING, "Cannot add doc in STORE for [" + name + "] | Exception is ", _Ex);
            }
            while (goOn) {
                store();
                updateMetaStore();
            }
        }
    }

    private final Context rootContext;

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

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

    private void firstTimeConfig() {
        try (final Session adminSession = new LocalSession(rootContext, "admin", adminPassword)) {

            s_Logger.log(Level.INFO, "Set Options");
            adminSession.execute(new Set(MainOptions.AUTOFLUSH, false));
            adminSession.execute(new Set(MainOptions.ADDCACHE, false));
            adminSession.execute(new Set(MainOptions.INTPARSE, true));
            adminSession.execute(new Set(MainOptions.STRIPNS, true));
            adminSession.execute(new Set(MainOptions.UPDINDEX, false));
            adminSession.execute(new Set(MainOptions.TEXTINDEX, false));
            adminSession.execute(new Set(MainOptions.ATTRINDEX, true));
            adminSession.execute(new CreateDB("STORE"));
            adminSession.execute(new CreateUser(userName, userPassword));
            
        }
        catch (IOException ex) {
            s_Logger.log(Level.SEVERE, "Cannot perform first time config", ex);
        }
    }

    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;
    }
}
