package fedora.server.search;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.dom4j.Dom4jXPath;

import fedora.server.errors.DatastreamNotFoundException;
import fedora.server.errors.ObjectIntegrityException;
import fedora.server.errors.ServerException;
import fedora.server.storage.ConnectionPool;
import fedora.server.storage.DOReader;
import fedora.server.storage.types.DatastreamXMLMetadata;
import fedora.server.utilities.ColumnSpec;
import fedora.server.utilities.SQLUtility;
import fedora.server.utilities.TableSpec;

/**
 * Half finished gDatabase kind of idea
 * @author lobo
 *
 */
public class CustomFieldsSQLImpl  {

    /** Logger for this class. */
    private static final Logger LOG = Logger.getLogger(
    		CustomFieldsSQLImpl.class.getName());

    private List tableSpecs = null;

    private ConnectionPool cPool;

    /** prefixes of numeric types in lowercase */
    public static String[] DB_NUMERIC_TYPES=new String[] { "bigint",
    	"smallint", "int", "decimal", "numeric", "real", "double",
    	"serial", "bigserial" };

    public CustomFieldsSQLImpl(ConnectionPool cPool)
    {
    	this.cPool = cPool;
    }

	public void update(DOReader reader) throws ServerException {
		List tableSpecs = getTableSpecs();
		String pid = reader.GetObjectPID();
		Connection conn = null;

		TableSpec table;
        for(Object tableObj : tableSpecs) {
        	table = (TableSpec) tableObj;

    		if (!reader.getContentModelId().equals(table.getContentModelId()))
    			continue;

        	DatastreamXMLMetadata dsXML=null;
            try {
            	dsXML=(DatastreamXMLMetadata) reader.GetDatastream(table.getDatastreamId(), null);
            } catch (ClassCastException cce) {
                throw new ObjectIntegrityException("Object " + pid
                        + " has a "+ table.getDatastreamId() +" datastream, but it's not inline XML.");
            }
            if (dsXML==null) {
            	throw new DatastreamNotFoundException("Datastream with id "+ table.getDatastreamId() +"  was not found.");
            }

			String[] names;
			String[] values;
			Boolean[] numeric;
        	Document xmlDom = DocumentHelper.parseText(dsXML.getContentStream());
			SimpleNamespaceContext namespace = new SimpleNamespaceContext(table.getNamespace());

			int i = 0;
			Iterator columnIt = table.columnSpecIterator();
			while(columnIt.hasNext())
			{
				ColumnSpec column = (ColumnSpec) columnIt.next();

	            Dom4jXPath xpath = new Dom4jXPath(column.getXPath());
	            xpath.setNamespaceContext(namespace);
	            List<Element> l = xpath.selectNodes(xmlDom);
	            names[i] = column.getName();
	            for (Iterator<Element> it = l.iterator(); it.hasNext();)
	            {
	            	values[i] += it.next().getText();
	            	if (it.hasNext()) values[i] += " ";
	            }
	            numeric[i] = isTypeNumeric(column.getType());

				i++;
			}

	        if (conn == null)
	        	conn = cPool.getConnection();
	        SQLUtility.replaceInto(conn, table.getName(), names,
	                values, table.getPrimaryColumnName(), numeric);
        }
	}

	public List getTableSpecs() throws ServerException, IOException
	{
        if (tableSpecs == null)
        {
			String dbSpecLocation = "fedora/server/storage/resources/custom.dbspec";
	        InputStream in = getClass().getClassLoader().getResourceAsStream(dbSpecLocation);
	        tableSpecs = TableSpec.getTableSpecs(in);
        }
        return tableSpecs;
	}

	protected boolean isTypeNumeric(String type)
	{
		type = type.toLowerCase();
		for (int i = 0; i < DB_NUMERIC_TYPES.length; i++)
		{
			if ( type.startsWith(DB_NUMERIC_TYPES[i]))
				return true;
		}
		return false;
	}
}
