package org.apache.xindice.tools;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.Collection;
import org.apache.xindice.core.Database;
import org.apache.xindice.core.data.Key;
import org.apache.xindice.core.data.Value;
import org.apache.xindice.core.filer.BTreeCallback;
import org.apache.xindice.core.filer.BTree;
import org.apache.xindice.core.indexer.Indexer;
import org.apache.xindice.core.indexer.ValueIndexer;
import org.apache.xindice.core.indexer.IndexMatch;
import org.apache.xindice.util.Configuration;
import org.apache.xindice.xml.dom.DOMParser;
import org.apache.xindice.xml.SymbolTable;

import java.io.File;

public class IndexScanner extends BTree {

    private static final Log log = LogFactory.getLog(IndexScanner.class);


    public static void main(String[] args) throws Exception {
        if (args.length != 3) {
            usage();
            System.exit(1);
        }

        File location = new File(args[0]);
        String path = location.getAbsolutePath();
        String name = location.getName();

        if ("".equals(name) || !location.exists() || !location.isDirectory()) {
            System.out.println("Database path must point to existing database directory");
            System.exit(1);
        }

        Database db = null;
        Indexer idx = null;
        try {
            // create minimal database configuration instead of trying to locate system.xml
            String config = "<root-collection dbroot='" + path + "/' name='" + name + "'/>";
            db = new Database();

            db.setConfig(new Configuration(DOMParser.toDocument(config)));
            Collection col = db.getCollection(args[1]);
            if (col == null) {
                System.out.println("Collection " + args[1] + " is not found.");
                return;
            }

            idx = col.getIndexer(args[2]);
            if (idx == null || !(idx instanceof ValueIndexer)) {
                System.out.println("Index " + args[2] + " is not found.");
                return;
            }

            idx.open();
            new IndexScanner().processIndex((ValueIndexer) idx, col.getSymbols());
        } finally {
            if (idx != null) {
                idx.close();
            }

            if (db != null) {
                db.close();
            }
        }

    }

    private static void usage() {
        System.out.println("Usage:");
        System.out.println("    IndexScanner <db location> <collection path> <index name>");
        System.out.println();
        System.out.println("DB Location - Directory containing Xindice database files.");
        System.out.println("Collection path - Path to the target collection (not including database).");
        System.out.println("Index name - Name of the string value index to scan. Must belong to the target collection.");
        System.out.println();
        System.out.println("Important: Shutdown database before proceeding!");
        System.out.println();
    }

    private IndexMatch getIndexMatch(Value v) {
        byte[] b = v.getData();
        int l = b.length - 13;
        Key key = new Key(b, 0, b.length - 13);

        int pos = ((b[l + 1] << 24) | (b[l + 2] << 16) | (b[l + 3] << 8) | b[l + 4]);
        int len = ((b[l + 5] << 24) | (b[l + 6] << 16) | (b[l + 7] << 8) | b[l + 8]);
        short elemID = (short) ((b[l + 9] << 8) | b[l + 10]);
        short attrID = (short) ((b[l + 11] << 8) | b[l + 12]);

        return new IndexMatch(key, pos, len, elemID, attrID);
    }

    private void processIndex(final ValueIndexer filer, final SymbolTable symbols) {

        try {
            filer.query(null, new BTreeCallback() {
                Value value;
                public boolean indexInfo(Value value, long pos) {
                    if (pos == -1000) {
                        IndexMatch match = getIndexMatch(value);
                        String elNS = symbols.getNamespaceURI(match.getElement());
                        String attrNS = symbols.getNamespaceURI(match.getAttribute());
                        String el = symbols.getName(match.getElement());
                        String attr = symbols.getName(match.getAttribute());
                        if (attr != null) {
                            System.out.println("value: " + this.value + "\tkey: " + match.getKey() + "\t" +
                                    (elNS != null ? "[" + elNS + "]" : "") + el + "@" +
                                    (attrNS != null ? "[" + attrNS + "]" : "") + attr);
                        } else {
                            System.out.println("value: " + this.value + "\tkey: " + match.getKey() + "\t" +
                                               (elNS != null ? "[" + elNS + "]" : "") + el);
                        }
                    } else {
                        this.value = value;
                        BTree.BTreeRootInfo root = new BTree.BTreeRootInfo(value, pos);
                        try {
                            filer.query(root, null, this);
                        } catch (Exception e) {
                            log.error("Got an exception while scanning the index", e);
                        }
                    }

                    return true;
                }
            });

        } catch (Exception e) {
            log.error("Got an exception while scanning the index", e);
        }
    }
}