// Copyright (C) 2004-2021 Artifex Software, Inc.
//
// This file is part of MuPDF.
//
// MuPDF is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// MuPDF 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 Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
//
// Alternative licensing terms are available from the licensor.
// For commercial licensing, see <https://www.artifex.com/> or contact
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
// CA 94129, USA, for further information.

/**
 * Mu Office Library
 *
 * This helper layer provides an API for loading, and displaying
 * a file. It is deliberately as identical as possible to the
 * smart-office-lib.h header file in Smart Office to facilitate
 * a product which can use both Smart Office and MuPDF.
 */

#ifndef MU_OFFICE_LIB_H
#define MU_OFFICE_LIB_H

/*
 * General use
 *
 * This library uses threads but is not thread safe for the caller. All
 * calls should be made from a single thread. Some calls allow the user
 * to arrange for their own functions to be called back from the library;
 * often the users functions will be called back from a different thread. If
 * a call into the library is required in response to information from a
 * callback, it is the responsibility of the user to arrange for those
 * library calls to be made on the same thread as for other library calls.
 * Calls back into the library from a callback are not permitted.
 *
 * There are two main modes of use. In interactive windowing systems, it
 * is usual for the app developers code to be called on the main UI thread.
 * It is from the UI thread that all library calls should be made. Such
 * systems usually provide a way to post a call onto the UI thread. That
 * is the best way to respond to a callback from the library.
 *
 * The other mode of use is for plain executables that might wish to
 * (say) generate images of all the pages of a document. This can
 * be achieved, without use of callbacks, using the few synchronous
 * calls. E.g., MuOfficeDoc_getNumPages will wait for background document
 * loading to complete before returning the total number of pages and
 * MuOfficeRender_destroy will wait for background rendering to complete
 * before returning.
 */

#include <stddef.h> /* For size_t */
#include "mupdf/fitz.h" /* For fz_context/fz_document/fz_page */

/** Error type returned from most MuOffice functions
 *
 * 0 means no error
 *
 * non-zero values mean an error occurred. The exact value is an indication
 * of what went wrong and should be included in bug reports or support
 * queries. Library users should not test this value except for 0,
 * non-zero and any explicitly documented values.
 */
typedef int MuError;

/** Errors returned to MuOfficeLoadingErrorFn
 *
 * Other values may also be returned.
 */
typedef enum MuOfficeDocErrorType
{
	MuOfficeDocErrorType_NoError = 0,
	MuOfficeDocErrorType_UnsupportedDocumentType = 1,
	MuOfficeDocErrorType_EmptyDocument = 2,
	MuOfficeDocErrorType_UnableToLoadDocument = 4,
	MuOfficeDocErrorType_UnsupportedEncryption = 5,
	MuOfficeDocErrorType_Aborted = 6,
	MuOfficeDocErrorType_OutOfMemory = 7,

	/* FIXME: Additional ones that should be backported to
	 * smart-office-lib.h */
	MuOfficeDocErrorType_IllegalArgument = 8,

	/** A password is required to open this document.
	 *
	 * The app should provide it using MuOffice_providePassword
	 * or if it doesn't want to proceed call MuOfficeDoc_destroy or
	 * MuOfficeDoc_abortLoad.
	 */
	MuOfficeDocErrorType_PasswordRequest = 0x1000
} MuOfficeDocErrorType;

/**
 *Structure holding the detail of the layout of a bitmap. b5g6r5 is assumed.
 */
typedef struct
{
	void *memptr;
	int   width;
	int   height;
	int   lineSkip;
} MuOfficeBitmap;

/**
 * Structure defining a point
 *
 *    x           x coord of point
 *    y           y coord of point
 */
typedef struct
{
	float x;
	float y;
} MuOfficePoint;

/**
 * Structure defining a rectangle
 *
 *    x           x coord of top left of area within the page
 *    y           y coord of top left of area within the page
 *    width       width of area
 *    height      height of area
 */
typedef struct
{
	float x;
	float y;
	float width;
	float height;
} MuOfficeBox;

typedef enum MuOfficePointType
{
	MuOfficePointType_MoveTo,
	MuOfficePointType_LineTo
} MuOfficePointType;

