/*
 * Copyright (c) Contributors, http://opensimulator.org/
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyrightD
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSimulator Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
using System;
using System.Collections.Generic;
using System.Text;
using OMV = OpenMetaverse;

namespace OpenSim.Region.Physics.BulletSNPlugin
{
// Classes to allow some type checking for the API
// These hold pointers to allocated objects in the unmanaged space.

// The physics engine controller class created at initialization
public struct BulletSim
{
    public BulletSim(uint worldId, BSScene bss, object xx)
    {
        ptr = xx;
        worldID = worldId;
        physicsScene = bss;
    }
    public object ptr;
    public uint worldID;
    // The scene is only in here so very low level routines have a handle to print debug/error messages
    public BSScene physicsScene;
}

// An allocated Bullet btRigidBody
public struct BulletBody
{
    public BulletBody(uint id) : this(id, null)
    {
    }
    public BulletBody(uint id, object xx)
    {
        ID = id;
        ptr = xx;
        collisionType = CollisionType.Static;
    }
    public object ptr;
    public uint ID;
    public CollisionType collisionType;

    public void Clear()
    {
        ptr = null;
    }
    public bool HasPhysicalBody { get { return ptr != null; } }

    // Apply the specificed collision mask into the physical world
    public void ApplyCollisionMask()
    {
        // Should assert the body has been added to the physical world.
        // (The collision masks are stored in the collision proxy cache which only exists for
        //    a collision body that is in the world.)
        BulletSimAPI.SetCollisionGroupMask2(ptr,
                                BulletSimData.CollisionTypeMasks[collisionType].group,
                                BulletSimData.CollisionTypeMasks[collisionType].mask);
    }

    public override string ToString()
    {
        StringBuilder buff = new StringBuilder();
        buff.Append("<id=");
        buff.Append(ID.ToString());
        buff.Append(",p=");
        buff.Append(ptr.ToString());
        buff.Append(",c=");
        buff.Append(collisionType);
        buff.Append(">");
        return buff.ToString();
    }
}

public struct BulletShape
{
    public BulletShape(object xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
    {
    }
    public BulletShape(object xx, BSPhysicsShapeType typ)
    {
        ptr = xx;
        type = typ;
        shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
        isNativeShape = false;
    }
    public object ptr;
    public BSPhysicsShapeType type;
    public System.UInt64 shapeKey;
    public bool isNativeShape;

    public void Clear()
    {
        ptr = null;
    }
    public bool HasPhysicalShape { get { return ptr != null; } }

    public override string ToString()
    {
        StringBuilder buff = new StringBuilder();
        buff.Append("<p=");
        buff.Append(ptr.ToString());
        buff.Append(",s=");
        buff.Append(type.ToString());
        buff.Append(",k=");
        buff.Append(shapeKey.ToString("X"));
        buff.Append(",n=");
        buff.Append(isNativeShape.ToString());
        buff.Append(">");
        return buff.ToString();
    }
}

// An allocated Bullet btConstraint
public struct BulletConstraint
{
    public BulletConstraint(object xx)
    {
        ptr = xx;
    }
    public object ptr;

    public void Clear()
    {
        ptr = null;
    }
    public bool HasPhysicalConstraint { get { return ptr != null; } }
}

// An allocated HeightMapThing which holds various heightmap info.
// Made a class rather than a struct so there would be only one
//      instance of this and C# will pass around pointers rather
//      than making copies.
public class BulletHeightMapInfo
{
    public BulletHeightMapInfo(uint id, float[] hm, object xx) {
        ID = id;
        Ptr = xx;
        heightMap = hm;
        terrainRegionBase = OMV.Vector3.Zero;
        minCoords = new OMV.Vector3(100f, 100f, 25f);
        maxCoords = new OMV.Vector3(101f, 101f, 26f);
        minZ = maxZ = 0f;
        sizeX = sizeY = 256f;
    }
    public uint ID;
    public object Ptr;
    public float[] heightMap;
    public OMV.Vector3 terrainRegionBase;
    public OMV.Vector3 minCoords;
    public OMV.Vector3 maxCoords;
    public float sizeX, sizeY;
    public float minZ, maxZ;
    public BulletShape terrainShape;
    public BulletBody terrainBody;

    public float collisionMargin { get; set; }
}

// The general class of collsion object.
public enum CollisionType
{
    Avatar,
    Groundplane,
    Terrain,
    Static,
    Dynamic,
    VolumeDetect,
    // Linkset, // A linkset should be either Static or Dynamic
    LinksetChild,
    Unknown
};

// Hold specification of group and mask collision flags for a CollisionType
public struct CollisionTypeFilterGroup
{
    public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
    {
        type = t;
        group = g;
        mask = m;
    }
    public CollisionType type;
    public uint group;
    public uint mask;
};

    /* NOTE: old definitions kept for reference. Delete when things are working.
    // The collsion filters and masked are defined in one place -- don't want them scattered
    AvatarGroup             = BCharacterGroup,
    AvatarMask              = BAllGroup,
    ObjectGroup             = BSolidGroup,
    ObjectMask              = BAllGroup,
    StaticObjectGroup       = BStaticGroup,
    StaticObjectMask        = AvatarGroup | ObjectGroup,    // static things don't interact with much
    LinksetGroup            = BLinksetGroup,
    LinksetMask             = BAllGroup,
    LinksetChildGroup       = BLinksetChildGroup,
    LinksetChildMask        = BNoneGroup,   // Linkset children disappear from the world
    VolumeDetectGroup       = BSensorTrigger,
    VolumeDetectMask        = ~BSensorTrigger,
    TerrainGroup            = BTerrainGroup,
    TerrainMask             = BAllGroup & ~BStaticGroup,  // static objects on the ground don't collide
    GroundPlaneGroup        = BGroundPlaneGroup,
    GroundPlaneMask         = BAllGroup
    */

