using System;
using Server;
using Server.Multis;
using Server.Mobiles;
using System.Collections.Generic;
using Server.ContextMenus;

namespace Server.Items
{
    public class MooringLine : Item
    {
        [CommandProperty(AccessLevel.GameMaster)]
        public BaseBoat Boat { get; private set; }

        public override int LabelNumber => Boat == null || Boat.IsRowBoat ? 1020935 : 1149697; // rope || mooring line

        public MooringLine(BaseBoat boat)
            : base(5368)
        {
            Boat = boat;
            Movable = false;
            Weight = 0;
        }

        public override void OnDoubleClickDead(Mobile m)
        {
            OnDoubleClick(m);
        }

        public override void OnDoubleClick(Mobile from)
        {
            if (Boat == null || from == null)
                return;

            BaseBoat boat = BaseBoat.FindBoatAt(from, from.Map);

            int range = boat != null && boat == Boat ? 3 : 8;
            bool canMove = false;

            if (!Boat.IsRowBoat)
                Boat.Refresh(from);

            if (!from.InRange(Location, range))
                from.SendLocalizedMessage(500295); //You are too far away to do that.
            else if (!from.InLOS(Location))
                from.SendLocalizedMessage(500950); //You cannot see that.
            else if (Boat.IsMoving || Boat.IsTurning)
                from.SendLocalizedMessage(1116611); //You can't use that while the ship is moving!
            else if (BaseBoat.IsDriving(from))
                from.SendLocalizedMessage(1116610); //You can't do that while piloting a ship!
            else if (BaseHouse.FindHouseAt(from) != null)
                from.SendLocalizedMessage(1149795); //You may not dock a ship while on another ship or inside a house.
            else if (Boat == boat && !MoveToNearestDockOrLand(from))
                from.SendLocalizedMessage(1149796); //You can not dock a ship this far out to sea. You must be near land or shallow water.
            else if (boat == null || boat != null && Boat != boat)
            {
                if (Boat.HasAccess(from))
                    canMove = true;
                else
                    from.SendLocalizedMessage(1116617); //You do not have permission to board this ship.
            }

            if (canMove)
            {
                BaseCreature.TeleportPets(from, Location, Map);
                from.MoveToWorld(Location, Map);

                Boat.SendContainerPacket();
            }
        }

        public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
        {
            base.GetContextMenuEntries(from, list);

            if (Boat != null && Boat.IsRowBoat && from.Alive && !Boat.Contains(from))
            {
                list.Add(new DryDockEntry(Boat, from));
            }
        }

        public bool MoveToNearestDockOrLand(Mobile from)
        {
            if ((Boat != null && !Boat.Contains(from)) || !ValidateDockOrLand())
                return false;

            Map map = Map;

            if (map == null)
                return false;

            Rectangle2D rec;
            Point3D nearest = Point3D.Zero;
            Point3D p = Point3D.Zero;

            if (Boat.IsRowBoat)
                rec = new Rectangle2D(Boat.X - 8, Boat.Y - 8, 16, 16);
            else
            {
                switch (Boat.Facing)
                {
                    default:
                    case Direction.North:
                    case Direction.South:
                        if (X < Boat.X)
                        {
                            rec = new Rectangle2D(X - 8, Y - 8, 8, 16);
                        }
                        else
                        {
                            rec = new Rectangle2D(X, Y - 8, 8, 16);
                        }
                        break;
                    case Direction.West:
                    case Direction.East:
                        if (Y < Boat.Y)
                        {
                            rec = new Rectangle2D(X - 8, Y - 8, 16, 8);
                        }
                        else
                        {
                            rec = new Rectangle2D(X - 8, Y, 16, 8);
                        }
                        break;
                }
            }

            for (int x = rec.X; x <= rec.X + rec.Width; x++)
            {
                for (int y = rec.Y; y <= rec.Y + rec.Height; y++)
                {
                    p = new Point3D(x, y, map.GetAverageZ(x, y));

                    if (ValidateTile(from, ref p))
                    {
                        if (nearest == Point3D.Zero || from.GetDistanceToSqrt(p) < from.GetDistanceToSqrt(nearest))
                            nearest = p;
                    }
                }
            }

            if (nearest != Point3D.Zero)
            {
                BaseCreature.TeleportPets(from, nearest, Map);
                from.MoveToWorld(nearest, Map);

                return true;
            }

            return false;
        }

        public bool ValidateTile(Mobile from, ref Point3D p)
        {
            int x = p.X;
            int y = p.Y;
            int z = p.Z;

            Map map = from.Map;

            if (Spells.SpellHelper.CheckMulti(p, map) || Region.Find(p, map).IsPartOf<Factions.StrongholdRegion>())
                return false;

            StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);
            object highest = null;

            //Gets highest tile, which will be used to determine if we can walk on it.
            foreach (StaticTile tile in staticTiles)
            {
                if (highest == null || (highest is StaticTile && tile.Z + tile.Height > ((StaticTile)highest).Z + ((StaticTile)highest).Height))
                    highest = tile;
            }

            if (highest != null && highest is StaticTile)
            {
                StaticTile st = (StaticTile)highest;

                ItemData id = TileData.ItemTable[st.ID & TileData.MaxItemValue];

                if (id.Surface && !id.Impassable)
                {
                    p.Z = st.Z + st.Height;
                    return from.InLOS(p);
                }

            }

            return map.CanFit(x, y, z, 16, false, false);
        }

        public bool ValidateDockOrLand()
        {
            return BaseGalleon.IsNearLandOrDocks(Boat);
        }

        public MooringLine(Serial serial)
            : base(serial)
        {
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);
            writer.Write((int)0);

            writer.Write(Boat);
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);
            int version = reader.ReadInt();

            Boat = reader.ReadItem() as BaseBoat;
        }
    }
}