typedef struct
{
	float x;
	float y;
	MuOfficePointType type;
} MuOfficePathPoint;

/**
 * Structure defining what area of a page should be rendered and to what
 * area of the bitmap
 *
 *    origin            coordinates of the document origin within the bitmap
 *    renderArea        the part of the bitmap to which to render
 */
typedef struct
{
	MuOfficePoint   origin;
	MuOfficeBox     renderArea;
} MuOfficeRenderArea;

typedef struct MuOfficeLib MuOfficeLib;
typedef struct MuOfficeDoc MuOfficeDoc;
typedef struct MuOfficePage MuOfficePage;
typedef struct MuOfficeRender MuOfficeRender;

/**
 * Allocator function used by some functions to get blocks of memory.
 *
 * @param cookie     data pointer passed in with the allocator.
 * @param size       the size of the required block.
 *
 * @returns as for malloc. (NULL implies OutOfMemory, or size == 0).
 * Otherwise a pointer to an allocated block.
 */
typedef void *(MuOfficeAllocFn)(void   *cookie,
				size_t  size);

/**
 * Callback function monitoring document loading
 *
 * Also called when the document is edited, either adding or
 * removing pages, with the pagesLoaded value decreasing
 * in the page-removal case.
 *
 * @param cookie        the data pointer that was originally passed
 *                      to MuOfficeLib_loadDocument.
 * @param pagesLoaded   the number of pages so far discovered.
 * @param complete      whether loading has completed. If this flag
 *                      is set, pagesLoaded is the actual number of
 *                      pages in the document.
 */
typedef void (MuOfficeLoadingProgressFn)(void *cookie,
					int   pagesLoaded,
					int   complete);

/**
 * Callback function used to monitor errors in the process of loading
 * a document.
 *
 * @param cookie        the data pointer that was originally passed
 *                      to MuOfficeLib_loadDocument.
 * @param error         the error being reported
 */
typedef void (MuOfficeLoadingErrorFn)(	void                 *cookie,
					MuOfficeDocErrorType  error);

/**
 * Callback function used to monitor page changes
 *
 * @param cookie        the data pointer that was originally passed
 *                      to MuOfficeDoc_getPage.
 * @param area          the area that has changed.
 */
typedef void (MuOfficePageUpdateFn)(	void                 *cookie,
					const MuOfficeBox *area);

/**
 * Callback function used to monitor a background render of a
 * document page. The function is called exactly once.
 *
 * @param cookie        the data pointer that was originally passed
 *                      to MuOfficeDoc_monitorRenderProgress.
 * @param error         error returned from the rendering process
 */
typedef void (MuOfficeRenderProgressFn)(void    *cookie,
					MuError  error);

/**
 * Document types
 *
 * Keep in sync with smart-office-lib.h
 */
typedef enum
{
	MuOfficeDocType_PDF,
	MuOfficeDocType_XPS,
	MuOfficeDocType_IMG
} MuOfficeDocType;

/**
 * The possible results of a save operation
 */
typedef enum MuOfficeSaveResult
{
	MuOfficeSave_Succeeded,
	MuOfficeSave_Error,
	MuOfficeSave_Cancelled
}
MuOfficeSaveResult;

/**
 * Callback function used to monitor save operations.
 *
 * @param cookie        the data pointer that was originally passed to
 *                      MuOfficeDoc_save.
 * @param result        the result of the save operation
 */
typedef void (MuOfficeSaveResultFn)(	void                 *cookie,
					MuOfficeSaveResult result);

/**
 * Create a MuOfficeLib instance.
 *
 * @param pMu       address of variable to
 *                  receive the created instance
 *
 * @return          error indication - 0 for success
 */
MuError MuOfficeLib_create(MuOfficeLib **pMu);

/**
 * Destroy a MuOfficeLib instance
 *
 * @param mu        the instance to destroy
 */
void MuOfficeLib_destroy(MuOfficeLib *mu);

/**
 * Find the type of a file given its filename extension.
 *
 * @param path      path to the file (in utf8)
 *
 * @return          a valid MuOfficeDocType value, or MuOfficeDocType_Other
 */
