/*
 ************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
package com.sun.star.wizards.reportbuilder;

import com.sun.star.util.XModeSelector;

import com.sun.star.awt.XWindowPeer;
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.container.XHierarchicalNameContainer;
import com.sun.star.container.XNameContainer;
import com.sun.star.frame.XController;
import com.sun.star.frame.XDispatch;
import com.sun.star.frame.XDispatchProvider;
import com.sun.star.frame.XFrame;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.report.XReportDefinition;
import com.sun.star.sdb.XSubDocument;
import com.sun.star.sdb.application.DatabaseObject;
import com.sun.star.sdb.application.XDatabaseDocumentUI;
import com.sun.star.sdbc.XConnection;
import com.sun.star.ucb.XCommandProcessor;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.util.XModifiable;
import com.sun.star.util.XURLTransformer;
import com.sun.star.wizards.common.NoValidPathException;
import com.sun.star.wizards.common.Resource;
import com.sun.star.wizards.db.FieldColumn;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.Vector;
import com.sun.star.wizards.common.FileAccess;
import com.sun.star.wizards.common.NamedValueCollection;
import com.sun.star.wizards.report.IReportBuilderLayouter;
import com.sun.star.wizards.report.IReportDefinitionReadAccess;
import com.sun.star.wizards.report.IReportDocument;
import com.sun.star.wizards.report.ReportImplementationHelper;
import com.sun.star.wizards.report.ReportLayouter;
import com.sun.star.wizards.report.ReportWizard;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class use the IReportDocument Interface to communicate between the UI
 * and the ReportBuilderLayouter which communicates to the new Sun Report Builder.
 *
 * @author ll93751
 */
