
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#ifndef SkAnimator_DEFINED
#define SkAnimator_DEFINED

#include "SkScalar.h"
#include "SkKey.h"
#include "SkEventSink.h"

class SkAnimateMaker;
class SkCanvas;
class SkDisplayable;
class SkEvent;
class SkExtras;
struct SkMemberInfo;
class SkPaint;
struct SkRect;
class SkStream;
class SkTypedArray;
class SkXMLParserError;
class SkDOM;
struct SkDOMNode;

/** SkElementType is the type of element: a rectangle, a color, an animator, and so on.
    This enum is incomplete and will be fleshed out in a future release */
enum SkElementType {
    kElementDummyType
};
/** SkFieldType is the type of field: a scalar, a string, an integer, a boolean, and so on.
    This enum is incomplete and will be fleshed out in a future release */
enum SkFieldType {
    kFieldDummyType
};

/** \class SkAnimator

    The SkAnimator class decodes an XML stream into a display list. The
    display list can be drawn statically as a picture, or can drawn
    different elements at different times to form a moving animation.

    SkAnimator does not read the system time on its own; it relies on the
    caller to pass the current time. The caller can pause, speed up, or
    reverse the animation by varying the time passed in.

    The XML describing the display list must conform to the schema
    described by SkAnimateSchema.xsd.

    The XML must contain an <event> element to draw. Usually, it contains
    an <event kind="onload" /> block to add some drawing elements to the
    display list when the document is first decoded.

    Here's an "Hello World" XML sample:

    <screenplay>
        <event kind="onload" >
            <text text="Hello World" y="20" />
        </event>
    </screenplay>

    To read and draw this sample:

        // choose one of these two
        SkAnimator animator; // declare an animator instance on the stack
    //  SkAnimator* animator = new SkAnimator() // or one could instantiate the class

        // choose one of these three
        animator.decodeMemory(buffer, size); // to read from RAM
        animator.decodeStream(stream); // to read from a user-defined stream (e.g., a zip file)
        animator.decodeURI(filename); // to read from a web location, or from a local text file

        // to draw to the current window:
        SkCanvas canvas(getBitmap()); // create a canvas
        animator.draw(canvas, &paint, 0); // draw the scene
*/
class SkAnimator : public SkEventSink {
public:
    SkAnimator();
    virtual ~SkAnimator();

    /** Add a drawable extension to the graphics engine. Experimental.
        @param extras A derived class that implements methods that identify and instantiate the class
    */
    void addExtras(SkExtras* extras);

    /** Read in XML from a stream, and append it to the current
        animator. Returns false if an error was encountered.
        Error diagnostics are stored in fErrorCode and fLineNumber.
        @param stream  The stream to append.
        @return true if the XML was parsed successfully.
    */
    bool appendStream(SkStream* stream);

    /** Read in XML from memory. Returns true if the file can be
        read without error. Returns false if an error was encountered.
        Error diagnostics are stored in fErrorCode and fLineNumber.
        @param buffer  The XML text as UTF-8 characters.
        @param size  The XML text length in bytes.
        @return true if the XML was parsed successfully.
    */
    bool decodeMemory(const void* buffer, size_t size);

    /** Read in XML from a stream. Returns true if the file can be
        read without error. Returns false if an error was encountered.
        Error diagnostics are stored in fErrorCode and fLineNumber.
        @param stream  The stream containg the XML text as UTF-8 characters.
        @return true if the XML was parsed successfully.
    */
    virtual bool decodeStream(SkStream* stream);

    /** Parse the DOM tree starting at the specified node. Returns true if it can be
        parsed without error. Returns false if an error was encountered.
        Error diagnostics are stored in fErrorCode and fLineNumber.
        @return true if the DOM was parsed successfully.
    */
    virtual bool decodeDOM(const SkDOM&, const SkDOMNode*);