MuOfficeDocType MuOfficeLib_getDocTypeFromFileExtension(const char *path);

/**
 * Return a list of filename extensions supported by Mu Office library.
 *
 * @return    comma-delimited list of extensions, without the leading ".".
 *            The caller should free the returned pointer..
 */
char * MuOfficeLib_getSupportedFileExtensions(void);

/**
 * Load a document
 *
 * Call will return immediately, leaving the document loading
 * in the background
 *
 * @param mu         a MuOfficeLib instance
 * @param path       path to the file to load (in utf8)
 * @param progressFn callback for monitoring progress
 * @param errorFn    callback for monitoring errors
 * @param cookie     a pointer to pass back to the callbacks
 * @param pDoc       address for return of a MuOfficeDoc object
 *
 * @return          error indication - 0 for success
 *
 * The progress callback may be called several times, with increasing
 * values of pagesLoaded. Unless MuOfficeDoc_destroy is called,
 * before loading completes, a call with "completed" set to true
 * is guaranteed.
 *
 * Once MuOfficeDoc_destroy is called there will be no
 * further callbacks.
 *
 * Alternatively, in a synchronous context, MuOfficeDoc_getNumPages
 * can be called to wait for loading to complete and return the total
 * number of pages. In this mode of use, progressFn can be NULL. 
 */
MuError MuOfficeLib_loadDocument(MuOfficeLib              *mu,
				const char                *path,
				MuOfficeLoadingProgressFn *progressFn,
				MuOfficeLoadingErrorFn    *errorFn,
				void                      *cookie,
				MuOfficeDoc              **pDoc);

/**
 * Perform MuPDF native operations on a given MuOfficeLib
 * instance.
 *
 * The function is called with a fz_context value that can
 * be safely used (i.e. the context is cloned/dropped
 * appropriately around the call). The function should signal
 * errors by fz_throw-ing.
 *
 * @param mu           the MuOfficeLib instance.
 * @param fn           the function to call to run the operations.
 * @param arg          Opaque data pointer.
 *
 * @return             error indication - 0 for success
 */
MuError MuOfficeLib_run(MuOfficeLib *mu, void (*fn)(fz_context *ctx, void *arg), void *arg);

/**
 * Provide the password for a document
 *
 * This function should be called to provide a password with a document
 * error if MuOfficeError_PasswordRequired is received.
 *
 * If a password is requested again, this means the password was incorrect.
 *
 * @param doc         the document object
 * @param password    the password (UTF8 encoded)
 * @return            error indication - 0 for success
 */
int MuOfficeDoc_providePassword(MuOfficeDoc *doc, const char *password);

/**
 * Return the type of an open document
 *
 * @param doc         the document object
 *
 * @return            the document type
 */
MuOfficeDocType MuOfficeDoc_docType(MuOfficeDoc *doc);

/**
 * Return the number of pages of a document
 *
 * This function waits for document loading to complete before returning
 * the result. It may block the calling thread for a significant period of
 * time. To avoid blocking, this call should be avoided in favour of using
 * the MuOfficeLib_loadDocument callbacks to monitor loading.
 *
 * If background loading fails, the associated error will be returned
 * from this call.
 *
 * @param doc         the document
 * @param pNumPages   address for return of the number of pages
 *
 * @return            error indication - 0 for success
 */
MuError MuOfficeDoc_getNumPages(MuOfficeDoc *doc, int *pNumPages);

/**
 * Determine if the document has been modified
 *
 * @param doc         the document
 *
 * @return            modified flag
 */
int MuOfficeDoc_hasBeenModified(MuOfficeDoc *doc);

/**
 * Start a save operation
 *
 * @param doc         the document
 * @param path        path of the file to which to save
 * @param resultFn    callback used to report completion
 * @param cookie      a pointer to pass to the callback
 *
 * @return            error indication - 0 for success
 */
MuError MuOfficeDoc_save(MuOfficeDoc         *doc,
			const char           *path,
			MuOfficeSaveResultFn *resultFn,
			void                 *cookie);