public class ReportBuilderImplementation extends ReportImplementationHelper
        implements IReportDocument, IReportDefinitionReadAccess
{
    private Resource            m_resource;
    private XDatabaseDocumentUI m_documentUI;

    private static final int MAXIMUM_GROUPCOUNT = 4;

    private ReportBuilderImplementation( XMultiServiceFactory _serviceFactory )
    {
        // creates an access to the ReportBuilder Extension
        super(_serviceFactory, ReportLayouter.SOOPTLANDSCAPE);
    }

    public static IReportDocument create( XMultiServiceFactory i_serviceFactory )
    {
        return new ReportBuilderImplementation( i_serviceFactory );
    }

    public void initialize( final XDatabaseDocumentUI i_documentUI, final Resource i_resource )
    {
        m_documentUI = i_documentUI;
        m_resource = i_resource;
    }

    public void clearDocument()
    {
    }

    public XWindowPeer getWizardParent()
    {
        final XWindowPeer aWindowPeer = UnoRuntime.queryInterface( XWindowPeer.class, getFrame().getComponentWindow() );
        return aWindowPeer;
    }
    private XFrame m_xFrame = null;
    // private ReportBuilderLayouter m_aReportBuilderLayouter = null;
    private String m_sReportBuilderLayoutName = "";

    /**
     * Get access to the current ReportLayouter, which depends on the name.
     * To change the ReportBuilderLayouter use <code>setReportBuilderLayouterName(String Name)</code>.
     * @return
     */
    private IReportBuilderLayouter getReportBuilderLayouter()
    {
        final IReportBuilderLayouter aReportBuilderLayouter = (IReportBuilderLayouter) getLayoutMap().get(m_sReportBuilderLayoutName);
        return aReportBuilderLayouter;
    }
    private Object m_aReportDocument;
    private XPropertySet m_documentDefinition;
    private XReportDefinition m_reportDocument;

    /**
     * initialize the Report Builder and open it representation
     * @param _aDoc
     * @param _xConnection
     */
    private void initialize(Object _aDoc)
    {
        m_aReportDocument = _aDoc;

        try
        {
            NamedValueCollection creationArgs = new NamedValueCollection();
            creationArgs.put( "DocumentServiceName", "com.sun.star.report.ReportDefinition" );
            creationArgs.put( "Mode", "remote" );

            XComponent[] docDefinition = new XComponent[] { null };
            XComponent reportDefinitionComp = m_documentUI.createComponentWithArguments(
                DatabaseObject.REPORT, creationArgs.getPropertyValues(), docDefinition );

            m_documentDefinition = UnoRuntime.queryInterface( XPropertySet.class, docDefinition[0] );
            m_reportDocument = UnoRuntime.queryInterface( XReportDefinition.class, reportDefinitionComp );
        }
        catch (com.sun.star.uno.Exception e)
        {
            ReportWizard.getLogger().log(com.sun.star.logging.LogLevel.SEVERE, "Problems with initialize the ReportDefinition" + e.getMessage());

        }

        switchOffPropertyBrowser();
        switchOffAddFieldWindow();

        setPageOrientation(m_nDefaultPageOrientation, false /* NO_LAYOUT*/);
    }

    private XModeSelector getModeSelector()
    {
        final XController xController = getReportDefinition().getCurrentController();
        final XModeSelector xModeSelector = UnoRuntime.queryInterface( XModeSelector.class, xController );
        return xModeSelector;
    }

    private void switchOffAddFieldWindow()
    {
        try
        {
            getModeSelector().setMode("remote");
        }
        catch (com.sun.star.uno.Exception e)
        {
            int dummy = 0;
        }
    }

    private void switchOnAddFieldWindow()
    {
        try
        {
            getModeSelector().setMode("normal");
        }
        catch (com.sun.star.uno.Exception e)
        {
            int dummy = 0;
        }
    }

    private void sleep(int _nSeconds)
    {
        try
        {
            Thread.sleep(_nSeconds * 1000);
        }
        catch (java.lang.InterruptedException e)
        {
        }

    }

    private void switchOffPropertyBrowser()
    {
        try
        {
            final XController xController = getReportDefinition().getCurrentController();
            final XDispatchProvider xDP = UnoRuntime.queryInterface( XDispatchProvider.class, xController );

            // Create special service for parsing of given URL.
            final Object aURLTransformer = getMSF().createInstance("com.sun.star.util.URLTransformer");
            final XURLTransformer xURLTransformer = UnoRuntime.queryInterface( com.sun.star.util.XURLTransformer.class, aURLTransformer );

            com.sun.star.util.URL[] aURL = new com.sun.star.util.URL[1];
            aURL[0] = new com.sun.star.util.URL();
            aURL[0].Complete = ".uno:ControlProperties";
            xURLTransformer.parseStrict(aURL);

            final XDispatch xDispatch = xDP.queryDispatch(aURL[0], "_self", 0);
            PropertyValue[] xEmpty = new PropertyValue[0];
            xDispatch.dispatch(aURL[0], xEmpty);
        }
        catch (com.sun.star.uno.Exception e)
        {
            int dummy = 0;
        }
    }

    private void switchOnPropertyBrowser()
    {
        // This is implemented with a toggle algorithm.
        switchOffPropertyBrowser();
    }

    /**
     * Returns the Frame of the underlieing Document
     * @return Returns the Frame of the parent (underlieing) Document
     */
    public XFrame getFrame()
    {
        if (m_xFrame == null)
        {
            initialize(getRecordParser().getReportDocuments());
            m_xFrame = getReportDefinition().getCurrentController().getFrame();
            setPageOrientation(m_nDefaultPageOrientation, true /* NO_LAYOUT*/);
        }
        return m_xFrame;
    }

    public boolean reconnectToDatabase(XMultiServiceFactory xMSF, PropertyValue[] Properties)
    {
        return false;
    }

    public void insertDatabaseDatatoReportDocument(XMultiServiceFactory xMSF)
    {
    }

    public void StopProcess()
    {
    }

    public void store(String Name, int OpenMode) throws com.sun.star.uno.Exception
    {
        // store into the ZIP Storage
        if (OpenMode == 1 /* static Report */)
        {
            // we will store later
            return;
        }

        final XCommandProcessor xProcessor = UnoRuntime.queryInterface( XCommandProcessor.class, m_documentDefinition );
        final com.sun.star.ucb.Command aCommand = new com.sun.star.ucb.Command();
        aCommand.Name = "store";

        xProcessor.execute(aCommand, xProcessor.createCommandIdentifier(), null);

        final XHierarchicalNameContainer aNameContainer = UnoRuntime.queryInterface(XHierarchicalNameContainer.class, m_aReportDocument);
        aNameContainer.insertByHierarchicalName( Name, m_documentDefinition );
    }

    public boolean liveupdate_addGroupNametoDocument(String[] GroupNames, String CurGroupTitle, Vector GroupFieldVector, ArrayList ReportPath, int iSelCount)
    {
        final int GroupCount = GroupFieldVector.size();
        if (GroupCount < MAXIMUM_GROUPCOUNT)
        {
            final FieldColumn CurFieldColumn = getRecordParser().getFieldColumnByTitle(CurGroupTitle);
            GroupFieldVector.addElement(CurFieldColumn.getFieldName());
        }
        return true;
    }

    public void refreshGroupFields(String[] _sNewNames)
    {
    }

    public void liveupdate_removeGroupName(String[] NewSelGroupNames, String CurGroupTitle, Vector GroupFieldVector)
    {
        final FieldColumn CurFieldColumn = getRecordParser().getFieldColumnByTitle(CurGroupTitle);
        GroupFieldVector.removeElement(CurFieldColumn.getFieldName());
    }

    private void setPageOrientation(int nOrientation, boolean bDoLayout)
    {
        getReportBuilderLayouter().setPageOrientation(nOrientation);
        if (bDoLayout)
        {
            getReportBuilderLayouter().layout();
        }
    }

    public void setPageOrientation(int nOrientation)
    {
        setPageOrientation(nOrientation, true);
    }

    public void liveupdate_changeLayoutTemplate(String LayoutTemplatePath/*, String BitmapPath*/)
    {
        /* Right Listbox */
        final IReportBuilderLayouter aLayouter = getReportBuilderLayouter();
        aLayouter.loadAndSetBackgroundTemplate(LayoutTemplatePath);
        aLayouter.layout();
    }

    /**
     * Change the current Layouter by a new one with the given Name _sName.
     * If there exist no other layouter with the given name, nothing happens, the layouter will not change to
     * an illegal one.
     * @param _sName Name of the other layouter.
     */
    public void setReportBuilderLayouterName(String _sName)
    {
        final IReportBuilderLayouter aCurrentLayouter = getReportBuilderLayouter();
        final IReportBuilderLayouter aNewLayouter = (IReportBuilderLayouter) m_aLayoutMap.get(_sName);
        if (aNewLayouter != null)
        {
            m_sReportBuilderLayoutName = _sName;
            aNewLayouter.initializeData(aCurrentLayouter);
            aNewLayouter.layout();
        }
    }

    public void liveupdate_changeContentTemplate(String ContentTemplatePath)
    {
        /* Left Listbox */
        setReportBuilderLayouterName(ContentTemplatePath);
    }

    public void layout_setupRecordSection(String TemplateName)
    {
    }

    public void removeTextTableAndTextSection()
    {
    }

    public void layout_selectFirstPage()
    {
    }

    private void closeReportDefinition()
    {
        try
        {
            if ( m_documentDefinition != null )
            {
                // set the document to "not modified", to ensure that it won't ask the user before closing
                XModifiable documentModify = UnoRuntime.queryInterface( XModifiable.class, m_reportDocument );
                documentModify.setModified( false );
                // actually close
                XSubDocument subComponent = UnoRuntime.queryInterface( XSubDocument.class, m_documentDefinition );
                subComponent.close();
            }
        }
        catch ( Exception ex )
        {
            Logger.getLogger( ReportBuilderImplementation.class.getName() ).log( Level.SEVERE, null, ex );
        }
        m_documentDefinition = null;
        m_reportDocument = null;
    }

    public void dispose()
    {
        getReportBuilderLayouter().dispose();
        closeReportDefinition();
    }

    public XComponent getComponent()
    {
        return null;
    }

    public void liveupdate_changeUserFieldContent(String fieldName, String titlename)
    {
    }

    public void liveupdate_updateReportTitle(String _sTitleName)
    {
    }

    public void addReportToDBView()
    {
    }

    private XComponent loadReport( final String i_reportName )
    {
        try
        {
            return m_documentUI.loadComponent( DatabaseObject.REPORT, i_reportName, false );
        }
        catch ( Exception ex )
        {
            Logger.getLogger( ReportBuilderImplementation.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return null;
    }

    private XComponent loadReportFromDocumentDefinition()
    {
        final XCommandProcessor commandProcessor = UnoRuntime.queryInterface(XCommandProcessor.class, m_documentDefinition);

        com.sun.star.ucb.Command aCommand = new com.sun.star.ucb.Command();
        aCommand.Name = "open";
        try
        {
            final Object result = commandProcessor.execute( aCommand, commandProcessor.createCommandIdentifier(), null );
            return UnoRuntime.queryInterface( XComponent.class, result );
        }
        catch ( Exception ex )
        {
            Logger.getLogger( ReportBuilderImplementation.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return null;
    }

    public void createAndOpenReportDocument( String i_name, boolean i_asTemplate, boolean i_openForEditing )
    {
        if ( i_openForEditing )
        {
            // we won't destroy the report builder window, also don't create a document
            // Do we need to reopen the report builder with the known name?
            switchOnAddFieldWindow();
            switchOnPropertyBrowser();
            return;
        }

        if ( i_asTemplate )
        {
            // don't need the report definition anymore - the document it represents has already been stored
            closeReportDefinition();

            // open the report, again, this time not in design, but containing data
            loadReport( i_name );
        }
        else
        {
            // execute the report from the (yet unsaved) report definition
            XComponent document = loadReportFromDocumentDefinition();

            // don't need the report definition anymore
            closeReportDefinition();

            // store the generated report
            if ( getRecordParser().storeDatabaseDocumentToTempPath( document, i_name ) )
                getRecordParser().addReportDocument( document, false );
        }

        dispose();
    }

    private XConnection getConnection()
    {
        return getRecordParser().DBConnection;
    }

    public void initializeFieldColumns(final int _nType, final String TableName, final String[] FieldNames)
    {
        getRecordParser().initializeFieldColumns(FieldNames, TableName);

        final com.sun.star.wizards.db.RecordParser a = getRecordParser();
        int[] FieldTypes = new int[FieldNames.length];
        int[] FieldWidths = new int[FieldNames.length];
        for (int i = 0; i < FieldNames.length; i++)
        {
            FieldTypes[i] = a.FieldColumns[i].getFieldType();
            FieldWidths[i] = a.FieldColumns[i].getFieldWidth();
        }
        getReportBuilderLayouter().setTableName(_nType, TableName);
        getReportBuilderLayouter().insertFieldNames(FieldNames);
        getReportBuilderLayouter().insertFieldTypes(FieldTypes);
        getReportBuilderLayouter().insertFieldWidths(FieldWidths);

        getReportBuilderLayouter().layout();
    }

    public void setFieldTitles(String[] _aFieldTitles)
    {
        getRecordParser().setFieldTitles(_aFieldTitles);

        getReportBuilderLayouter().insertFieldTitles(_aFieldTitles);
        getReportBuilderLayouter().layout();
    }

    public void setSorting(String[][] _aSortFieldNames)
    {
        getRecordParser().setSortFieldNames(_aSortFieldNames);
    }

    public void setGrouping(String[] _aGroupFieldNames)
    {
        getRecordParser().prependSortFieldNames(_aGroupFieldNames);

        getReportBuilderLayouter().insertGroupNames(_aGroupFieldNames);
        getReportBuilderLayouter().layout();
    }

    public String getLayoutPath()
    {
        return "default";
    }
    private ArrayList m_aReportPath;

    public ArrayList getReportPath()
    {
        if (m_aReportPath == null)
        {
            try
            {
                // Check general availability of office paths
                m_aReportPath = FileAccess.getOfficePaths( getMSF(), "Template", "share", "/wizard" );
                FileAccess.combinePaths( getMSF(), m_aReportPath, "/wizard/report" );
            }
            catch ( NoValidPathException ex )
            {
                Logger.getLogger( ReportBuilderImplementation.class.getName() ).log( Level.SEVERE, null, ex );
            }
        }
        return m_aReportPath;
    }

    public String getContentPath()
    {
        return "";
    }

    public int getDefaultPageOrientation()
    {
        return m_nDefaultPageOrientation;
    }

    /**
     * Helper function to get a com.sun.star.wizards.report.layout.ReportBuilderLayouter by it's name
     *
     * @param _sClassName
     * @return the object or null
     */
    private IReportBuilderLayouter getLayoutInstanceFrom(String _sClassName)
    {
        try
        {
            // TODO: Use Package.getPackages(...)
            final Class a = Class.forName(_sClassName);

            final Constructor cTor = a.getConstructor(new Class[]
                    {
                        IReportDefinitionReadAccess.class, Resource.class
                    });
            Object[] aParams = new Object[2];
            aParams[0] = this;
            aParams[1] = m_resource;
            final IReportBuilderLayouter aReportBuilderLayouter = (IReportBuilderLayouter) cTor.newInstance(aParams);
            return aReportBuilderLayouter;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
    private LinkedHashMap m_aLayoutMap = null;

    private void insertIntoLayoutMap(IReportBuilderLayouter _aLayout)
    {
        insertIntoLayoutMap(_aLayout, false);
    }

    private void insertIntoLayoutMap(IReportBuilderLayouter _aLayout, boolean _bDefault)
    {
        if (_aLayout != null)
        {
            final String sName = _aLayout.getName();
            if (_bDefault)
            {
                m_sReportBuilderLayoutName = sName;  // here we set the default layout name!
            }
            m_aLayoutMap.put(sName, _aLayout);
        }
    }

    /**
     * Initialize all well known com.sun.star.wizards.report.layout.ReportBuilderLayouter Objects and create exact one instance.
     */
    private LinkedHashMap getLayoutMap()
    {
        if (m_aLayoutMap == null)
        {
            // The LayoutMap is empty, so we create a new LinkedHashMap
            m_aLayoutMap = new LinkedHashMap();

            // TODO: We must know the name of a layouts, There should be a way to say where to find, not the names.
            IReportBuilderLayouter aLayout = getLayoutInstanceFrom("com.sun.star.wizards.reportbuilder.layout.Tabular");
            insertIntoLayoutMap(aLayout, true);
            aLayout = getLayoutInstanceFrom("com.sun.star.wizards.reportbuilder.layout.ColumnarSingleColumn");
            insertIntoLayoutMap(aLayout);
            aLayout = getLayoutInstanceFrom("com.sun.star.wizards.reportbuilder.layout.ColumnarTwoColumns");
            insertIntoLayoutMap(aLayout);
            aLayout = getLayoutInstanceFrom("com.sun.star.wizards.reportbuilder.layout.ColumnarThreeColumns");
            insertIntoLayoutMap(aLayout);
            aLayout = getLayoutInstanceFrom("com.sun.star.wizards.reportbuilder.layout.InBlocksLabelsAbove");
            insertIntoLayoutMap(aLayout);
            aLayout = getLayoutInstanceFrom("com.sun.star.wizards.reportbuilder.layout.InBlocksLabelsLeft");
            insertIntoLayoutMap(aLayout);

        }
        return m_aLayoutMap;
    }

    /**
     * Return a string array array with all found layouts
     * At the moment these layout are hard coded
     * @return
     */
    public String[][] getDataLayout()
    {
        String[][] ContentFiles;
        ContentFiles = new String[2][];
        String[] a = new String[getLayoutMap().size()];
        String[] b = new String[getLayoutMap().size()];

        // run through the whole layoutmap and
        final Set aKeys = getLayoutMap().keySet();
        final Iterator aKeyIterator = aKeys.iterator();
        int i = 0;
        while (aKeyIterator.hasNext())
        {
            final String sKey = (String) aKeyIterator.next();
            a[i] = sKey;
            final IReportBuilderLayouter aLayouter = (IReportBuilderLayouter) m_aLayoutMap.get(sKey);
            b[i++] = aLayouter.getLocalizedName();
        }

        ContentFiles[1] = a;
        ContentFiles[0] = b;
        return ContentFiles;
    }
    private String m_sDefaultHeaderLayoutPath;

    public String[][] getHeaderLayout()
    {
        String[][] LayoutFiles;
        try
        {
            // TODO: check different languages in header layouts
            ArrayList aReportPath = FileAccess.getOfficePaths(getMSF(), "Template", "share", "/wizard");
            FileAccess.combinePaths(getMSF(), aReportPath, "/wizard/report");

            LayoutFiles = FileAccess.getFolderTitles(getMSF(), null, aReportPath, ".otr");
        }
        catch (com.sun.star.wizards.common.NoValidPathException e)
        {
            // if there are problems, don't show anything is a little bit hard.
            LayoutFiles = new String[2][];
            String[] a = new String[1];
            String[] b = new String[1];
            a[0] = "DefaultLayoutOfHeaders";
            b[0] = "default";
            LayoutFiles[1] = a;
            LayoutFiles[0] = b;
        }
        for (int i = 0; i < LayoutFiles[0].length; i++)
        {
            if (LayoutFiles[0][i].equals("default"))
            {
                m_sDefaultHeaderLayoutPath = LayoutFiles[1][i];
                break;
            }
        }
        return LayoutFiles;
    }

    public XReportDefinition getReportDefinition()
    {
        if (m_reportDocument == null)
        {
            throw new NullPointerException("Report Definition is not already initialized, check if you too early access the report definition.");
        }
        return m_reportDocument;
    }

    public XMultiServiceFactory getGlobalMSF()
    {
        return getMSF();
    }

    public void importReportData(ReportWizard aWizard)
    {
    }

    public String getDefaultHeaderLayout()
    {
        return m_sDefaultHeaderLayoutPath;
    }

    public void setCommand(String _sCommand)
    {
        getRecordParser().Command = _sCommand;
        getReportDefinition().setCommand(_sCommand);
    }

    public void setCommandType(int _nCommand)
    {
        getReportDefinition().setCommandType(_nCommand);
    }


    public void checkInvariants() throws java.lang.Exception
    {
        final String sDefaultHeaderLayoutPath = getDefaultHeaderLayout();
        if (sDefaultHeaderLayoutPath == null)
        {
            throw new java.io.IOException("default.otr");
        }

        FileAccess aAccess = new FileAccess(getGlobalMSF());
        if (! aAccess.exists(sDefaultHeaderLayoutPath, true))
        {
            throw new java.io.IOException("default.otr");
        }
    }

}


