
import { vec2 } from '../math/vec2';

/**
 * 所有形状的基类
 */
export class Shape {
    private static idCounter = 0;


    /**
     * The body this shape is attached to. A shape can only be attached to a single body.
     * @property {Body} body
     */
    body: any;
    /**
     * Body-local position of the shape.
     * @property {Array} position
     */
    position: Float32Array;
    /**
     * Body-local angle of the shape.
     * @property {number} angle
     */
    angle: number;
    /**
     * The type of the shape. One of:
     *
     * <ul>
     * <li><a href="Shape.html#property_CIRCLE">Shape.CIRCLE</a></li>
     * <li><a href="Shape.html#property_PARTICLE">Shape.PARTICLE</a></li>
     * <li><a href="Shape.html#property_PLANE">Shape.PLANE</a></li>
     * <li><a href="Shape.html#property_CONVEX">Shape.CONVEX</a></li>
     * <li><a href="Shape.html#property_LINE">Shape.LINE</a></li>
     * <li><a href="Shape.html#property_BOX">Shape.BOX</a></li>
     * <li><a href="Shape.html#property_CAPSULE">Shape.CAPSULE</a></li>
     * <li><a href="Shape.html#property_HEIGHTFIELD">Shape.HEIGHTFIELD</a></li>
     * </ul>
     *
     * @property {number} type
     */
    type: any;
    /**
     * Shape object identifier. Read only.
     * @readonly
     * @type {Number}
     * @property id
     */
    id: number;
    /**
     * Bounding circle radius of this shape
     * @readonly
     * @property boundingRadius
     * @type {Number}
     */
    boundingRadius: number;
    /**
     * Collision group that this shape belongs to (bit mask). See <a href="http://www.aurelienribon.com/blog/2011/07/box2d-tutorial-collision-filtering/">this tutorial</a>.
     * @property collisionGroup
     * @type {Number}
     * @example
     *     // Setup bits for each available group
     *     var PLAYER = Math.pow(2,0),
     *         ENEMY =  Math.pow(2,1),
     *         GROUND = Math.pow(2,2)
     *
     *     // Put shapes into their groups
     *     player1Shape.collisionGroup = PLAYER;
     *     player2Shape.collisionGroup = PLAYER;
     *     enemyShape  .collisionGroup = ENEMY;
     *     groundShape .collisionGroup = GROUND;
     *
     *     // Assign groups that each shape collide with.
     *     // Note that the players can collide with ground and enemies, but not with other players.
     *     player1Shape.collisionMask = ENEMY | GROUND;
     *     player2Shape.collisionMask = ENEMY | GROUND;
     *     enemyShape  .collisionMask = PLAYER | GROUND;
     *     groundShape .collisionMask = PLAYER | ENEMY;
     *
     * @example
     *     // How collision check is done
     *     if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
     *         // The shapes will collide
     *     }
     */
    collisionGroup: any;
    /**
     * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. That means that this shape will move through other body shapes, but it will still trigger contact events, etc.
     * @property {Boolean} collisionResponse
     */
    collisionResponse: any;
    /**
     * Collision mask of this shape. See .collisionGroup.
     * @property collisionMask
     * @type {Number}
     */
    collisionMask: any;
    /**
     * Material to use in collisions for this Shape. If this is set to null, the world will use default material properties instead.
     * @property material
     * @type {Material}
     */
    material: any;
    /**
     * 面积
     * Area of this shape.
     * @property area
     * @type {Number}
     */
    area: number;
    /**
     * 设置为true，则只做感应，不反馈碰撞冲量
     * Set to true if you want this shape to be a sensor. A sensor does not generate contacts, but it still reports contact events. This is good if you want to know if a shape is overlapping another shape, without them generating contacts.
     * @property {Boolean} sensor
     */
    sensor: boolean;
    /**
     * Base class for shapes. Not to be used directly.
     * @class Shape
     * @constructor
     * @param {object} [options]
     * @param {number} [options.angle=0]
     * @param {number} [options.collisionGroup=1]
     * @param {number} [options.collisionMask=1]
     * @param {boolean} [options.collisionResponse=true]
     * @param {Material} [options.material=null]
     * @param {array} [options.position]
     * @param {boolean} [options.sensor=false]
     * @param {object} [options.type=0]
     */
    constructor(options) {
        options = options || {};

        this.body = null;

        this.position = vec2.create();
        if (options.position) {
            vec2.copy(this.position, options.position);
        }

        this.angle = options.angle || 0;

        this.type = options.type || 0;

        this.id = Shape.idCounter++;

        this.boundingRadius = 0;

        this.collisionGroup = options.collisionGroup !== undefined ? options.collisionGroup : 1;

        this.collisionResponse = options.collisionResponse !== undefined ? options.collisionResponse : true;

        this.collisionMask = options.collisionMask !== undefined ? options.collisionMask : 1;

        this.material = options.material || null;

        this.area = 0;

        this.sensor = options.sensor !== undefined ? options.sensor : false;

        //在继承中实现，注意，子类必须执行
        // if (this.type) {
        //     this.updateBoundingRadius();
        // }

        // this.updateArea();
    }