/**
 * Stop a document loading. The document is not destroyed, but
 * no further content will be read from the file.
 *
 * @param doc       the MuOfficeDoc object
 */
void MuOfficeDoc_abortLoad(MuOfficeDoc *doc);

/**
 * Destroy a MuOfficeDoc object. Loading of the document is shutdown
 * and no further callbacks will be issued for the specified object.
 *
 * @param doc       the MuOfficeDoc object
 */
void MuOfficeDoc_destroy(MuOfficeDoc *doc);

/**
 * Get a page of a document
 *
 * @param doc          the document object
 * @param pageNumber   the number of the page to load (lying in the
 *                     range 0 to one less than the number of pages)
 * @param updateFn     Function to be called back when the page updates
 * @param cookie       Opaque value to pass for any updates
 * @param pPage        Address for return of the page object
 *
 * @return             error indication - 0 for success
 */
MuError MuOfficeDoc_getPage(	MuOfficeDoc          *doc,
				int                   pageNumber,
				MuOfficePageUpdateFn *updateFn,
				void                 *cookie,
				MuOfficePage        **pPage);

/**
 * Perform MuPDF native operations on a given document.
 *
 * The function is called with fz_context and fz_document
 * values that can be safely used (i.e. the context is
 * cloned/dropped appropriately around the function, and
 * locking is used to ensure that no other threads are
 * simultaneously using the document). Functions can
 * signal errors by fz_throw-ing.
 *
 * Due to the locking, it is best to ensure that as little
 * time is taken here as possible (i.e. if you fetch some
 * data and then spend a long time processing it, it is
 * probably best to fetch the data using MuOfficeDoc_run
 * and then process it outside). This avoids potentially
 * blocking the UI.
 *
 * @param doc          the document object.
 * @param fn           the function to call with fz_context/fz_document
 *                     values.
 * @param arg          Opaque data pointer.
 *
 * @return             error indication - 0 for success
 */
MuError MuOfficeDoc_run(MuOfficeDoc *doc, void (*fn)(fz_context *ctx, fz_document *doc, void *arg), void *arg);

/**
 * Destroy a page object
 *
 * Note this does not delete or remove the page from the document.
 * It simply destroys the page object which is merely a reference
 * to the page.
 *
 * @param page         the page object
 */
void MuOfficePage_destroy(MuOfficePage *page);

/**
 * Get the size of a page in pixels
 *
 * This returns the size of the page in pixels. Pages can be rendered
 * with a zoom factor. The returned value is the size of bitmap
 * appropriate for rendering with a zoom of 1.0 and corresponds to
 * 90 dpi. The returned values are not necessarily whole numbers.
 *
 * @param page         the page object
 * @param pWidth       address for return of the width
 * @param pHeight      address for return of the height
 *
 * @return             error indication - 0 for success
 */
MuError MuOfficePage_getSize(	MuOfficePage *page,
				float        *pWidth,
				float        *pHeight);

/**
 * Return the zoom factors necessary to render at to a given
 * size in pixels. (deprecated)
 *
 * @param page         the page object
 * @param width        the desired width
 * @param height       the desired height
 * @param pXZoom       Address for return of zoom necessary to fit width
 * @param pYZoom       Address for return of zoom necessary to fit height
 *
 * @return             error indication - 0 for success
 */
MuError MuOfficePage_calculateZoom(	MuOfficePage *page,
					int           width,
					int           height,
					float        *pXZoom,
					float        *pYZoom);

/**
 * Get the size of a page in pixels for a specified zoom factor
 * (deprecated)
 *
 * This returns the size of bitmap that should be used to display
 * the entire page at the given zoom factor. A zoom of 1.0
 * corresponds to 90 dpi.
 *
 * @param page         the page object
 * @param zoom         the zoom factor
 * @param pWidth       address for return of the width
 * @param pHeight      address for return of the height
 *
 * @return             error indication - 0 for success
 */
MuError MuOfficePage_getSizeForZoom(	MuOfficePage *page,
					float         zoom,
					int          *pWidth,
					int          *pHeight);