    /** Read in XML from a URI. Returns true if the file can be
        read without error. Returns false if an error was encountered.
        Error diagnostics are stored in fErrorCode and fLineNumber.
        @param uri The complete url path to be read (either ftp, http or https).
        @return true if the XML was parsed successfully.
    */
    bool decodeURI(const char uri[]);

    /** Pass a char event, usually a keyboard symbol, to the animator.
        This triggers events of the form <event kind="keyChar" key="... />
        @param ch  The character to match against <event> element "key"
            attributes.
        @return true if the event was dispatched successfully.
    */
    bool doCharEvent(SkUnichar ch);

    /** Experimental:
        Pass a mouse click event along with the mouse coordinates to
        the animator. This triggers events of the form <event kind="mouseDown" ... />
        and other mouse events.
        @param state The mouse state, described by SkView::Click::State : values are
        down == 0, moved == 1, up == 2
        @param x    The x-position of the mouse
        @param y The y-position of the mouse
        @return true if the event was dispatched successfully.
    */
    bool doClickEvent(int state, SkScalar x, SkScalar y);

    /** Pass a meta-key event, such as an arrow , to the animator.
        This triggers events of the form <event kind="keyPress" code="... />
        @param code  The key to match against <event> element "code"
            attributes.
        @return true if the event was dispatched successfully.
    */
    bool doKeyEvent(SkKey code);
    bool doKeyUpEvent(SkKey code);

    /** Send an event to the animator. The animator's clock is set
        relative to the current time.
        @return true if the event was dispatched successfully.
    */
    bool doUserEvent(const SkEvent& evt);

    /** The possible results from the draw function.
    */
    enum DifferenceType {
        kNotDifferent,
        kDifferent,
        kPartiallyDifferent
    };
    /** Draws one frame of the animation. The first call to draw always
        draws the initial frame of the animation. Subsequent calls draw
        the offset into the animation by
        subtracting the initial time from the current time.
        @param canvas  The canvas to draw into.
        @param paint     The paint to draw with.
        @param time  The offset into the current animation.
        @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and
        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal
        redraw area.
    */
    DifferenceType draw(SkCanvas* canvas, SkPaint* paint, SkMSec time);

    /** Draws one frame of the animation, using a new Paint each time.
        The first call to draw always
        draws the initial frame of the animation. Subsequent calls draw
        the offset into the animation by
        subtracting the initial time from the current time.
        @param canvas  The canvas to draw into.
        @param time  The offset into the current animation.
        @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and
        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal
        redraw area.
    */
    DifferenceType draw(SkCanvas* canvas, SkMSec time);

    /** Experimental:
        Helper to choose whether to return a SkView::Click handler.
        @param x ignored
        @param y ignored
        @return true if a mouseDown event handler is enabled.
    */
    bool findClickEvent(SkScalar x, SkScalar y);


    /** Get the nested animator associated with this element, if any.
        Use this to access a movie's event sink, to send events to movies.
        @param element the value returned by getElement
        @return the internal animator.
    */
    const SkAnimator* getAnimator(const SkDisplayable* element) const;

    /** Returns the scalar value of the specified element's attribute[index]
        @param element the value returned by getElement
        @param field the value returned by getField
        @param index the array entry
        @return the integer value to retrieve, or SK_NaN32 if unsuccessful
    */
    int32_t getArrayInt(const SkDisplayable* element, const SkMemberInfo* field, int index);

    /** Returns the scalar value of the specified element's attribute[index]
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param index the array entry
        @return the integer value to retrieve, or SK_NaN32 if unsuccessful
    */
    int32_t getArrayInt(const char* elementID, const char* fieldName, int index);

    /** Returns the scalar value of the specified element's attribute[index]
        @param element the value returned by getElement
        @param field the value returned by getField
        @param index the array entry
        @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful
    */
    SkScalar getArrayScalar(const SkDisplayable* element, const SkMemberInfo* field, int index);

    /** Returns the scalar value of the specified element's attribute[index]
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param index the array entry
        @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful
    */
    SkScalar getArrayScalar(const char* elementID, const char* fieldName, int index);

