/*
 * Copyright (C) 2006 JasperSoft http://www.jaspersoft.com
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed WITHOUT ANY WARRANTY; and without the 
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see http://www.gnu.org/licenses/gpl.txt 
 * or write to:
 * 
 * Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330,
 * Boston, MA  USA  02111-1307
 */

package com.jaspersoft.jasperserver.api.metadata;

import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;

import junit.textui.TestRunner;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.olap4j.CellSet;
import org.olap4j.OlapConnection;
import org.olap4j.layout.RectangularCellSetFormatter;
import org.olap4j.layout.TraditionalCellSetFormatter;
import org.olap4j.metadata.Cube;
import org.olap4j.metadata.Dimension;
import org.olap4j.metadata.Hierarchy;
import org.olap4j.metadata.Level;
import org.olap4j.metadata.Schema;
import org.springframework.security.acl.basic.SimpleAclEntry;

import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.common.domain.impl.ExecutionContextImpl;
import com.jaspersoft.jasperserver.api.metadata.common.domain.FileResource;
import com.jaspersoft.jasperserver.api.metadata.common.domain.Folder;
import com.jaspersoft.jasperserver.api.metadata.common.domain.Resource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.JdbcReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.olap.domain.MondrianConnection;
import com.jaspersoft.jasperserver.api.metadata.olap.service.impl.OlapConnectionServiceImpl;
import com.jaspersoft.jasperserver.api.metadata.user.domain.Role;
import com.jaspersoft.jasperserver.util.test.BaseJasperServerTest;

/**
 * @author bob
 *
 */
public class Olap4jConnectionTest extends BaseJasperServerTest {
    private static final Log log = LogFactory.getLog(Olap4jConnectionTest.class);

    protected final String foodMartSchema2006URI   = "/queries/FoodmartSchema2006.xml";
    protected final String foodMartSchema2006UpperURI   = "/queries/FoodmartSchema2006Upper.xml";
    protected final String foodMartJaSchemaURI = "/queries/FoodMart_ja.xml";
    protected final String sugarCRMSchemaURI   = "/queries/SugarCRMSchema.xml";
    protected final String sugarCRMSchemaUpperURI   = "/queries/SugarcrmSchemaUpper.xml";
    protected final String tempDir   = "/temp_olap4j";
    protected final String mdsUri = tempDir + "/foodmartMDX_DS";
	
    public static String SAMPLE_FOODMART_MDX_QUERY =
    	"select {[Measures].[Unit Sales], [Measures].[Store Cost], [Measures].[Store Sales]} on columns, " +
    	"{([Promotion Media].[All Media], [Product].[All Products])} ON rows " + 
    	"from Sales " +
    	"where ([Time].[2006].[Q4].[12])";
    
	public enum Format {
        /**
         * Traditional format, one row per cell.
         */
        TRADITIONAL,

        /**
         * Rectangular format that is similar to {@link #RECTANGULAR} but omits
         * vertical bars and is therefore more compact.
         */
        COMPACT_RECTANGULAR,

        /**
         * Rectangular format that uses vertical bars and hyphens to draw a
         * grid.
         */
        RECTANGULAR
    }

    private ExecutionContext executionContext;

    // direct access to impl to call methods that aren't in the i/f...
    private OlapConnectionServiceImpl olapConnectionServiceTarget;
    
    private MondrianConnection mondrianDataSource;

    public Olap4jConnectionTest() {
    	super();
    	setAutowireMode(AUTOWIRE_BY_NAME);
    }

    public Olap4jConnectionTest(String name) {
    	super(name);
    	setAutowireMode(AUTOWIRE_BY_NAME);
    }
    
    public static void main(String[] args) {
        TestRunner.run(OlapConnectionTest.class);
    }

    protected void onSetUp() throws Exception {

        executionContext = new ExecutionContextImpl();
		basicAuthSetup();
        Folder tempFolder = getRepositoryService().getFolder(null, tempDir);
        if (tempFolder != null) {
        	getRepositoryService().deleteFolder(null, tempDir);
        }
        tempFolder = ensureParentFolderExists(tempDir + "/x");
        Role adminRole = getOrCreateRole(administratorRoleName);
        createObjectPermission(tempDir, adminRole, SimpleAclEntry.ADMINISTRATION);
    	// create mds if not there
    	createMondrianDataSource(mdsUri, "foodmart", foodMartSchema2006URI);
    }
    