/**
 * Perform MuPDF native operations on a given page.
 *
 * The function is called with fz_context and fz_page
 * values that can be safely used (i.e. the context is
 * cloned/dropped appropriately around the function, and
 * locking is used to ensure that no other threads are
 * simultaneously using the document). Functions can
 * signal errors by fz_throw-ing.
 *
 * Due to the locking, it is best to ensure that as little
 * time is taken here as possible (i.e. if you fetch some
 * data and then spend a long time processing it, it is
 * probably best to fetch the data using MuOfficePage_run
 * and then process it outside). This avoids potentially
 * blocking the UI.
 *
 * @param page         the page object.
 * @param fn           the function to call with fz_context/fz_document
 *                     values.
 * @param arg          Opaque data pointer.
 *
 * @return             error indication - 0 for success
 */
MuError MuOfficePage_run(MuOfficePage *page, void (*fn)(fz_context *ctx, fz_page *page, void *arg), void *arg);

/**
 * Schedule the rendering of an area of document page to
 * an area of a bitmap.
 *
 * The alignment between page and bitmap is defined by specifying
 * document's origin within the bitmap, possibly either positive or
 * negative. A render object is returned via which the process can
 * be monitored or terminated.
 *
 * The progress function is called exactly once per render in either
 * the success or failure case.
 *
 * Note that, since a render object represents a running thread that
 * needs access to the page, document, and library objects, it is important
 * to call MuOfficeRender_destroy, not only before using or deallocating
 * the bitmap, but also before calling MuOfficePage_destroy, etc..
 *
 * @param page              the page to render
 * @param zoom              the zoom factor
 * @param bitmap            the bitmap
 * @param area              area to render
 * @param progressFn        the progress callback function
 * @param cookie            a pointer to pass to the callback function
 * @param pRender           Address for return of the render object
 *
 * @return                  error indication - 0 for success
 */
MuError MuOfficePage_render(	MuOfficePage             *page,
				float                     zoom,
			const	MuOfficeBitmap           *bitmap,
			const	MuOfficeRenderArea       *area,
				MuOfficeRenderProgressFn *progressFn,
				void                     *cookie,
				MuOfficeRender          **pRender);

/**
 * Destroy a render
 *
 * This call destroys a MuOfficeRender object, aborting any current
 * render.
 *
 * This call is intended to support an app dealing with a user quickly
 * flicking through document pages. A render may be scheduled but, before
 * completion, be found not to be needed. In that case the bitmap will
 * need to be reused, which requires any existing render to be aborted.
 * The call to MuOfficeRender_destroy will cut short the render and
 * allow the bitmap to be reused immediately.
 *
 * @note If an active render thread is destroyed, it will be aborted.
 * While fast, this is not an instant operation. For maximum
 * responsiveness, it is best to 'abort' as soon as you realise you
 * don't need the render, and to destroy when you get the callback.
 *
 * @param render           The render object
 */
void MuOfficeRender_destroy(MuOfficeRender *render);

/**
 * Abort a render
 *
 * This call aborts any rendering currently underway. The 'render
 * complete' callback (if any) given when the render was created will
 * still be called. If a render has completed, this call will have no
 * effect.
 *
 * This call will not block to wait for the render thread to stop, but
 * will cause it to stop as soon as it can in the background.
 *
 * @note It is important not to start any new render to the same bitmap
 * until the callback comes in (or until waitUntilComplete returns), as
 * otherwise you can have multiple renders drawing to the same bitmap
 * with unpredictable consequences.
 *
 * @param render           The render object to abort
 */
void MuOfficeRender_abort(MuOfficeRender *render);

/**
 * Wait for a render to complete.
 *
 * This call will not return until rendering is complete, so on return
 * the bitmap will contain the page image (assuming the render didn't
 * run into an error condition) and will not be used further by any
 * background processing. Any error during rendering will be returned
 * from this function.
 *
 * This call may block the calling thread for a significant period of
 * time. To avoid blocking, supply a progress-monitoring callback
 * function to MuOfficePage_render.
 *
 * @param render           The render object to destroy
 * @return render error condition - 0 for no error.
 */
MuError MuOfficeRender_waitUntilComplete(MuOfficeRender *render);

#endif /* SMART_OFFICE_LIB_H */