    /** Returns the string value of the specified element's attribute[index]
        @param element is a value returned by getElement
        @param field is a value returned by getField
        @param index the array entry
        @return the string value to retrieve, or null if unsuccessful
    */
    const char* getArrayString(const SkDisplayable* element, const SkMemberInfo* field, int index);

    /** Returns the string value of the specified element's attribute[index]
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param index the array entry
        @return the string value to retrieve, or null if unsuccessful
    */
    const char* getArrayString(const char* elementID, const char* fieldName, int index);

    /** Returns the XML element corresponding to the given ID.
        @param elementID is the value of the id attribute in the XML of this element
        @return the element matching the ID, or null if the element can't be found
    */
    const SkDisplayable* getElement(const char* elementID);

    /** Returns the element type corresponding to the XML element.
        The element type matches the element name; for instance, <line> returns kElement_LineType
        @param element is a value returned by getElement
        @return element type, or 0 if the element can't be found
    */
    SkElementType getElementType(const SkDisplayable* element);

    /** Returns the element type corresponding to the given ID.
        @param elementID is the value of the id attribute in the XML of this element
        @return element type, or 0 if the element can't be found
    */
    SkElementType getElementType(const char* elementID);

    /** Returns the XML field of the named attribute in the XML element.
        @param element is a value returned by getElement
        @param fieldName is the attribute to return
        @return the attribute matching the fieldName, or null if the element can't be found
    */
    const SkMemberInfo* getField(const SkDisplayable* element, const char* fieldName);

    /** Returns the XML field of the named attribute in the XML element matching the elementID.
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName is the attribute to return
        @return the attribute matching the fieldName, or null if the element can't be found
    */
    const SkMemberInfo* getField(const char* elementID, const char* fieldName);

    /** Returns the value type coresponding to the element's attribute.
        The value type matches the XML schema: and may be kField_BooleanType, kField_ScalarType, etc.
        @param field is a value returned by getField
        @return the attribute type, or 0 if the element can't be found
    */
    SkFieldType getFieldType(const SkMemberInfo* field);

    /** Returns the value type coresponding to the element's attribute.
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @return the attribute type, or 0 if the element can't be found
    */
    SkFieldType getFieldType(const char* elementID, const char* fieldName);

    /** Returns the recommended animation interval. Returns zero if no
        interval is specified.
    */
    SkMSec getInterval();

    /** Returns the partial rectangle to invalidate after drawing. Call after draw() returns
    kIsPartiallyDifferent to do a mimimal inval(). */
    void getInvalBounds(SkRect* inval);

    /** Returns the details of any error encountered while parsing the XML.
    */
    const SkXMLParserError* getParserError();

    /** Returns the details of any error encountered while parsing the XML as string.
    */
    const char* getParserErrorString();

    /** Returns the scalar value of the specified element's attribute
        @param element is a value returned by getElement
        @param field is a value returned by getField
        @return the integer value to retrieve, or SK_NaN32 if not found
    */
    int32_t getInt(const SkDisplayable* element, const SkMemberInfo* field);

    /** Returns the scalar value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @return the integer value to retrieve, or SK_NaN32 if not found
    */
    int32_t getInt(const char* elementID, const char* fieldName);

    /** Returns the scalar value of the specified element's attribute
        @param element is a value returned by getElement
        @param field is a value returned by getField
        @return the scalar value to retrieve, or SK_ScalarNaN if not found
    */
    SkScalar getScalar(const SkDisplayable* element, const SkMemberInfo* field);

    /** Returns the scalar value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @return the scalar value to retrieve, or SK_ScalarNaN if not found
    */
    SkScalar getScalar(const char* elementID, const char* fieldName);

    /** Returns the string value of the specified element's attribute
        @param element is a value returned by getElement
        @param field is a value returned by getField
        @return the string value to retrieve, or null if not found
    */
    const char* getString(const SkDisplayable* element, const SkMemberInfo* field);

    /** Returns the string value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @return the string value to retrieve, or null if not found
    */
    const char* getString(const char* elementID, const char* fieldName);