    protected MondrianConnection createMondrianDataSource(String uri, String dsName, String schemaPath) throws Exception {
    	if (mondrianDataSource == null) {
	        Resource oldDS = getRepositoryService().getResource(null, uri);
	        if (oldDS != null) {
	        	getRepositoryService().deleteResource(null, uri);
	        }

	        // create mondrian ds
	    	String name = getNameFromURI(uri);
	    	Folder parent = ensureParentFolderExists(uri);
	        mondrianDataSource = (MondrianConnection) getRepositoryService().newResource(null, MondrianConnection.class);
	        mondrianDataSource.setName(name);
	        mondrianDataSource.setLabel(name);
	        mondrianDataSource.setParentFolder(parent);
	
	        // create and set schema
	        FileResource schema = (FileResource) getRepositoryService().newResource(null, FileResource.class);
	        schema.setFileType(FileResource.TYPE_XML);
	        schema.setName(name + "_schema");
	        schema.setLabel(name + "_schema");
	        schema.setParentFolder(parent);
	        InputStream schemaIS = getClass().getResourceAsStream(schemaPath);
	        schema.readData(schemaIS);
	        getRepositoryService().saveResource(null, schema);
	        
	        mondrianDataSource.setSchemaReference(schema.getURIString());
	
	        // create and set jdbc data source
	        JdbcReportDataSource ds = createAndSaveJdbcDSFromProps(parent.getURIString(), dsName);
	        mondrianDataSource.setDataSourceReference(ds.getURIString());
	
	        // save report
	        getRepositoryService().saveResource(null, mondrianDataSource);
    	}
        return mondrianDataSource;
    }

	public void runOlap4jQuery(String connResourceName, String mdxQuery) throws Exception {
		OlapConnection monConnection = getOlapConnectionServiceTarget().getOlapConnection(null, connResourceName);

		// perform olap test query with this mondrian Connection
        CellSet result = monConnection.prepareOlapStatement(mdxQuery).executeQuery();
        String resultString = toString(result, Format.COMPACT_RECTANGULAR);
        log.warn(resultString);
	}
	
    static String toString(CellSet cellSet, Format format) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        switch (format) {
        case TRADITIONAL:
            new TraditionalCellSetFormatter().format(cellSet, pw);
            break;
        case COMPACT_RECTANGULAR:
        case RECTANGULAR:
            new RectangularCellSetFormatter(
                format == Format.COMPACT_RECTANGULAR)
                .format(cellSet, pw);
            break;
        }
        pw.flush();
        return sw.toString();
    }

    public void testSampleFoodmartQuery() throws Exception {
    	runOlap4jQuery(mdsUri, SAMPLE_FOODMART_MDX_QUERY);
    }

	public void testMetadata() throws Exception {
		OlapConnection monConnection = getOlapConnectionServiceTarget().getOlapConnection(null, mdsUri);
		Schema schema = monConnection.getSchema();
		for (Cube cube : schema.getCubes()) {
			log.warn("cube: " + cube.getName() + ", " + cube.getDescription(null));
			for (Dimension d : cube.getDimensions()) {
				log.warn("  dimension: " + d.getName());
				Hierarchy h = d.getDefaultHierarchy();
				for (Level l : h.getLevels()) {
					log.warn("    level: " + l.getName());
				}
			}
		}
	}
	

    /**
	 * @param olapConnectionServiceTarget The olapConnectionServiceTarget to set.
	 */
	public void setOlapConnectionServiceTarget(OlapConnectionServiceImpl olapConnectionServiceTarget) {
		this.olapConnectionServiceTarget = olapConnectionServiceTarget;
	}

	/**
	 * @return Returns the olapConnectionServiceTarget.
	 */
	public OlapConnectionServiceImpl getOlapConnectionServiceTarget() {
		return olapConnectionServiceTarget;
	}

}
