/*
 * 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 copyright
 *       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 OpenSim 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.Drawing;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenSim.Framework;
using OpenSim.Region.Environment.Interfaces;
using OpenSim.Region.Physics.Manager;

namespace OpenSim.Region.Environment.Scenes
{
    [Flags]
    public enum scriptEvents
    {
        None = 0,
        attach = 1,
        collision = 16,
        collision_end = 32,
        collision_start = 64,
        control = 128,
        dataserver = 256,
        email = 512,
        http_response = 1024,
        land_collision = 2048,
        land_collision_end = 4096,
        land_collision_start = 8192,
        at_target = 16384,
        listen = 32768,
        money = 65536,
        moving_end = 131072,
        moving_start = 262144,
        not_at_rot_target = 524288,
        not_at_target = 1048576,
        remote_data = 8388608,
        run_time_permissions = 268435456,
        state_entry = 1073741824,
        state_exit = 2,
        timer = 4,
        touch = 8,
        touch_end = 536870912,
        touch_start = 2097152,
        object_rez = 4194304
    }

    struct scriptPosTarget
    {
        public Vector3 targetPos;
        public float tolerance;
    }

    public delegate void PrimCountTaintedDelegate();

    /// <summary>
    /// A scene object group is conceptually an object in the scene.  The object is constituted of SceneObjectParts
    /// (often known as prims), one of which is considered the root part.
    /// </summary>
    public partial class SceneObjectGroup : EntityBase
    {
        // private PrimCountTaintedDelegate handlerPrimCountTainted = null;

        /// <summary>
        /// Signal whether the non-inventory attributes of any prims in the group have changed
        /// since the group's last persistent backup
        /// </summary>
        public bool HasGroupChanged = false;

        public float scriptScore = 0f;

        private Vector3 lastPhysGroupPos;
        private Quaternion lastPhysGroupRot;

        /// <summary>
        /// The constituent parts of this group
        /// </summary>
        protected Dictionary<UUID, SceneObjectPart> m_parts = new Dictionary<UUID, SceneObjectPart>();

        protected ulong m_regionHandle;
        protected SceneObjectPart m_rootPart;
        // private Dictionary<UUID, scriptEvents> m_scriptEvents = new Dictionary<UUID, scriptEvents>();

        private Dictionary<uint, scriptPosTarget> m_targets = new Dictionary<uint, scriptPosTarget>();

        private bool m_scriptListens_atTarget = false;
        private bool m_scriptListens_notAtTarget = false;

        #region Properties

        /// <summary>
        /// The name of an object grouping is always the same as its root part
        /// </summary>
        public override string Name
        {
            get { return RootPart.Name; }
            set { RootPart.Name = value; }
        }

        /// <summary>
        /// Added because the Parcel code seems to use it
        /// but not sure a object should have this
        /// as what does it tell us? that some avatar has selected it (but not what Avatar/user)
        /// think really there should be a list (or whatever) in each scenepresence
        /// saying what prim(s) that user has selected.
        /// </summary>
        protected bool m_isSelected = false;

        /// <summary>
        /// Number of prims in this group
        /// </summary>
        public int PrimCount
        {
            get { return m_parts.Count; }
        }

        public Quaternion GroupRotation
        {
            get { return m_rootPart.RotationOffset; }
        }

        public UUID GroupID
        {
            get { return m_rootPart.GroupID; }
            set { m_rootPart.GroupID = value; }
        }

        public Dictionary<UUID, SceneObjectPart> Children
        {
            get { return m_parts; }
            set { m_parts = value; }
        }

        public SceneObjectPart RootPart
        {
            get { return m_rootPart; }
            set { m_rootPart = value; }
        }

        public ulong RegionHandle
        {
            get { return m_regionHandle; }
            set
            {
                m_regionHandle = value;
                lock (m_parts)
                {
                    foreach (SceneObjectPart part in m_parts.Values)
                    {
                        part.RegionHandle = m_regionHandle;
                    }
                }
            }
        }

        /// <summary>
        /// The absolute position of this scene object in the scene
        /// </summary>
        public override Vector3 AbsolutePosition
        {
            get
            {
                if (m_rootPart == null)
                {
                    throw new NullReferenceException(
                        string.Format("[SCENE OBJECT GROUP]: Object {0} has no root part.", m_uuid));
                }

                return m_rootPart.GroupPosition;
            }
            set
            {
                Vector3 val = value;
                
                if ((val.X > 257f || val.X < -1f || val.Y > 257f || val.Y < -1f) && !m_rootPart.IsAttachment)
                {                                       
                    m_scene.CrossPrimGroupIntoNewRegion(val, this);
                }

                lock (m_parts)
                {
                    foreach (SceneObjectPart part in m_parts.Values)
                    {
                        part.GroupPosition = val;
                    }
                }

                //if (m_rootPart.PhysActor != null)
                //{
                //m_rootPart.PhysActor.Position =
                //new PhysicsVector(m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y,
                //m_rootPart.GroupPosition.Z);
                //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
                //}
            }
        }

        public override uint LocalId
        {
            get
            {
                if (m_rootPart == null)
                {
                    m_log.Error("[SCENE OBJECT GROUP]: Unable to find the rootpart for a LocalId Request!");
                    return 0;
                }

                return m_rootPart.LocalId;
            }
            set { m_rootPart.LocalId = value; }
        }

        public override UUID UUID
        {
            get {
                if (m_rootPart == null)
                {
                    m_log.Error("Got a null rootpart while requesting UUID. Called from: ", new Exception());
                    return UUID.Zero;
                }
                else return m_rootPart.UUID;
            }
            set { m_rootPart.UUID = value; }
        }

        public UUID OwnerID
        {
            get
            {
                if (m_rootPart == null)
                    return UUID.Zero;

                return m_rootPart.OwnerID;
            }
            set { m_rootPart.OwnerID = value; }
        }

        public Color Color
        {
            get { return m_rootPart.Color; }
            set { m_rootPart.Color = value; }
        }

        public string Text
        {
            get {
                string returnstr = m_rootPart.Text;
                if (returnstr.Length  > 255)
                {
                    returnstr = returnstr.Substring(0, 255);
                }
                return returnstr;
            }
            set { m_rootPart.Text = value; }
        }

        protected virtual bool InSceneBackup
        {
            get { return true; }
        }

        public bool IsSelected
        {
            get { return m_isSelected; }
            set
            {
                m_isSelected = value;
                // Tell physics engine that group is selected
                if (m_rootPart.PhysActor != null)
                {
                    m_rootPart.PhysActor.Selected = value;
                    // Pass it on to the children.
                    foreach (SceneObjectPart child in Children.Values)
                    {
                        if (child.PhysActor != null)
                        {
                            child.PhysActor.Selected = value;
                        }
                    }
                }
            }
        }

        // The UUID for the Region this Object is in.
        public UUID RegionUUID
        {
            get
            {
                if (m_scene != null)
                {
                    return m_scene.RegionInfo.RegionID;
                }
                return UUID.Zero;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor
        /// </summary>
        public SceneObjectGroup()
        {
        }

        /// <summary>
        /// This constructor creates a SceneObjectGroup using a pre-existing SceneObjectPart.
        /// The original SceneObjectPart will be used rather than a copy, preserving
        /// its existing localID and UUID.
        /// </summary>
        public SceneObjectGroup(Scene scene, ulong regionHandle, SceneObjectPart part)
        {
            m_scene = scene;

            part.SetParent(this);
            part.ParentID = 0;
            part.LinkNum = 0;

            m_parts.Add(part.UUID, part);

            SetPartAsRoot(part);

            RegionHandle = regionHandle;
        }

        /// <summary>
        /// Create an object using serialized data in OpenSim's original xml format.
        /// </summary>
        public SceneObjectGroup(Scene scene, ulong regionHandle, string xmlData)
        {
            m_scene = scene;
            m_regionHandle = regionHandle;
            // libomv.types changes UUID to Guid
            xmlData = xmlData.Replace("<UUID>", "<Guid>");
            xmlData = xmlData.Replace("</UUID>", "</Guid>");

            // Handle Nested <UUID><UUID> property
            xmlData = xmlData.Replace("<Guid><Guid>", "<UUID><Guid>");
            xmlData = xmlData.Replace("</Guid></Guid>", "</Guid></UUID>");
            StringReader sr = new StringReader(xmlData);
            XmlTextReader reader = new XmlTextReader(sr);

            try
            {
                reader.Read();
                reader.ReadStartElement("SceneObjectGroup");
                reader.ReadStartElement("RootPart");
                m_rootPart = SceneObjectPart.FromXml(reader);
                int linkNum = m_rootPart.LinkNum;
                AddPart(m_rootPart);
                m_rootPart.LinkNum = linkNum;

                reader.ReadEndElement();

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            if (reader.Name == "Part")
                            {
                                reader.Read();
                                SceneObjectPart part = SceneObjectPart.FromXml(reader);
                                part.LocalId = m_scene.PrimIDAllocate();
                                linkNum = part.LinkNum;
                                AddPart(part);
                                part.LinkNum = linkNum;
                                part.RegionHandle = m_regionHandle;

                                part.TrimPermissions();
                                part.StoreUndoState();
                            }
                            break;
                        case XmlNodeType.EndElement:
                            break;
                    }
                }
            }
            catch (XmlException)
            {
                m_log.ErrorFormat("[SCENE OBJECT GROUP]: Deserialization of following xml failed, {0}", xmlData);

                // Let's see if carrying on does anything for us
            }

            reader.Close();
            sr.Close();

            m_rootPart.LocalId = m_scene.PrimIDAllocate();
            m_rootPart.ParentID = 0;
            m_rootPart.RegionHandle = m_regionHandle;
            UpdateParentIDs();
        }

        /// <summary>
        /// Create an object using serialized data in OpenSim's xml2 format.
        /// </summary>
        public SceneObjectGroup(string xmlData)
        {
            // libomv.types changes UUID to Guid
            xmlData = xmlData.Replace("<UUID>", "<Guid>");
            xmlData = xmlData.Replace("</UUID>", "</Guid>");

            // Handle Nested <UUID><UUID> property
            xmlData = xmlData.Replace("<Guid><Guid>", "<UUID><Guid>");
            xmlData = xmlData.Replace("</Guid></Guid>", "</Guid></UUID>");

            StringReader sr = new StringReader(xmlData);
            XmlTextReader reader = new XmlTextReader(sr);
            reader.Read();

            reader.ReadStartElement("SceneObjectGroup");
            m_rootPart = SceneObjectPart.FromXml(reader);
            m_rootPart.SetParent(this);
            m_parts.Add(m_rootPart.UUID, m_rootPart);
            m_rootPart.ParentID = 0;
            m_rootPart.LinkNum = 0;

            reader.Read();
            bool more = true;

            while (more)
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        if (reader.Name == "SceneObjectPart")
                        {
                            SceneObjectPart Part = SceneObjectPart.FromXml(reader);
                            if (m_rootPart.LinkNum == 0)
                                m_rootPart.LinkNum++;
                            AddPart(Part);
                            Part.LinkNum = m_parts.Count;
                            Part.StoreUndoState();
                        }
                        else
                        {
                            Console.WriteLine("found unexpected element: " + reader.Name);
                            reader.Read();
                        }
                        break;
                    case XmlNodeType.EndElement:
                        reader.Read();
                        break;
                }
                more = !reader.EOF;
            }

            reader.Close();
            sr.Close();

            UpdateParentIDs();
        }

        /// <summary>
        ///
        /// </summary>
        public SceneObjectGroup(Scene scene, ulong regionHandle, UUID ownerID, uint localID, Vector3 pos,
                                Quaternion rot, PrimitiveBaseShape shape)
        {
            m_regionHandle = regionHandle;
            m_scene = scene;

            // this.Pos = pos;
            Vector3 rootOffset = new Vector3(0, 0, 0);
            SceneObjectPart newPart =
                new SceneObjectPart(m_regionHandle, this, ownerID, localID, shape, pos, rot, rootOffset);
            newPart.LinkNum = 0;
            m_parts.Add(newPart.UUID, newPart);
            SetPartAsRoot(newPart);

            // one of these is a proxy.
            if (shape.PCode != (byte)PCode.None && shape.PCode != (byte)PCode.ParticleSystem)
                AttachToBackup();

            //ApplyPhysics(scene.m_physicalPrim);
        }

        /// <summary>
        ///
        /// </summary>
        public SceneObjectGroup(Scene scene, ulong regionHandle, UUID ownerID, uint localID, Vector3 pos,
                                PrimitiveBaseShape shape)
            : this(scene, regionHandle, ownerID, localID, pos, Quaternion.Identity, shape)
        {
        }

        public void SetFromAssetID(UUID AssetId)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.FromAssetID = AssetId;
                }
            }
        }

        public UUID GetFromAssetID()
        {
            if (m_rootPart != null)
            {
                return m_rootPart.FromAssetID;
            }
            return UUID.Zero;
        }

        /// <summary>
        /// Hooks this object up to the backup event so that it is persisted to the database when the update thread executes.
        /// </summary>
        public void AttachToBackup()
        {
            if (InSceneBackup)
            {
                //m_log.DebugFormat(
                //    "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);

                m_scene.EventManager.OnBackup += ProcessBackup;
            }
        }

        public Vector3 GroupScale()
        {
            Vector3 minScale = new Vector3(Constants.RegionSize,Constants.RegionSize,Constants.RegionSize);
            Vector3 maxScale = new Vector3(0f,0f,0f);
            Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f);

            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    Vector3 partscale = part.Scale;
                    Vector3 partoffset = part.OffsetPosition;

                    minScale.X = (partscale.X + partoffset.X < minScale.X) ? partscale.X + partoffset.X : minScale.X;
                    minScale.Y = (partscale.Y + partoffset.Y < minScale.Y) ? partscale.X + partoffset.Y : minScale.Y;
                    minScale.Z = (partscale.Z + partoffset.Z < minScale.Z) ? partscale.X + partoffset.Z : minScale.Z;

                    maxScale.X = (partscale.X + partoffset.X > maxScale.X) ? partscale.X + partoffset.X : maxScale.X;
                    maxScale.Y = (partscale.Y + partoffset.Y > maxScale.Y) ? partscale.Y + partoffset.Y : maxScale.Y;
                    maxScale.Z = (partscale.Z + partoffset.Z > maxScale.Z) ? partscale.Z + partoffset.Z : maxScale.Z;
                }
            }
            finalScale.X = (minScale.X > maxScale.X) ? minScale.X : maxScale.X;
            finalScale.Y = (minScale.Y > maxScale.Y) ? minScale.Y : maxScale.Y;
            finalScale.Z = (minScale.Z > maxScale.Z) ? minScale.Z : maxScale.Z;
            return finalScale;

        }
        public EntityIntersection TestIntersection(Ray hRay, bool frontFacesOnly, bool faceCenters)
        {
            // We got a request from the inner_scene to raytrace along the Ray hRay
            // We're going to check all of the prim in this group for intersection with the ray
            // If we get a result, we're going to find the closest result to the origin of the ray
            // and send back the intersection information back to the innerscene.

            EntityIntersection returnresult = new EntityIntersection();

            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    // Temporary commented to stop compiler warning
                    //Vector3 partPosition =
                    //    new Vector3(part.AbsolutePosition.X, part.AbsolutePosition.Y, part.AbsolutePosition.Z);
                    Quaternion parentrotation = GroupRotation;

                    // Telling the prim to raytrace.
                    //EntityIntersection inter = part.TestIntersection(hRay, parentrotation);

                    EntityIntersection inter = part.TestIntersectionOBB(hRay, parentrotation,frontFacesOnly, faceCenters);

                    // This may need to be updated to the maximum draw distance possible..
                    // We might (and probably will) be checking for prim creation from other sims
                    // when the camera crosses the border.
                    float idist = Constants.RegionSize;


                    if (inter.HitTF)
                    {
                        // We need to find the closest prim to return to the testcaller along the ray
                        if (inter.distance < idist)
                        {
                            returnresult.HitTF = true;
                            returnresult.ipoint = inter.ipoint;
                            returnresult.obj = part;
                            returnresult.normal = inter.normal;
                            returnresult.distance = inter.distance;
                        }
                    }
                }
            }
            return returnresult;
        }

        #endregion


        public string ToXmlString()
        {
            using (StringWriter sw = new StringWriter())
            {
                using (XmlTextWriter writer = new XmlTextWriter(sw))
                {
                    ToXml(writer);
                }

                return sw.ToString();
            }
        }

        public void ToXml(XmlTextWriter writer)
        {
            writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty);
            writer.WriteStartElement(String.Empty, "RootPart", String.Empty);
            m_rootPart.ToXml(writer);
            writer.WriteEndElement();
            writer.WriteStartElement(String.Empty, "OtherParts", String.Empty);

            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (part.UUID != m_rootPart.UUID)
                    {
                        writer.WriteStartElement(String.Empty, "Part", String.Empty);
                        part.ToXml(writer);
                        writer.WriteEndElement();
                    }
                }
            }

            writer.WriteEndElement();
            writer.WriteEndElement();
        }

        public string ToXmlString2()
        {
            using (StringWriter sw = new StringWriter())
            {
                using (XmlTextWriter writer = new XmlTextWriter(sw))
                {
                    ToXml2(writer);
                }

                return sw.ToString();
            }
        }

        public void ToXml2(XmlTextWriter writer)
        {
            writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty);
            m_rootPart.ToXml(writer);
            writer.WriteStartElement(String.Empty, "OtherParts", String.Empty);

            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (part.UUID != m_rootPart.UUID)
                    {
                        part.ToXml(writer);
                    }
                }
            }

            writer.WriteEndElement();
            writer.WriteEndElement();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="part"></param>
        private void SetPartAsRoot(SceneObjectPart part)
        {
            m_rootPart = part;
        }

        /// <summary>
        /// Attach this scene object to the given avatar.
        /// </summary>
        /// <param name="agentID"></param>
        /// <param name="attachmentpoint"></param>
        /// <param name="AttachOffset"></param>
        public void AttachToAgent(UUID agentID, uint attachmentpoint, Vector3 AttachOffset)
        {
            ScenePresence avatar = m_scene.GetScenePresence(agentID);
            if (avatar != null)
            {
                // don't attach attachments to child agents
                if (avatar.IsChildAgent) return;

                DetachFromBackup();

                // Remove from database and parcel prim count
                //
                m_scene.DeleteFromStorage(UUID);
                m_scene.EventManager.TriggerParcelPrimCountTainted();

                m_rootPart.AttachedAvatar = agentID;

                if (m_rootPart.PhysActor != null)
                {
                    m_scene.PhysicsScene.RemovePrim(m_rootPart.PhysActor);
                    m_rootPart.PhysActor = null;

                }

                AbsolutePosition = AttachOffset;
                m_rootPart.AttachedPos = AttachOffset;
                m_rootPart.IsAttachment = true;

                m_rootPart.SetParentLocalId(avatar.LocalId);
                SetAttachmentPoint(Convert.ToByte(attachmentpoint));

                avatar.AddAttachment(this);
                // Killing it here will cause the client to deselect it
                // It then reappears on the avatar, deselected
                // through the full update below
                //
                if (IsSelected)
                {
                    m_scene.SendKillObject(m_rootPart.LocalId);
                }

                IsSelected = false; // fudge....
                ScheduleGroupForFullUpdate();
            }
        }
        public byte GetAttachmentPoint()
        {
            if (m_rootPart != null)
            {
                return m_rootPart.Shape.State;
            }
            return (byte)0;
        }

        public void ClearPartAttachmentData()
        {
            SetAttachmentPoint((Byte)0);
        }

        public void DetachToGround()
        {
            ScenePresence avatar = m_scene.GetScenePresence(m_rootPart.AttachedAvatar);
            Vector3 detachedpos = new Vector3(127f,127f,127f);
            if (avatar == null)
                return;

            detachedpos = avatar.AbsolutePosition;

            AbsolutePosition = detachedpos;
            m_rootPart.AttachedAvatar = UUID.Zero;
            m_rootPart.SetParentLocalId(0);
            SetAttachmentPoint((byte)0);
            m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_scene.m_physicalPrim);
            HasGroupChanged = true;
            AttachToBackup();
            m_scene.EventManager.TriggerParcelPrimCountTainted();
            m_rootPart.ScheduleFullUpdate();
            m_rootPart.ClearUndoState();
        }

        public void DetachToInventoryPrep()
        {
            ScenePresence avatar = m_scene.GetScenePresence(m_rootPart.AttachedAvatar);
            //Vector3 detachedpos = new Vector3(127f, 127f, 127f);
            if (avatar != null)
            {
                //detachedpos = avatar.AbsolutePosition;
                avatar.RemoveAttachment(this);
            }

            m_rootPart.AttachedAvatar = UUID.Zero;
            m_rootPart.SetParentLocalId(0);
            //m_rootPart.SetAttachmentPoint((byte)0);
            m_rootPart.IsAttachment = false;
            AbsolutePosition = m_rootPart.AttachedPos;
            //m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_scene.m_physicalPrim);
            //AttachToBackup();
            //m_rootPart.ScheduleFullUpdate();

        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="part"></param>
        private void SetPartAsNonRoot(SceneObjectPart part)
        {
            part.ParentID = m_rootPart.LocalId;
            part.ClearUndoState();
        }

        public override void UpdateMovement()
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.UpdateMovement();
                }
            }
        }

        public float GetTimeDilation()
        {
            return m_scene.TimeDilation;
        }

        /// <summary>
        /// Added as a way for the storage provider to reset the scene,
        /// most likely a better way to do this sort of thing but for now...
        /// </summary>
        /// <param name="scene"></param>
        public void SetScene(Scene scene)
        {
            m_scene = scene;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="part"></param>
        public void AddPart(SceneObjectPart part)
        {
            lock (m_parts)
            {
                part.SetParent(this);

                try
                {
                    m_parts.Add(part.UUID, part);

                }
                catch (Exception e)
                {
                    m_log.Error("Failed to add scene object part", e);
                }

                part.LinkNum = m_parts.Count;

                if (part.LinkNum == 2 && RootPart != null)
                    RootPart.LinkNum = 1;
            }
        }

        /// <summary>
        /// Make sure that every non root part has the proper parent root part local id
        /// </summary>
        public void UpdateParentIDs()
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (part.UUID != m_rootPart.UUID)
                    {
                        part.ParentID = m_rootPart.LocalId;
                    }
                }
            }
        }

        public void RegenerateFullIDs()
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.UUID = UUID.Random();

                }
            }
        }
        // helper provided for parts.
        public int GetSceneMaxUndo()
        {
            if (m_scene != null)
                return m_scene.MaxUndoCount;
            return 5;
        }

        // justincc: I don't believe this hack is needed any longer, especially since the physics
        // parts of set AbsolutePosition were already commented out.  By changing HasGroupChanged to false
        // this method was preventing proper reload of scene objects.
        // dahlia: I had to uncomment it, without it meshing was failing on some prims and objects
        // at region startup
        public void ResetChildPrimPhysicsPositions()
        {
            AbsolutePosition = AbsolutePosition; // could someone in the know please explain how this works?
        }

        public UUID GetPartsFullID(uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                return part.UUID;
            }
            return UUID.Zero;
        }

        public void ObjectGrabHandler(uint localId, Vector3 offsetPos, IClientAPI remoteClient)
        {
            if (m_rootPart.LocalId == localId)
            {
                OnGrabGroup(offsetPos, remoteClient);
            }
            else
            {
                SceneObjectPart part = GetChildPart(localId);
                OnGrabPart(part, offsetPos, remoteClient);

            }
        }

        public virtual void OnGrabPart(SceneObjectPart part, Vector3 offsetPos, IClientAPI remoteClient)
        {
            part.StoreUndoState();
            part.OnGrab(offsetPos, remoteClient);

        }

        public virtual void OnGrabGroup(Vector3 offsetPos, IClientAPI remoteClient)
        {
            m_scene.EventManager.TriggerGroupGrab(UUID, offsetPos, remoteClient.AgentId);
        }

        /// <summary>
        /// Delete this group from its scene and tell all the scene presences about that deletion.
        /// </summary>
        public void DeleteGroup()
        {
            // We need to keep track of this state in case this group is still queued for backup.
            // FIXME: This is a poor temporary solution, since it still leaves plenty of scope for race
            // conditions where a user deletes an entity while it is being stored.  Really, the update
            // code needs a redesign.
            m_isDeleted = true;

            DetachFromBackup();

            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
//                    part.RemoveScriptInstances();

                    List<ScenePresence> avatars = Scene.GetScenePresences();
                    for (int i = 0; i < avatars.Count; i++)
                    {
                        if (avatars[i].ParentID == LocalId)
                        {
                            avatars[i].StandUp();
                        }

                        if (m_rootPart != null && part == m_rootPart)
                            avatars[i].ControllingClient.SendKillObject(m_regionHandle, part.LocalId);
                    }
                }

                m_rootPart = null;
                m_parts.Clear();
            }
        }

        public void FakeDeleteGroup()
        {
            // If there are any updates queued for this object when the 'fake' delete happens, then make sure
            // that they don't happen, otherwise the deleted objects will reappear
            m_isDeleted = true;

            foreach (SceneObjectPart part in m_parts.Values)
            {
                List<ScenePresence> avatars = Scene.GetScenePresences();
                for (int i = 0; i < avatars.Count; i++)
                {
                    if (avatars[i].ParentID == LocalId)
                    {
                        avatars[i].StandUp();
                    }

                    if (m_rootPart != null && part == m_rootPart)
                        avatars[i].ControllingClient.SendKillObject(m_regionHandle, part.LocalId);
                }
            }
        }

        public void AddScriptLPS(int count)
        {
            if (scriptScore + count >= float.MaxValue - count)
                scriptScore = 0;

            scriptScore += (float)count;
            InnerScene d = m_scene.m_innerScene;
            d.AddToScriptLPS(count);
        }

        public void AddActiveScriptCount(int count)
        {
            InnerScene d = m_scene.m_innerScene;
            d.AddActiveScripts(count);
        }

        public void aggregateScriptEvents()
        {
            uint objectflagupdate=(uint)RootPart.GetEffectiveObjectFlags();

            scriptEvents aggregateScriptEvents=0;

            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (part == null)
                        continue;
                    if (part != RootPart)
                        part.ObjectFlags = objectflagupdate;
                    aggregateScriptEvents |= part.AggregateScriptEvents;
                }
            }

            if ((aggregateScriptEvents & scriptEvents.at_target) != 0)
            {
                m_scriptListens_atTarget = true;
            }
            else
            {
                m_scriptListens_atTarget = false;
            }

            if ((aggregateScriptEvents & scriptEvents.not_at_target) != 0)
            {
                m_scriptListens_notAtTarget = true;
            }
            else
            {
                m_scriptListens_notAtTarget = false;
            }

            if (m_scriptListens_atTarget || m_scriptListens_notAtTarget)
            {
            }
            else
            {
                lock (m_targets)
                    m_targets.Clear();
            }

            ScheduleGroupForFullUpdate();
        }

        public override void SetText(string text, Vector3 color, double alpha)
        {
            Color = Color.FromArgb(0xff - (int) (alpha * 0xff),
                                   (int) (color.X * 0xff),
                                   (int) (color.Y * 0xff),
                                   (int) (color.Z * 0xff));
            Text = text;

            HasGroupChanged = true;
            m_rootPart.ScheduleFullUpdate();
        }

        /// <summary>
        /// Apply physics to this group
        /// </summary>
        /// <param name="m_physicalPrim"></param>
        public void ApplyPhysics(bool m_physicalPrim)
        {
            lock (m_parts)
            {
                if (m_parts.Count > 1)
                {
                    m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_physicalPrim);
                    foreach (SceneObjectPart part in m_parts.Values)
                    {
                        if (part.LocalId != m_rootPart.LocalId)
                        {
                            part.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_physicalPrim);
                        }                                                  
                    }    
                    
                    // Hack to get the physics scene geometries in the right spot
                    ResetChildPrimPhysicsPositions();                     
                }
                else
                {
                    m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_physicalPrim);
                }
            }
        }

        public void SetOwnerId(UUID userId)
        {
            ForEachPart(delegate(SceneObjectPart part) { part.OwnerID = userId; });
        }

        public void ForEachPart(Action<SceneObjectPart> whatToDo)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    whatToDo(part);
                }
            }
        }

        #region Events

        /// <summary>
        /// Processes backup.
        /// </summary>
        /// <param name="datastore"></param>
        public void ProcessBackup(IRegionDataStore datastore)
        {
            // Since this is the top of the section of call stack for backing up a particular scene object, don't let
            // any exception propogate upwards.
            try
            {
                if (HasGroupChanged)
                {
                    // don't backup while it's selected or you're asking for changes mid stream.
                    if ((!IsSelected) && (RootPart != null) && (!m_rootPart.IsAttachment))
                    {
                        m_log.DebugFormat(
                            "[SCENE]: Storing {0}, {1} in {2}",
                            Name, UUID, m_scene.RegionInfo.RegionName);

                        SceneObjectGroup backup_group = Copy(OwnerID, GroupID, false);
                        backup_group.RootPart.Velocity = RootPart.Velocity;
                        backup_group.RootPart.Acceleration = RootPart.Acceleration;
                        backup_group.RootPart.AngularVelocity = RootPart.AngularVelocity;
                        backup_group.RootPart.ParticleSystem = RootPart.ParticleSystem;

                        datastore.StoreObject(backup_group, m_scene.RegionInfo.RegionID);
                        HasGroupChanged = false;

                        backup_group.ForEachPart(delegate(SceneObjectPart part) { part.ProcessInventoryBackup(datastore); });

                        backup_group = null;
                    }
    //                else
    //                {
    //                    m_log.DebugFormat(
    //                        "[SCENE]: Did not update persistence of object {0} {1}, selected = {2}",
    //                        Name, UUID, IsSelected);
    //                }
                }
            }
            catch (Exception e)
            {
                m_log.ErrorFormat(
                    "[SCENE]: Storing of {0}, {1} in {2} failed with exception {3}", 
                    Name, UUID, m_scene.RegionInfo.RegionName, e);
            }
        }

        #endregion

        #region Client Updating

        public void SendFullUpdateToClient(IClientAPI remoteClient, uint clientFlags)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    SendPartFullUpdate(remoteClient, part, clientFlags);
                }
            }
        }

        /// <summary>
        /// Send a full update to the client for the given part
        /// </summary>
        /// <param name="remoteClient"></param>
        /// <param name="part"></param>
        internal void SendPartFullUpdate(IClientAPI remoteClient, SceneObjectPart part, uint clientFlags)
        {
            if (m_rootPart != null && m_rootPart.UUID == part.UUID)
            {
                if (m_rootPart.IsAttachment)
                {
                    part.SendFullUpdateToClient(remoteClient, m_rootPart.AttachedPos, clientFlags);
                }
                else
                {
                    part.SendFullUpdateToClient(remoteClient, AbsolutePosition, clientFlags);
                }
            }
            else
            {
                part.SendFullUpdateToClient(remoteClient, clientFlags);
            }
        }

        /// <summary>
        /// Send a terse update to the client for the given part
        /// </summary>
        /// <param name="remoteClient"></param>
        /// <param name="part"></param>
        internal void SendPartTerseUpdate(IClientAPI remoteClient, SceneObjectPart part)
        {
            SceneObjectPart rootPart = m_rootPart;
            
            // TODO: that could by caused by some race condition with attachments on sim-crossing
            if (rootPart == null) return;

            if (rootPart.UUID == part.UUID)
            {
                if (rootPart.IsAttachment)
                {
                    part.SendTerseUpdateToClient(remoteClient, rootPart.AttachedPos);
                }
                else
                {
                    part.SendTerseUpdateToClient(remoteClient, AbsolutePosition);
                }
            }
            else
            {
                part.SendTerseUpdateToClient(remoteClient);
            }
        }

        #endregion

        #region Copying

        /// <summary>
        /// Duplicates this object, including operations such as physics set up and attaching to the backup event.
        /// </summary>
        /// <returns></returns>
        public SceneObjectGroup Copy(UUID cAgentID, UUID cGroupID, bool userExposed)
        {
            SceneObjectGroup dupe = (SceneObjectGroup) MemberwiseClone();
            dupe.m_parts = new Dictionary<UUID, SceneObjectPart>();
            dupe.m_parts.Clear();
            //dupe.OwnerID = AgentID;
            //dupe.GroupID = GroupID;
            dupe.AbsolutePosition = new Vector3(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z);
            dupe.m_scene = m_scene;
            dupe.m_regionHandle = m_regionHandle;

            dupe.CopyRootPart(m_rootPart, OwnerID, GroupID, userExposed);

            if (userExposed)
                dupe.m_rootPart.TrimPermissions();

            /// may need to create a new Physics actor.
            if (dupe.RootPart.PhysActor != null && userExposed)
            {
                PrimitiveBaseShape pbs = dupe.RootPart.Shape;

                dupe.RootPart.PhysActor = m_scene.PhysicsScene.AddPrimShape(
                    dupe.RootPart.Name,
                    pbs,
                    new PhysicsVector(dupe.RootPart.AbsolutePosition.X, dupe.RootPart.AbsolutePosition.Y, dupe.RootPart.AbsolutePosition.Z),
                    new PhysicsVector(dupe.RootPart.Scale.X, dupe.RootPart.Scale.Y, dupe.RootPart.Scale.Z),
                    dupe.RootPart.RotationOffset,
                    dupe.RootPart.PhysActor.IsPhysical);

                dupe.RootPart.PhysActor.LocalID = dupe.RootPart.LocalId;

                dupe.RootPart.DoPhysicsPropertyUpdate(dupe.RootPart.PhysActor.IsPhysical, true);
            }

            // Now we've made a copy that replaces this one, we need to
            // switch the owner to the person who did the copying
            // Second Life copies an object and duplicates the first one in it's place
            // So, we have to make a copy of this one, set it in it's place then set the owner on this one
            if (userExposed)
            {
                SetRootPartOwner(m_rootPart, cAgentID, cGroupID);
                m_rootPart.ScheduleFullUpdate();
            }

            List<SceneObjectPart> partList = new List<SceneObjectPart>(m_parts.Values);
            foreach (SceneObjectPart part in partList)
            {
                if (part.UUID != m_rootPart.UUID)
                {
                    dupe.CopyPart(part, OwnerID, GroupID, userExposed);

                    if (userExposed)
                    {
                        SetPartOwner(part, cAgentID, cGroupID);
                        part.ScheduleFullUpdate();
                    }
                }
            }

            if (userExposed)
            {
                dupe.UpdateParentIDs();
                dupe.HasGroupChanged = true;
                dupe.AttachToBackup();

                ScheduleGroupForFullUpdate();
            }

            return dupe;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="part"></param>
        /// <param name="cAgentID"></param>
        /// <param name="cGroupID"></param>
        public void CopyRootPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed)
        {
            SceneObjectPart newPart = part.Copy(m_scene.PrimIDAllocate(), OwnerID, GroupID, m_parts.Count, userExposed);
            newPart.SetParent(this);

            lock (m_parts)
            {
                m_parts.Add(newPart.UUID, newPart);
            }

            SetPartAsRoot(newPart);
        }

        public void ScriptSetPhysicsStatus(bool UsePhysics)
        {
            if (m_scene.m_physicalPrim)
            {
                lock (m_parts)
                {
                    foreach (SceneObjectPart part in m_parts.Values)
                    {
                        if (UsePhysics)
                            part.AddFlag(PrimFlags.Physics);
                        else
                            part.RemFlag(PrimFlags.Physics);

                        part.DoPhysicsPropertyUpdate(UsePhysics, false);
                        IsSelected = false;
                    }
                }
            }
        }

        public void ScriptSetPhantomStatus(bool PhantomStatus)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (PhantomStatus)
                    {
                        part.AddFlag(PrimFlags.Phantom);
                        if (part.PhysActor != null)
                        {
                            m_scene.PhysicsScene.RemovePrim(part.PhysActor);
                        }
                    }
                    else
                    {
                        part.RemFlag(PrimFlags.Phantom);
                        if ((part.GetEffectiveObjectFlags() & (int) PrimFlags.Physics) != 0)
                        {
                            part.DoPhysicsPropertyUpdate(true, false);
                        }
                    }
                }
            }
        }

        public void applyImpulse(PhysicsVector impulse)
        {
            // We check if rootpart is null here because scripts don't delete if you delete the host.
            // This means that unfortunately, we can pass a null physics actor to Simulate!
            // Make sure we don't do that!
            SceneObjectPart rootpart = m_rootPart;
            if (rootpart != null)
            {
                if (rootpart.PhysActor != null)
                {
                    if (rootpart.IsAttachment)
                    {
                        ScenePresence avatar = m_scene.GetScenePresence(rootpart.AttachedAvatar);
                        if (avatar != null)
                        {
                            avatar.PushForce(impulse);
                        }
                    }
                    else
                    {
                        rootpart.PhysActor.AddForce(impulse,true);
                        m_scene.PhysicsScene.AddPhysicsActorTaint(rootpart.PhysActor);
                    }
                }
            }
        }

        public void moveToTarget(Vector3 target, float tau)
        {
            SceneObjectPart rootpart = m_rootPart;
            if (rootpart != null)
            {
                if (rootpart.PhysActor != null)
                {
                    rootpart.PhysActor.PIDTarget = new PhysicsVector(target.X, target.Y, target.Z);
                    rootpart.PhysActor.PIDTau = tau;
                    rootpart.PhysActor.PIDActive = true;
                }
            }
        }

        public void stopMoveToTarget()
        {
            SceneObjectPart rootpart = m_rootPart;
            if (rootpart != null)
            {
                rootpart.PhysActor.PIDActive = false;
            }
        }

        public void SetRootPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID)
        {
            part.LastOwnerID = part.OwnerID;
            part.OwnerID = cAgentID;
            part.GroupID = cGroupID;

            if (part.OwnerID != cAgentID)
            {
                // Apply Next Owner Permissions if we're not bypassing permissions
                if (!m_scene.ExternalChecks.ExternalChecksBypassPermissions())
                    ApplyNextOwnerPermissions();
            }

            part.ScheduleFullUpdate();
        }

        /// <summary>
        /// Make a copy of the given part.
        /// </summary>
        /// <param name="part"></param>
        /// <param name="cAgentID"></param>
        /// <param name="cGroupID"></param>
        public void CopyPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed)
        {
            SceneObjectPart newPart = part.Copy(m_scene.PrimIDAllocate(), OwnerID, GroupID, m_parts.Count, userExposed);
            newPart.SetParent(this);

            lock (m_parts)
            {
                m_parts.Add(newPart.UUID, newPart);
            }

            SetPartAsNonRoot(newPart);

        }

        /// <summary>
        /// Reset the UUIDs for all the prims that make up this group.
        ///
        /// This is called by methods which want to add a new group to an existing scene, in order
        /// to ensure that there are no clashes with groups already present.
        /// </summary>
        public void ResetIDs()
        {
            // As this is only ever called for prims which are not currently part of the scene (and hence
            // not accessible by clients), there should be no need to lock
            List<SceneObjectPart> partsList = new List<SceneObjectPart>(m_parts.Values);
            m_parts.Clear();
            foreach (SceneObjectPart part in partsList)
            {
                part.ResetIDs(part.LinkNum); // Don't change link nums
                m_parts.Add(part.UUID, part);
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="part"></param>
        public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags)
        {

            remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.ObjectOwner, RootPart.GroupID, RootPart.BaseMask,
                                                        RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask,
                                                        RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category,
                                                        RootPart.CreatorID, RootPart.Name, RootPart.Description);
        }

        public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID)
        {
            part.OwnerID = cAgentID;
            part.GroupID = cGroupID;
        }

        #endregion

        #region Scheduling

        /// <summary>
        /// Examine this object's parts to see if they've changed sufficiently to warrant an update
        /// </summary>
        public override void Update()
        {
            // Check that the group was not deleted before the scheduled update
            // FIXME: This is merely a temporary measure to reduce the incidence of failure when
            // an object has been deleted from a scene before update was processed.
            // A more fundamental overhaul of the update mechanism is required to eliminate all
            // the race conditions.
            if (m_isDeleted)
                return;

            // This is what happens when an orphanced link set child prim's
            // group was queued when it was linked
            //
            if (m_rootPart == null)
                return;

            lock (m_parts)
            {
                //if (m_rootPart.m_IsAttachment)
                //{
                    //foreach (SceneObjectPart part in m_parts.Values)
                    //{
                        //part.SendScheduledUpdates();
                    //}
                    //return;
                //}

                if (Util.GetDistanceTo(lastPhysGroupPos, AbsolutePosition) > 0.02)
                {
                    m_rootPart.UpdateFlag = 1;
                    lastPhysGroupPos = AbsolutePosition;
                }
                    //foreach (SceneObjectPart part in m_parts.Values)
                    //{
                        //if (part.UpdateFlag == 0) part.UpdateFlag = 1;
                    //}

                checkAtTargets();

                if ((Math.Abs(lastPhysGroupRot.W - GroupRotation.W) > 0.1)
                    || (Math.Abs(lastPhysGroupRot.X - GroupRotation.X) > 0.1)
                    || (Math.Abs(lastPhysGroupRot.Y - GroupRotation.Y) > 0.1)
                    || (Math.Abs(lastPhysGroupRot.Z - GroupRotation.Z) > 0.1))
                {
                    m_rootPart.UpdateFlag = 1;

                    lastPhysGroupRot = GroupRotation;
                }

                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.SendScheduledUpdates();
                }
            }
        }

        public void ScheduleFullUpdateToAvatar(ScenePresence presence)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.AddFullUpdateToAvatar(presence);
                }
            }
        }

        public void ScheduleTerseUpdateToAvatar(ScenePresence presence)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.AddTerseUpdateToAvatar(presence);
                }
            }
        }

        /// <summary>
        /// Schedule a full update for this scene object
        /// </summary>
        public void ScheduleGroupForFullUpdate()
        {
            checkAtTargets();
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.ScheduleFullUpdate();
                }
            }
        }

        /// <summary>
        /// Schedule a terse update for this scene object
        /// </summary>
        public void ScheduleGroupForTerseUpdate()
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.ScheduleTerseUpdate();
                }
            }
        }

        /// <summary>
        /// Immediately send a full update for this scene object.
        /// </summary>
        public void SendGroupFullUpdate()
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.SendFullUpdateToAllClients();
                }
            }
        }

        public void QueueForUpdateCheck()
        {
            m_scene.m_innerScene.AddToUpdateList(this);
        }

        /// <summary>
        /// Immediately send a terse update for this scene object.
        /// </summary>
        public void SendGroupTerseUpdate()
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.SendTerseUpdateToAllClients();
                }
            }
        }

        #endregion

        #region SceneGroupPart Methods

        /// <summary>
        /// Get the child part by LinkNum
        /// </summary>
        /// <param name="linknum"></param>
        /// <returns>null if no child part with that linknum or child part</returns>
        public SceneObjectPart GetLinkNumPart(int linknum)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (part.LinkNum == linknum)
                    {
                        return part;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Get a child part with a given UUID
        /// </summary>
        /// <param name="primID"></param>
        /// <returns>null if a child part with the primID was not found</returns>
        public SceneObjectPart GetChildPart(UUID primID)
        {
            SceneObjectPart childPart = null;
            if (m_parts.ContainsKey(primID))
            {
                childPart = m_parts[primID];
            }
            return childPart;
        }

        /// <summary>
        /// Get a child part with a given local ID
        /// </summary>
        /// <param name="localID"></param>
        /// <returns>null if a child part with the local ID was not found</returns>
        public SceneObjectPart GetChildPart(uint localID)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (part.LocalId == localID)
                    {
                        return part;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Does this group contain the child prim
        /// should be able to remove these methods once we have a entity index in scene
        /// </summary>
        /// <param name="primID"></param>
        /// <returns></returns>
        public bool HasChildPrim(UUID primID)
        {
            if (m_parts.ContainsKey(primID))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Does this group contain the child prim
        /// should be able to remove these methods once we have a entity index in scene
        /// </summary>
        /// <param name="localID"></param>
        /// <returns></returns>
        public bool HasChildPrim(uint localID)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    if (part.LocalId == localID)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        #endregion

        #region Packet Handlers

        /// <summary>
        /// Link the prims in a given group to this group
        /// </summary>
        /// <param name="objectGroup">The group of prims which should be linked to this group</param>
        public void LinkToGroup(SceneObjectGroup objectGroup)
        {
            if (objectGroup.RootPart.UpdateFlag > 0)
            {
                // I've never actually seen this happen, though I think it's theoretically possible
                m_log.WarnFormat(
                    "[SCENE OBJECT GROUP]: Aborted linking {0}, {1} to {2}, {3} as it has yet to finish delinking",
                    objectGroup.RootPart.Name, objectGroup.RootPart.UUID, RootPart.Name, RootPart.UUID);

                return;
            }

//            m_log.DebugFormat(
//                "[SCENE OBJECT GROUP]: Linking group with root part {0}, {1} to group with root part {2}, {3}",
//                objectGroup.RootPart.Name, objectGroup.RootPart.UUID, RootPart.Name, RootPart.UUID);

            SceneObjectPart linkPart = objectGroup.m_rootPart;

            Vector3 oldGroupPosition = linkPart.GroupPosition;
            Quaternion oldRootRotation = linkPart.RotationOffset;

            linkPart.OffsetPosition = linkPart.GroupPosition - AbsolutePosition;
            linkPart.GroupPosition = AbsolutePosition;
            Vector3 axPos = linkPart.OffsetPosition;

            Quaternion parentRot = m_rootPart.RotationOffset;
            axPos *= Quaternion.Inverse(parentRot);

            linkPart.OffsetPosition = axPos;
            Quaternion oldRot = linkPart.RotationOffset;
            Quaternion newRot = Quaternion.Inverse(parentRot) * oldRot;
            linkPart.RotationOffset = newRot;

            linkPart.ParentID = m_rootPart.LocalId;
            if (m_rootPart.LinkNum == 0)
                m_rootPart.LinkNum = 1;

            lock (m_parts)
            {
                m_parts.Add(linkPart.UUID, linkPart);
            }

            linkPart.LinkNum = m_parts.Count;

            linkPart.SetParent(this);
            linkPart.AddFlag(PrimFlags.CreateSelected);

            //if (linkPart.PhysActor != null)
            //{
            // m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor);

            //linkPart.PhysActor = null;
            //}

            //TODO: rest of parts
            foreach (SceneObjectPart part in objectGroup.Children.Values)
            {
                if (part.UUID != objectGroup.m_rootPart.UUID)
                {
                    LinkNonRootPart(part, oldGroupPosition, oldRootRotation);
                }
                part.ClearUndoState();
            }

            m_scene.UnlinkSceneObject(objectGroup.UUID, true);
            objectGroup.Children.Clear();
            objectGroup.RootPart = null;

            // TODO Deleting the original group object may cause problems later on if they have already
            // made it into the update queue.  However, sending out updates for those parts is now
            // spurious, so it would be good not to send them at some point.
            // The traffic caused is always going to be pretty minor, so it's not high priority
            //objectGroup.DeleteGroup();

            HasGroupChanged = true;
            ScheduleGroupForFullUpdate();
        }

        /// <summary>
        /// Delink the given prim from this group.  The delinked prim is established as
        /// an independent SceneObjectGroup.
        /// </summary>
        /// <param name="partID"></param>
        public void DelinkFromGroup(uint partID)
        {
            DelinkFromGroup(partID, true);
        }

        public void DelinkFromGroup(uint partID, bool sendEvents)
        {
            SceneObjectPart linkPart = GetChildPart(partID);

            if (null != linkPart)
            {
                linkPart.ClearUndoState();
//                m_log.DebugFormat(
//                    "[SCENE OBJECT GROUP]: Delinking part {0}, {1} from group with root part {2}, {3}",
//                    linkPart.Name, linkPart.UUID, RootPart.Name, RootPart.UUID);

                Quaternion worldRot = linkPart.GetWorldRotation();

                // Remove the part from this object
                lock (m_parts)
                {
                    m_parts.Remove(linkPart.UUID);
                }

                if (m_parts.Count == 1 && RootPart != null) //Single prim is left
                    RootPart.LinkNum = 0;
                else
                {
                    foreach (SceneObjectPart p in m_parts.Values)
                    {
                        if (p.LinkNum > linkPart.LinkNum)
                            p.LinkNum--;
                    }
                }

                linkPart.ParentID = 0;
                linkPart.LinkNum = 0;

                if (linkPart.PhysActor != null)
                {
                    m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor);
                }

                // We need to reset the child part's position
                // ready for life as a separate object after being a part of another object
                Quaternion parentRot = m_rootPart.RotationOffset;

                Vector3 axPos = linkPart.OffsetPosition;

                axPos *= parentRot;
                linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z);
                linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition;
                linkPart.OffsetPosition = new Vector3(0, 0, 0);

                linkPart.RotationOffset = worldRot;

                SceneObjectGroup objectGroup = new SceneObjectGroup(m_scene, m_regionHandle, linkPart);

                m_scene.AddNewSceneObject(objectGroup, true);

                if (sendEvents)
                    linkPart.TriggerScriptChangedEvent(Changed.LINK);

                HasGroupChanged = true;
                ScheduleGroupForFullUpdate();
            }
            else
            {
                m_log.InfoFormat("[SCENE OBJECT GROUP]: " +
                                 "DelinkFromGroup(): Child prim {0} not found in object {1}, {2}",
                                 partID, LocalId, UUID);
            }
        }

        /// <summary>
        /// Stop this object from being persisted over server restarts.
        /// </summary>
        /// <param name="objectGroup"></param>
        public void DetachFromBackup()
        {
            m_scene.EventManager.OnBackup -= ProcessBackup;
        }

        private void LinkNonRootPart(SceneObjectPart part, Vector3 oldGroupPosition, Quaternion oldGroupRotation)
        {
            part.SetParent(this);
            part.ParentID = m_rootPart.LocalId;

            lock (m_parts)
            {
                m_parts.Add(part.UUID, part);
            }

            part.LinkNum = m_parts.Count;

            Vector3 oldPos = part.OffsetPosition;
            oldPos *= oldGroupRotation;
            oldPos += oldGroupPosition;
            Vector3 oldAbsolutePosition = oldPos;
            part.OffsetPosition = oldAbsolutePosition - AbsolutePosition;

            Quaternion rootRotation = m_rootPart.RotationOffset;

            Vector3 pos = part.OffsetPosition;
            pos *= Quaternion.Inverse(rootRotation);
            part.OffsetPosition = pos;

            Quaternion partRotation = part.RotationOffset;

            partRotation *= oldGroupRotation;
            partRotation *= Quaternion.Inverse(rootRotation);
            part.RotationOffset = partRotation;
        }

        /// <summary>
        /// If object is physical, apply force to move it around
        /// If object is not physical, just put it at the resulting location
        /// </summary>
        /// <param name="offset">Always seems to be 0,0,0, so ignoring</param>
        /// <param name="pos">New position.  We do the math here to turn it into a force</param>
        /// <param name="remoteClient"></param>
        public void GrabMovement(Vector3 offset, Vector3 pos, IClientAPI remoteClient)
        {
            if (m_scene.EventManager.TriggerGroupMove(UUID, pos))
            {
                if (m_rootPart.PhysActor != null)
                {
                    if (m_rootPart.PhysActor.IsPhysical)
                    {
                        Vector3 llmoveforce = pos - AbsolutePosition;
                        PhysicsVector grabforce = new PhysicsVector(llmoveforce.X, llmoveforce.Y, llmoveforce.Z);
                        grabforce = (grabforce / 10) * m_rootPart.PhysActor.Mass;
                        m_rootPart.PhysActor.AddForce(grabforce,true);
                        m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
                    }
                    else
                    {
                        //NonPhysicalGrabMovement(pos);
                    }
                }
                else
                {
                    //NonPhysicalGrabMovement(pos);
                }
            }
        }

        public void NonPhysicalGrabMovement(Vector3 pos)
        {
            AbsolutePosition = pos;
            m_rootPart.SendTerseUpdateToAllClients();
        }

        /// <summary>
        /// Return metadata about a prim (name, description, sale price, etc.)
        /// </summary>
        /// <param name="client"></param>
        public void GetProperties(IClientAPI client)
        {
            m_rootPart.GetProperties(client);
        }

        /// <summary>
        /// Set the name of a prim
        /// </summary>
        /// <param name="name"></param>
        /// <param name="localID"></param>
        public void SetPartName(string name, uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                part.Name = name;
            }
        }

        public void SetPartDescription(string des, uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                part.Description = des;
            }
        }

        public void SetPartText(string text, uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                part.SetText(text);
            }
        }

        public void SetPartText(string text, UUID partID)
        {
            SceneObjectPart part = GetChildPart(partID);
            if (part != null)
            {
                part.SetText(text);
            }
        }

        public string GetPartName(uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                return part.Name;
            }
            return String.Empty;
        }

        public string GetPartDescription(uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                return part.Description;
            }
            return String.Empty;
        }

        /// <summary>
        /// Update prim flags for this group.
        /// </summary>
        /// <param name="localID"></param>
        /// <param name="type"></param>
        /// <param name="inUse"></param>
        /// <param name="data"></param>
        public void UpdatePrimFlags(uint localID, ushort type, bool inUse, byte[] data)
        {
            SceneObjectPart selectionPart = GetChildPart(localID);

            if (data[47] != 0) // Temporary
            {
                DetachFromBackup();
                // Remove from database and parcel prim count
                //
                m_scene.DeleteFromStorage(UUID);
                m_scene.EventManager.TriggerParcelPrimCountTainted();
            }

            if (selectionPart != null)
            {
                lock (m_parts)
                {
                    foreach (SceneObjectPart part in m_parts.Values)
                    {
                        if (part.Scale.X > 10.0 || part.Scale.Y > 10.0 || part.Scale.Z > 10.0)
                        {
                            data[46] = 0; // Reset physics
                            break;
                        }
                    }

                    foreach (SceneObjectPart part in m_parts.Values)
                    {
                        part.UpdatePrimFlags(type, inUse, data);
                    }
                }
            }
        }

        public void UpdateExtraParam(uint localID, ushort type, bool inUse, byte[] data)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                part.UpdateExtraParam(type, inUse, data);
            }
        }

        /// <summary>
        /// Get the parts of this scene object
        /// </summary>
        /// <returns></returns>
        public SceneObjectPart[] GetParts()
        {
            int numParts = Children.Count;
            SceneObjectPart[] partArray = new SceneObjectPart[numParts];
            Children.Values.CopyTo(partArray, 0);
            return partArray;
        }

        /// <summary>
        /// Update the texture entry for this part
        /// </summary>
        /// <param name="localID"></param>
        /// <param name="textureEntry"></param>
        public void UpdateTextureEntry(uint localID, byte[] textureEntry)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                part.UpdateTextureEntry(textureEntry);
            }
        }

        public void UpdatePermissions(UUID AgentID, byte field, uint localID,
                uint mask, byte addRemTF)
        {
            foreach (SceneObjectPart part in m_parts.Values)
                part.UpdatePermissions(AgentID, field, localID, mask,
                        addRemTF);

            HasGroupChanged = true;
        }

        #endregion

        #region Shape

        /// <summary>
        ///
        /// </summary>
        /// <param name="shapeBlock"></param>
        public void UpdateShape(ObjectShapePacket.ObjectDataBlock shapeBlock, uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                part.UpdateShape(shapeBlock);

                if (part.PhysActor != null)
                    m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor);
            }
        }

        #endregion

        #region Resize

        /// <summary>
        /// Resize the given part
        /// </summary>
        /// <param name="scale"></param>
        /// <param name="localID"></param>
        public void Resize(Vector3 scale, uint localID)
        {
            if (scale.X > m_scene.m_maxNonphys)
                scale.X = m_scene.m_maxNonphys;
            if (scale.Y > m_scene.m_maxNonphys)
                scale.Y = m_scene.m_maxNonphys;
            if (scale.Z > m_scene.m_maxNonphys)
                scale.Z = m_scene.m_maxNonphys;

            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                part.Resize(scale);
                if (part.PhysActor != null)
                {
                    if (part.PhysActor.IsPhysical)
                    {
                        if (scale.X > m_scene.m_maxPhys)
                            scale.X = m_scene.m_maxPhys;
                        if (scale.Y > m_scene.m_maxPhys)
                            scale.Y = m_scene.m_maxPhys;
                        if (scale.Z > m_scene.m_maxPhys)
                            scale.Z = m_scene.m_maxPhys;
                    }
                    part.PhysActor.Size =
                        new PhysicsVector(scale.X, scale.Y, scale.Z);
                    m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor);
                }
                //if (part.UUID != m_rootPart.UUID)

                HasGroupChanged = true;
                ScheduleGroupForFullUpdate();

                //if (part.UUID == m_rootPart.UUID)
                //{
                //if (m_rootPart.PhysActor != null)
                //{
                //m_rootPart.PhysActor.Size =
                //new PhysicsVector(m_rootPart.Scale.X, m_rootPart.Scale.Y, m_rootPart.Scale.Z);
                //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
                //}
                //}
            }
        }

        public void GroupResize(Vector3 scale, uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                if (scale.X > m_scene.m_maxNonphys)
                    scale.X = m_scene.m_maxNonphys;
                if (scale.Y > m_scene.m_maxNonphys)
                    scale.Y = m_scene.m_maxNonphys;
                if (scale.Z > m_scene.m_maxNonphys)
                    scale.Z = m_scene.m_maxNonphys;
                if (part.PhysActor != null && part.PhysActor.IsPhysical)
                {
                    if (scale.X > m_scene.m_maxPhys)
                        scale.X = m_scene.m_maxPhys;
                    if (scale.Y > m_scene.m_maxPhys)
                        scale.Y = m_scene.m_maxPhys;
                    if (scale.Z > m_scene.m_maxPhys)
                        scale.Z = m_scene.m_maxPhys;
                }
                float x = (scale.X / part.Scale.X);
                float y = (scale.Y / part.Scale.Y);
                float z = (scale.Z / part.Scale.Z);

                lock (m_parts)
                {
                    if (x > 1.0f || y > 1.0f || z > 1.0f)
                    {
                        foreach (SceneObjectPart obPart in m_parts.Values)
                        {
                            if (obPart.UUID != m_rootPart.UUID)
                            {
                                Vector3 oldSize = new Vector3(obPart.Scale);

                                float f = 1.0f;
                                float a = 1.0f;

                                if (part.PhysActor != null && part.PhysActor.IsPhysical)
                                {
                                    if (oldSize.X*x > m_scene.m_maxPhys)
                                    {
                                        f = m_scene.m_maxPhys / oldSize.X;
                                        a = f / x;
                                        x *= a;
                                        y *= a;
                                        z *= a;
                                    }
                                    if (oldSize.Y*y > m_scene.m_maxPhys)
                                    {
                                        f = m_scene.m_maxPhys / oldSize.Y;
                                        a = f / y;
                                        x *= a;
                                        y *= a;
                                        z *= a;
                                    }
                                    if (oldSize.Z*z > m_scene.m_maxPhys)
                                    {
                                        f = m_scene.m_maxPhys / oldSize.Z;
                                        a = f / z;
                                        x *= a;
                                        y *= a;
                                        z *= a;
                                    }
                                }
                                else
                                {
                                    if (oldSize.X*x > m_scene.m_maxNonphys)
                                    {
                                        f = m_scene.m_maxNonphys / oldSize.X;
                                        a = f / x;
                                        x *= a;
                                        y *= a;
                                        z *= a;
                                    }
                                    if (oldSize.Y*y > m_scene.m_maxNonphys)
                                    {
                                        f = m_scene.m_maxNonphys / oldSize.Y;
                                        a = f / y;
                                        x *= a;
                                        y *= a;
                                        z *= a;
                                    }
                                    if (oldSize.Z*z > m_scene.m_maxNonphys)
                                    {
                                        f = m_scene.m_maxNonphys / oldSize.Z;
                                        a = f / z;
                                        x *= a;
                                        y *= a;
                                        z *= a;
                                    }
                                }
                            }
                        }
                    }
                }

                Vector3 prevScale = part.Scale;
                prevScale.X *= x;
                prevScale.Y *= y;
                prevScale.Z *= z;
                part.Resize(prevScale);

                lock (m_parts)
                {
                    foreach (SceneObjectPart obPart in m_parts.Values)
                    {
                        if (obPart.UUID != m_rootPart.UUID)
                        {
                            Vector3 currentpos = new Vector3(obPart.OffsetPosition);
                            currentpos.X *= x;
                            currentpos.Y *= y;
                            currentpos.Z *= z;
                            Vector3 newSize = new Vector3(obPart.Scale);
                            newSize.X *= x;
                            newSize.Y *= y;
                            newSize.Z *= z;
                            obPart.Resize(newSize);
                            obPart.UpdateOffSet(currentpos);
                        }
                    }
                }

                if (part.PhysActor != null)
                {
                    part.PhysActor.Size =
                        new PhysicsVector(prevScale.X, prevScale.Y, prevScale.Z);
                    m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor);
                }

                HasGroupChanged = true;
                ScheduleGroupForTerseUpdate();
            }
        }

        #endregion

        #region Position

        /// <summary>
        /// Move this scene object
        /// </summary>
        /// <param name="pos"></param>
        public void UpdateGroupPosition(Vector3 pos)
        {
            if (m_scene.EventManager.TriggerGroupMove(UUID, pos))
            {
                if (m_rootPart.IsAttachment)
                {
                    m_rootPart.AttachedPos = pos;
                }

                AbsolutePosition = pos;

                HasGroupChanged = true;
            }

            //we need to do a terse update even if the move wasn't allowed
            // so that the position is reset in the client (the object snaps back)
            ScheduleGroupForTerseUpdate();
        }

        /// <summary>
        /// Update the position of a single part of this scene object
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="localID"></param>
        public void UpdateSinglePosition(Vector3 pos, uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);

            if (part != null)
            {
                if (part.UUID == m_rootPart.UUID)
                {
                    UpdateRootPosition(pos);
                }
                else
                {
                    part.UpdateOffSet(pos);
                }

                HasGroupChanged = true;
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="pos"></param>
        private void UpdateRootPosition(Vector3 pos)
        {
            Vector3 newPos = new Vector3(pos.X, pos.Y, pos.Z);
            Vector3 oldPos =
                new Vector3(AbsolutePosition.X + m_rootPart.OffsetPosition.X,
                              AbsolutePosition.Y + m_rootPart.OffsetPosition.Y,
                              AbsolutePosition.Z + m_rootPart.OffsetPosition.Z);
            Vector3 diff = oldPos - newPos;
            Vector3 axDiff = new Vector3(diff.X, diff.Y, diff.Z);
            Quaternion partRotation = m_rootPart.RotationOffset;
            axDiff *= Quaternion.Inverse(partRotation);
            diff = axDiff;

            lock (m_parts)
            {
                foreach (SceneObjectPart obPart in m_parts.Values)
                {
                    if (obPart.UUID != m_rootPart.UUID)
                    {
                        obPart.OffsetPosition = obPart.OffsetPosition + diff;
                    }
                }
            }

            AbsolutePosition = newPos;

            HasGroupChanged = true;
            ScheduleGroupForTerseUpdate();
        }

        public void OffsetForNewRegion(Vector3 offset)
        {
            m_rootPart.GroupPosition = offset;
        }

        #endregion

        #region Rotation

        /// <summary>
        ///
        /// </summary>
        /// <param name="rot"></param>
        public void UpdateGroupRotation(Quaternion rot)
        {
            m_rootPart.UpdateRotation(rot);
            if (m_rootPart.PhysActor != null)
            {
                m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset;
                m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
            }

            HasGroupChanged = true;
            ScheduleGroupForTerseUpdate();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="rot"></param>
        public void UpdateGroupRotation(Vector3 pos, Quaternion rot)
        {
            m_rootPart.UpdateRotation(rot);
            if (m_rootPart.PhysActor != null)
            {
                m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset;
                m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
            }
            AbsolutePosition = pos;

            HasGroupChanged = true;
            ScheduleGroupForTerseUpdate();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="rot"></param>
        /// <param name="localID"></param>
        public void UpdateSingleRotation(Quaternion rot, uint localID)
        {
            SceneObjectPart part = GetChildPart(localID);
            if (part != null)
            {
                if (part.UUID == m_rootPart.UUID)
                {
                    UpdateRootRotation(rot);
                }
                else
                {
                    part.UpdateRotation(rot);
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="rot"></param>
        private void UpdateRootRotation(Quaternion rot)
        {
            Quaternion axRot = rot;
            Quaternion oldParentRot = m_rootPart.RotationOffset;

            m_rootPart.UpdateRotation(rot);
            if (m_rootPart.PhysActor != null)
            {
                m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset;
                m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
            }

            lock (m_parts)
            {
                foreach (SceneObjectPart prim in m_parts.Values)
                {
                    if (prim.UUID != m_rootPart.UUID)
                    {
                        Vector3 axPos = prim.OffsetPosition;
                        axPos *= oldParentRot;
                        axPos *= Quaternion.Inverse(axRot);
                        prim.OffsetPosition = axPos;
                        Quaternion primsRot = prim.RotationOffset;
                        Quaternion newRot = oldParentRot * primsRot;
                        newRot *= Quaternion.Inverse(axRot);
                        prim.RotationOffset = newRot;
                        prim.ScheduleTerseUpdate();
                    }
                }
            }

            m_rootPart.ScheduleTerseUpdate();
        }

        #endregion

        internal void SetAxisRotation(int axis, int rotate10)
        {
            bool setX = false;
            bool setY = false;
            bool setZ = false;

            int xaxis = 2;
            int yaxis = 4;
            int zaxis = 8;

            if (m_rootPart != null)
            {
                setX = ((axis & xaxis) != 0) ? true : false;
                setY = ((axis & yaxis) != 0) ? true : false;
                setZ = ((axis & zaxis) != 0) ? true : false;

                float setval = (rotate10 > 0) ? 1f : 0f;

                if (setX)
                    m_rootPart.RotationAxis.X = setval;
                if (setY)
                    m_rootPart.RotationAxis.Y = setval;
                if (setZ)
                    m_rootPart.RotationAxis.Z = setval;

                if (setX || setY || setZ)
                {
                    m_rootPart.SetPhysicsAxisRotation();
                }

            }
        }

        public int registerTargetWaypoint(Vector3 target, float tolerance)
        {
            scriptPosTarget waypoint = new scriptPosTarget();
            waypoint.targetPos = target;
            waypoint.tolerance = tolerance;
            uint handle = m_scene.PrimIDAllocate();
            lock (m_targets)
            {
                m_targets.Add(handle, waypoint);
            }
            return (int)handle;
        }
        public void unregisterTargetWaypoint(int handle)
        {
            lock (m_targets)
            {
                if (m_targets.ContainsKey((uint)handle))
                    m_targets.Remove((uint)handle);
            }
        }

        private void checkAtTargets()
        {
            if (m_scriptListens_atTarget || m_scriptListens_notAtTarget)
            {
                if (m_targets.Count > 0)
                {
                    bool at_target = false;
                    //Vector3 targetPos;
                    //uint targetHandle;
                    Dictionary<uint, scriptPosTarget> atTargets = new Dictionary<uint, scriptPosTarget>();
                    lock (m_targets)
                    {
                        foreach (uint idx in m_targets.Keys)
                        {
                            scriptPosTarget target = m_targets[idx];
                            if (Util.GetDistanceTo(target.targetPos, m_rootPart.GroupPosition) <= target.tolerance)
                            {
                                // trigger at_target
                                if (m_scriptListens_atTarget)
                                {
                                    // Reusing att.tolerance to hold the index of the target in the targets dictionary
                                    // to avoid deadlocking the sim.
                                    at_target = true;
                                    scriptPosTarget att = new scriptPosTarget();
                                    att.targetPos = target.targetPos;
                                    att.tolerance = (float)idx;
                                    atTargets.Add(idx, att);
                                }
                            }
                        }
                    }
                    if (atTargets.Count > 0)
                    {
                        uint[] localids = new uint[0];
                        lock (m_parts)
                        {
                            localids = new uint[m_parts.Count];
                            int cntr = 0;
                            foreach (SceneObjectPart part in m_parts.Values)
                            {
                                localids[cntr] = part.LocalId;
                                cntr++;
                            }
                        }
                        for (int ctr = 0; ctr < localids.Length; ctr++)
                        {
                            foreach (uint target in atTargets.Keys)
                            {
                                scriptPosTarget att = atTargets[target];
                                // Reusing att.tolerance to hold the index of the target in the targets dictionary
                                // to avoid deadlocking the sim.
                                m_scene.TriggerAtTargetEvent(localids[ctr], (uint)att.tolerance, att.targetPos, m_rootPart.GroupPosition);


                            }
                        }
                        return;
                    }
                    if (m_scriptListens_notAtTarget && !at_target)
                    {
                        //trigger not_at_target
                        uint[] localids = new uint[0];
                        lock (m_parts)
                        {
                            localids = new uint[m_parts.Count];
                            int cntr = 0;
                            foreach (SceneObjectPart part in m_parts.Values)
                            {
                                localids[cntr] = part.LocalId;
                                cntr++;
                            }
                        }
                        for (int ctr = 0; ctr < localids.Length; ctr++)
                        {
                            m_scene.TriggerNotAtTargetEvent(localids[ctr]);
                        }
                    }
                }
            }
        }
        public float GetMass()
        {
            float retmass = 0f;
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    retmass += part.GetMass();
                }
            }
            return retmass;
        }
        public void CheckSculptAndLoad()
        {
            lock (m_parts)
            {
                if (RootPart != null)
                {
                    if ((RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Phantom) == 0)
                    {
                        foreach (SceneObjectPart part in m_parts.Values)
                        {
                            if (part.Shape.SculptEntry && part.Shape.SculptTexture != UUID.Zero)
                            {
                                m_scene.AssetCache.GetAsset(part.Shape.SculptTexture, part.SculptTextureCallback, true);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Set the user group to which this scene object belongs.
        /// </summary>
        /// <param name="GroupID"></param>
        /// <param name="client"></param>
        public void SetGroup(UUID GroupID, IClientAPI client)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                {
                    part.SetGroup(GroupID, client);
                }

                HasGroupChanged = true;
            }

            ScheduleGroupForFullUpdate();
        }

        public void TriggerScriptChangedEvent(Changed val)
        {
            foreach (SceneObjectPart part in Children.Values)
            {
                part.TriggerScriptChangedEvent(val);
            }
        }
        
        public override string ToString()
        {
            return String.Format("{0} {1} ({2})", Name, UUID, AbsolutePosition);
        }

        public void SetAttachmentPoint(byte point)
        {
            lock (m_parts)
            {
                foreach (SceneObjectPart part in m_parts.Values)
                    part.SetAttachmentPoint(point);
            }
        }
    }
}