    /** Gets the file default directory of the URL base path set explicitly or by reading the last URL. */
    const char* getURIBase();

    /** Resets the animator to a newly created state with no animation data. */
    void initialize();

    /** Experimental. Resets any active animations so that the next time passed is treated as
        time zero. */
    void reset();

    /** Sets the scalar value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param array is the c-style array of integers
        @param count is the length of the array
        @return true if the value was set successfully
    */
    bool setArrayInt(const char* elementID, const char* fieldName, const int* array, int count);

    /** Sets the scalar value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param array is the c-style array of strings
        @param count is the length of the array
        @return true if the value was set successfully
    */
    bool setArrayString(const char* elementID, const char* fieldName, const char** array, int count);

    /** Sets the scalar value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param data the integer value to set
        @return true if the value was set successfully
    */
    bool setInt(const char* elementID, const char* fieldName, int32_t data);

    /** Sets the scalar value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param data the scalar value to set
        @return true if the value was set successfully
    */
    bool setScalar(const char* elementID, const char* fieldName, SkScalar data);

    /** Sets the string value of the specified element's attribute
        @param elementID is the value of the id attribute in the XML of this element
        @param fieldName specifies the name of the attribute
        @param data the string value to set
        @return true if the value was set successfully
    */
    bool setString(const char* elementID, const char* fieldName, const char* data);

    /** Sets the file default directory of the URL base path
        @param path the directory path
    */
    void setURIBase(const char* path);

    typedef void* Handler;
    // This guy needs to be exported to java, so don't make it virtual
    void setHostHandler(Handler handler) {
        this->onSetHostHandler(handler);
    }

    /** \class Timeline
    Returns current time to animator. To return a custom timeline, create a child
    class and override the getMSecs method.
    */
    class Timeline {
    public:
        virtual ~Timeline() {}

        /** Returns the current time in milliseconds */
        virtual SkMSec getMSecs() const = 0;
    };

    /** Sets a user class to return the current time to the animator.
        Optional; if not called, the system clock will be used by calling SkTime::GetMSecs instead.
        @param callBack the time function
    */
    void setTimeline(const Timeline& );

    static void Init(bool runUnitTests);
    static void Term();

    /** The event sink events generated by the animation are posted to.
        Screenplay also posts an inval event to this event sink after processing an
        event to force a redraw.
        @param target the event sink id
    */
    void setHostEventSinkID(SkEventSinkID hostID);
    SkEventSinkID getHostEventSinkID() const;

    // helper
    void setHostEventSink(SkEventSink* sink) {
        this->setHostEventSinkID(sink ? sink->getSinkID() : 0);
    }

    virtual void setJavaOwner(Handler owner);

#ifdef SK_DEBUG
    virtual void eventDone(const SkEvent& evt);
    virtual bool isTrackingEvents();
    static bool NoLeaks();
#endif

protected:
    virtual void onSetHostHandler(Handler handler);
    virtual void onEventPost(SkEvent*, SkEventSinkID);
    virtual void onEventPostTime(SkEvent*, SkEventSinkID, SkMSec time);

private:
// helper functions for setters
    bool setArray(SkDisplayable* element, const SkMemberInfo* field, SkTypedArray array);
    bool setArray(const char* elementID, const char* fieldName, SkTypedArray array);
    bool setInt(SkDisplayable* element, const SkMemberInfo* field, int32_t data);
    bool setScalar(SkDisplayable* element, const SkMemberInfo* field, SkScalar data);
    bool setString(SkDisplayable* element, const SkMemberInfo* field, const char* data);

    virtual bool onEvent(const SkEvent&);
    SkAnimateMaker* fMaker;
    friend class SkAnimateMaker;
    friend class SkAnimatorScript;
    friend class SkAnimatorScript2;
    friend class SkApply;
    friend class SkDisplayMovie;
    friend class SkDisplayType;
    friend class SkPost;
    friend class SkXMLAnimatorWriter;
};

#endif
