/*
 * Copyright (C) 2009 Matt Oswald
 * Copyright (c) 2009-2010 Ricardo Quesada
 * Copyright (c) 2010-2012 cocos2d-x.org
 * Copyright (c) 2011      Zynga Inc.
 * Copyright (c) 2011      Marco Tillemans
 * Copyright (c) 2013-2016 Chukong Technologies Inc.
 * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
 *
 * https://axmolengine.github.io/
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */
#pragma once

#include "2d/CCNode.h"
#include "base/CCProtocols.h"
#include "renderer/CCCustomCommand.h"

NS_AX_BEGIN

class Texture2D;
class TextureAtlas;
class ParticleSystem;

/**
 * @addtogroup _2d
 * @{
 */

#define kParticleDefaultCapacity 500

/** @class ParticleBatchNode
 * @brief ParticleBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call
 * (often known as "batch draw").
 *
 * A ParticleBatchNode can reference one and only one texture (one image file, one texture atlas).
 * Only the ParticleSystems that are contained in that texture can be added to the SpriteBatchNode.
 * All ParticleSystems added to a SpriteBatchNode are drawn in one OpenGL ES draw call.
 * If the ParticleSystems are not added to a ParticleBatchNode then an OpenGL ES draw call will be needed for each one,
 * which is less efficient.
 *
 *
 * Limitations:
 * - At the moment only ParticleSystemQuad is supported
 * - All systems need to be drawn with the same parameters, blend function, aliasing, texture
 *
 * Most efficient usage
 * - Initialize the ParticleBatchNode with the texture and enough capacity for all the particle systems
 * - Initialize all particle systems and add them as child to the batch node
 * @since v1.1
 */

class AX_DLL ParticleBatchNode : public Node, public TextureProtocol
{
public:
    /** Create the particle system with Texture2D, a capacity of particles, which particle system to use.
     *
     * @param tex A given texture.
     * @param capacity A capacity of particles.
     * @return An autoreleased ParticleBatchNode object.
     * @js NA
     */
    static ParticleBatchNode* createWithTexture(Texture2D* tex, int capacity = kParticleDefaultCapacity);

    /** Create the particle system with the name of a file on disk (for a list of supported formats look at the
     * Texture2D class), a capacity of particles.
     *
     * @param fileImage A given file name.
     * @param capacity A capacity of particles.
     * @return An autoreleased ParticleBatchNode object.
     */
    static ParticleBatchNode* create(std::string_view fileImage, int capacity = kParticleDefaultCapacity);

    /** Inserts a child into the ParticleBatchNode.
     *
     * @param system A given particle system.
     * @param index The insert index.
     */
    void insertChild(ParticleSystem* system, int index);

    /** Remove a child of the ParticleBatchNode.
     *
     * @param index The index of the child.
     * @param doCleanup True if all actions and callbacks on this node should be removed, false otherwise.
     */
    void removeChildAtIndex(int index, bool doCleanup);
    void removeAllChildrenWithCleanup(bool doCleanup) override;

    /** Disables a particle by inserting a 0'd quad into the texture atlas.
     *
     * @param particleIndex The index of the particle.
     */
    void disableParticle(int particleIndex);

    /** Gets the texture atlas used for drawing the quads.
     *
     * @return The texture atlas used for drawing the quads.
     */
    TextureAtlas* getTextureAtlas() const { return _textureAtlas; }

    /** Sets the texture atlas used for drawing the quads.
     *
     * @param atlas The texture atlas used for drawing the quads.
     */
    void setTextureAtlas(TextureAtlas* atlas) { _textureAtlas = atlas; }

    // Overrides
    virtual void visit(Renderer* renderer, const Mat4& parentTransform, uint32_t parentFlags) override;

    using Node::addChild;
    virtual void addChild(Node* child, int zOrder, int tag) override;
    virtual void addChild(Node* child, int zOrder, std::string_view name) override;
    virtual void removeChild(Node* child, bool cleanup) override;
    virtual void reorderChild(Node* child, int zOrder) override;
    virtual void draw(Renderer* renderer, const Mat4& transform, uint32_t flags) override;
    virtual Texture2D* getTexture() const override;
    virtual void setTexture(Texture2D* texture) override;
    /**
     * @code
     * When this function bound into js or lua,the parameter will be changed
     * In js: var setBlendFunc(var src, var dst)
     * @endcode
     * @lua NA
     */
    virtual void setBlendFunc(const BlendFunc& blendFunc) override;
    /**
     * @js NA
     * @lua NA
     */
    virtual const BlendFunc& getBlendFunc() const override;

    /**
     * @js ctor
     */
    ParticleBatchNode();
    /**
     * @js NA
     * @lua NA
     */
    virtual ~ParticleBatchNode();

    /** initializes the particle system with Texture2D, a capacity of particles */
    bool initWithTexture(Texture2D* tex, int capacity);

    /** initializes the particle system with the name of a file on disk (for a list of supported formats look at the
     * Texture2D class), a capacity of particles */
    bool initWithFile(std::string_view fileImage, int capacity);

private:
    void updateAllAtlasIndexes();
    void increaseAtlasCapacityTo(ssize_t quantity);
    int searchNewPositionInChildrenForZ(int z);
    void getCurrentIndex(int* oldIndex, int* newIndex, Node* child, int z);
    int addChildHelper(ParticleSystem* child, int z, int aTag, std::string_view name, bool setTag);
    void addChildByTagOrName(ParticleSystem* child, int z, int tag, std::string_view name, bool setTag);
    void updateBlendFunc();

    void updateProgramStateTexture();

    /** the texture atlas used for drawing the quads */
    TextureAtlas* _textureAtlas = nullptr;

    /** the blend function used for drawing the quads */
    BlendFunc _blendFunc;

    CustomCommand _customCommand;

    backend::UniformLocation _mvpMatrixLocaiton;
    backend::UniformLocation _textureLocation;
};

// end of _2d group
/// @}

NS_AX_END