package odfdom.example4;

import com.sun.star.beans.PropertyValue;
import com.sun.star.frame.XDispatch;
import com.sun.star.frame.XDispatchProvider;
import com.sun.star.frame.XFrame;
import com.sun.star.frame.XModel;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.XInitialization;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.lib.uno.helper.Factory;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.lib.uno.helper.WeakBase;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.odftoolkit.odfdom.doc.OdfDocument;
import org.odftoolkit.odfdom.doc.OdfFileDom;
import org.odftoolkit.odfdom.doc.element.table.OdfTable;
import org.odftoolkit.odfdom.doc.element.table.OdfTableCell;
import org.odftoolkit.odfdom.doc.element.table.OdfTableRow;
import org.odftoolkit.odfdom.dom.OdfNamespace;
import org.w3c.dom.NodeList;

public final class OpenDocumentExport extends WeakBase
        implements XServiceInfo, XDispatchProvider, XInitialization, XDispatch {

    private final XComponentContext m_xContext;
    private XFrame m_xFrame;
    private static final String m_implementationName = OpenDocumentExport.class.getName();
    private static final String[] m_serviceNames = {"com.sun.star.frame.ProtocolHandler"};
    // Edit the LAB_ROOT - it is the directory where output is written to.
    private static final String LAB_ROOT = <LAB_ROOT>;
    private static final Logger LOG = Logger.getLogger(OpenDocumentExport.class.getName());
    private static final char ROW_DELIMITER_CHAR = ';';
    private static final char CELL_DELIMITER_CHAR = ',';

    public OpenDocumentExport(XComponentContext context) {
        m_xContext = context;
    }

    public static XSingleComponentFactory __getComponentFactory(String sImplementationName) {
        XSingleComponentFactory xFactory = null;

        if (sImplementationName.equals(m_implementationName)) {
            xFactory = Factory.createComponentFactory(OpenDocumentExport.class, m_serviceNames);
        }
        return xFactory;
    }

    public static boolean __writeRegistryServiceInfo(XRegistryKey xRegistryKey) {
        return Factory.writeRegistryServiceInfo(m_implementationName,
                m_serviceNames,
                xRegistryKey);
    }

    // com.sun.star.lang.XServiceInfo:
    public String getImplementationName() {
        return m_implementationName;
    }

    public boolean supportsService(String sService) {
        int len = m_serviceNames.length;

        for (int i = 0; i < len; i++) {
            if (sService.equals(m_serviceNames[i])) {
                return true;
            }
        }
        return false;
    }

    public String[] getSupportedServiceNames() {
        return m_serviceNames;
    }

    // com.sun.star.frame.XDispatchProvider:
    public com.sun.star.frame.XDispatch queryDispatch(com.sun.star.util.URL aURL,
            String sTargetFrameName,
            int iSearchFlags) {
        if (aURL.Protocol.compareTo("odfdom.example4.OpenDocumentExport:") == 0) {
            if (aURL.Path.compareTo("Export") == 0) {
                return this;
            }
        }
        return null;
    }

    // com.sun.star.frame.XDispatchProvider:
    public com.sun.star.frame.XDispatch[] queryDispatches(
            com.sun.star.frame.DispatchDescriptor[] seqDescriptors) {
        int nCount = seqDescriptors.length;
        com.sun.star.frame.XDispatch[] seqDispatcher =
                new com.sun.star.frame.XDispatch[seqDescriptors.length];

        for (int i = 0; i < nCount; ++i) {
            seqDispatcher[i] = queryDispatch(seqDescriptors[i].FeatureURL,
                    seqDescriptors[i].FrameName,
                    seqDescriptors[i].SearchFlags);
        }
        return seqDispatcher;
    }

    // com.sun.star.lang.XInitialization:
    public void initialize(Object[] object)
            throws com.sun.star.uno.Exception {
        if (object.length > 0) {
            m_xFrame = (com.sun.star.frame.XFrame) UnoRuntime.queryInterface(
                    com.sun.star.frame.XFrame.class, object[0]);
        }
    }

    // com.sun.star.frame.XDispatch:
    public void dispatch(com.sun.star.util.URL aURL,
            com.sun.star.beans.PropertyValue[] aArguments) {
        if (aURL.Protocol.compareTo("odfdom.example4.OpenDocumentExport:") == 0) {
            if (aURL.Path.compareTo("Export") == 0) {
                try {
                    exportFile();
                } catch (com.sun.star.io.IOException ex) {
                    Logger.getLogger(OpenDocumentExport.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    Logger.getLogger(OpenDocumentExport.class.getName()).log(Level.SEVERE, null, ex);
                } catch (Exception ex) {
                    Logger.getLogger(OpenDocumentExport.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    public void addStatusListener(com.sun.star.frame.XStatusListener xControl,
            com.sun.star.util.URL aURL) {
        // add your own code here
    }

    public void removeStatusListener(com.sun.star.frame.XStatusListener xControl,
            com.sun.star.util.URL aURL) {
        // add your own code here
    }

    private void exportFile() throws IOException, com.sun.star.io.IOException, Exception {
        ////////////////////////////////////////////////////////////////
        // OpenOffice.org API handling
        // 0 save file first
        XModel xModel = m_xFrame.getController().getModel();
        XStorable xStorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, xModel);
        File file = File.createTempFile("temp", ".ods");
        String url = getUrl(file);
        LOG.info("Saving a temporary ODS document to " + file.getAbsolutePath());
        xStorable.storeToURL(url, new PropertyValue[0]);

        String csvFilePath = LAB_ROOT + "/output.csv";
        LOG.info("Creating output file " + csvFilePath);
        FileWriter exportFileName = new FileWriter(csvFilePath);

		// CHALLENGE!
        //
        // exportFileWriter.close();

        //* 6 Free resources (delete temporary file)
        LOG.info("Removing the temporary ODS document " + file.getAbsolutePath());
        file.delete();

    }

    /** Returns the ODF table row as CSV row appended to the provided stringBuilder (second parameter)*/
    private StringBuilder getRowAsCSV(OdfTableRow row, StringBuilder csvCache) {
        if (csvCache == null) {
            csvCache = new StringBuilder();
        }
        LOG.info("Transforming an ODF Row to CSV!!!");
        NodeList tableRowChilds = row.getChildNodes();
        int rowNumber = tableRowChilds.getLength();
        for (int j = 0; j < rowNumber; ++j) {
            csvCache = getCellAsCSV((OdfTableCell) tableRowChilds.item(j), csvCache, (j + 1) == rowNumber);
        }
        // At the end of a line set a ';' and line break
        return csvCache.append(ROW_DELIMITER_CHAR + "\n");
    }

    /** Returns the ODF table row as CSV row appended to the provided stringBuilder (second parameter)*/
    private StringBuilder getCellAsCSV(OdfTableCell cell, StringBuilder csvCache, boolean isLastCell) {
        if (csvCache == null) {
            csvCache = new StringBuilder();
        }
        String cellContent = cell.getTextContent();
        int cellRepeat = cell.getNumberColumnsRepeated().intValue();
        if (cellRepeat > 1) {
            LOG.info("The cell is repeated " + cellRepeat + " times!");
            for (int i = 1; i <= cellRepeat; i++) {
                csvCache.append(cellContent);
                if (!isLastCell ||  i != cellRepeat) {
                    csvCache.append(CELL_DELIMITER_CHAR);
                }
            }
            csvCache.append(cellContent);
        } else {
            csvCache.append(cellContent);
            if (!isLastCell) {
                csvCache.append(CELL_DELIMITER_CHAR);
            }
        }
        return csvCache;
    }

    private String getUrl(File file) {
        String path = file.getPath().replace('\\', '/');
        if (!path.startsWith("/")) {
            path = "/".concat(path);
        }
        path = "file://".concat(path);
        return path;
    }
}