    /**
     * @static
     * @property {Number} CIRCLE
     */
    public static CIRCLE = 1;

    /**
     * @static
     * @property {Number} PARTICLE
     */
    public static PARTICLE = 2;

    /**
     * @static
     * @property {Number} PLANE
     */
    public static PLANE = 4;

    /**
     * @static
     * @property {Number} CONVEX
     */
    public static CONVEX = 8;

    /**
     * @static
     * @property {Number} LINE
     */
    public static LINE = 16;

    /**
     * @static
     * @property {Number} BOX
     */
    public static BOX = 32;

    /**
     * @static
     * @property {Number} CAPSULE
     */
    public static CAPSULE = 64;

    /**
     * @static
     * @property {Number} HEIGHTFIELD
     */
    public static HEIGHTFIELD = 128;


    /**
     * 计算转动惯量，不同形状自己重写
     * Should return the moment of inertia around the Z axis of the body. See <a href="http://en.wikipedia.org/wiki/List_of_moments_of_inertia">Wikipedia's list of moments of inertia</a>.
     * @method computeMomentOfInertia
     * @return {Number} If the inertia is infinity or if the object simply isn't possible to rotate, return 0.
     */
    computeMomentOfInertia() { };

    /**
     * 包围圆
     * Returns the bounding circle radius of this shape.
     * @method updateBoundingRadius
     * @return {Number}
     */
    updateBoundingRadius() { };

    /**
     * 更新面积
     * Update the .area property of the shape.
     * @method updateArea
     */
    updateArea(...ads) { };

    /**
     * AABB检测，粗测碰撞
     * Compute the world axis-aligned bounding box (AABB) of this shape.
     * @method computeAABB
     * @param  {AABB} out The resulting AABB.
     * @param  {Array} position World position of the shape.
     * @param  {Number} angle World angle of the shape.
     */
    computeAABB(out, position?, angle?) {
        // To be implemented in each subclass
    };

    /**
     * 暂不用
     * Perform raycasting on this shape.
     * @method raycast
     * @param  {RayResult} result Where to store the resulting data.
     * @param  {Ray} ray The Ray that you want to use for raycasting.
     * @param  {array} position World position of the shape (the .position property will be ignored).
     * @param  {number} angle World angle of the shape (the .angle property will be ignored).
     */
    raycast(result, ray, position, angle) {
        // To be implemented in each subclass
    };

    /**
     * 检测点是否在shape内
     * Test if a point is inside this shape.
     * @method pointTest
     * @param {array} localPoint
     * @return {boolean}
     */
    pointTest(localPoint) { return false; };

    /**
     * 转换点从世界坐标到本地坐标
     * Transform a world point to local shape space (assumed the shape is transformed by both itself and the body).
     * @method worldPointToLocal
     * @param {array} out
     * @param {array} worldPoint
     */
    worldPointToLocal = (function () {
        var shapeWorldPosition = vec2.create();
        return function (out, worldPoint) {
            var body = this.body;

            vec2.rotate(shapeWorldPosition, this.position, body.angle);
            vec2.add(shapeWorldPosition, shapeWorldPosition, body.position);

            vec2.toLocalFrame(out, worldPoint, shapeWorldPosition, this.body.angle + this.angle);
        };
    })()
};