public static class BulletSimData
{

// Map of collisionTypes to flags for collision groups and masks.
// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
//     but, instead, use references to this dictionary. Finding and debugging
//     collision flag problems will be made easier.
public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks 
            = new Dictionary<CollisionType, CollisionTypeFilterGroup>()
{
    { CollisionType.Avatar, 
                new CollisionTypeFilterGroup(CollisionType.Avatar, 
                                (uint)CollisionFilterGroups.BCharacterGroup, 
                                (uint)CollisionFilterGroups.BAllGroup)
    },
    { CollisionType.Groundplane, 
                new CollisionTypeFilterGroup(CollisionType.Groundplane, 
                                (uint)CollisionFilterGroups.BGroundPlaneGroup, 
                                (uint)CollisionFilterGroups.BAllGroup)
    },
    { CollisionType.Terrain, 
                new CollisionTypeFilterGroup(CollisionType.Terrain, 
                                (uint)CollisionFilterGroups.BTerrainGroup, 
                                (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
    },
    { CollisionType.Static, 
                new CollisionTypeFilterGroup(CollisionType.Static, 
                                (uint)CollisionFilterGroups.BStaticGroup, 
                                (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
    },
    { CollisionType.Dynamic, 
                new CollisionTypeFilterGroup(CollisionType.Dynamic, 
                                (uint)CollisionFilterGroups.BSolidGroup, 
                                (uint)(CollisionFilterGroups.BAllGroup))
    },
    { CollisionType.VolumeDetect, 
                new CollisionTypeFilterGroup(CollisionType.VolumeDetect, 
                                (uint)CollisionFilterGroups.BSensorTrigger, 
                                (uint)(~CollisionFilterGroups.BSensorTrigger))
    },
    { CollisionType.LinksetChild,
                new CollisionTypeFilterGroup(CollisionType.LinksetChild, 
                                (uint)CollisionFilterGroups.BTerrainGroup, 
                                (uint)(CollisionFilterGroups.BNoneGroup))
    },
};

}
}