context: using System;
  using System.IO;
  using System.Collections;
  using Server;
  using Server.Gumps;
  using Server.Items;
  using Server.Commands;
  using Server.Targeting;
  using System.Collections.Generic;
  
  namespace Server
  {
  	public class Statics
  	{
  		public static void Initialize()
  		{
  			CommandSystem.Register( "Freeze", AccessLevel.Administrator, new CommandEventHandler( Freeze_OnCommand ) );
  			CommandSystem.Register( "FreezeMap", AccessLevel.Administrator, new CommandEventHandler( FreezeMap_OnCommand ) );
  			CommandSystem.Register( "FreezeWorld", AccessLevel.Administrator, new CommandEventHandler( FreezeWorld_OnCommand ) );
  
  			CommandSystem.Register( "Unfreeze", AccessLevel.Administrator, new CommandEventHandler( Unfreeze_OnCommand ) );
  			CommandSystem.Register( "UnfreezeMap", AccessLevel.Administrator, new CommandEventHandler( UnfreezeMap_OnCommand ) );
  			CommandSystem.Register( "UnfreezeWorld", AccessLevel.Administrator, new CommandEventHandler( UnfreezeWorld_OnCommand ) );
  		}
  
  		private static Point3D NullP3D = new Point3D( int.MinValue, int.MinValue, int.MinValue );
  
  		[Usage( "Freeze" )]
  		[Description( "Makes a targeted area of dynamic items static." )]
  		public static void Freeze_OnCommand( CommandEventArgs e )
  		{
  			BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( FreezeBox_Callback ), null );
  		}
  
  		[Usage( "FreezeMap" )]
  		[Description( "Makes every dynamic item in your map static." )]
  		public static void FreezeMap_OnCommand( CommandEventArgs e )
  		{
  			Map map = e.Mobile.Map;
  
  			if ( map != null && map != Map.Internal )
  				SendWarning( e.Mobile, "You are about to freeze <u>all items in {0}</u>.", BaseFreezeWarning, map, NullP3D, NullP3D, new WarningGumpCallback( FreezeWarning_Callback ) );
  		}
  
  		[Usage( "FreezeWorld" )]
  		[Description( "Makes every dynamic item on all maps static." )]
  		public static void FreezeWorld_OnCommand( CommandEventArgs e )
  		{
  			SendWarning( e.Mobile, "You are about to freeze <u>every item on every map</u>.", BaseFreezeWarning, null, NullP3D, NullP3D, new WarningGumpCallback( FreezeWarning_Callback ) );
  		}
  
  		public static void SendWarning( Mobile m, string header, string baseWarning, Map map, Point3D start, Point3D end, WarningGumpCallback callback )
  		{
  			m.SendGump( new WarningGump( 1060635, 30720, String.Format( baseWarning, String.Format( header, map ) ), 0xFFC000, 420, 400, callback, new StateInfo( map, start, end ) ) );
  		}
  
  		private const string BaseFreezeWarning =	"{0}  " +
  													"Those items <u>will be removed from the world</u> and placed into the server data files.  " +
  													"Other players <u>will not see the changes</u> unless you distribute your data files to them.<br><br>" +
  													"This operation may not complete unless the server and client are using different data files.  " +
  													"If you receive a message stating 'output data files could not be opened,' then you are probably sharing data files.  " +
  													"Create a new directory for the world data files (statics*.mul and staidx*.mul) and add that to Scritps/Misc/DataPath.cs.<br><br>" +
  													"The change will be in effect immediately on the server, however, you must restart your client and update it's data files for the changes to become visible.  " +
  													"It is strongly recommended that you make backup of the data files mentioned above.  " +
  													"Do you wish to proceed?";
  
  		private static void FreezeBox_Callback( Mobile from, Map map, Point3D start, Point3D end, object state )
  		{
  			SendWarning( from, "You are about to freeze a section of items.", BaseFreezeWarning, map, start, end, new WarningGumpCallback( FreezeWarning_Callback ) );
  		}
  
  		private static void FreezeWarning_Callback( Mobile from, bool okay, object state )
  		{
  			if ( !okay )
  				return;
  
  			StateInfo si = (StateInfo)state;
  
  			Freeze( from, si.m_Map, si.m_Start, si.m_End );
  		}
  
  		public static void Freeze( Mobile from, Map targetMap, Point3D start3d, Point3D end3d )
  		{
  			Hashtable mapTable = new Hashtable();
  
  			if ( start3d == NullP3D && end3d == NullP3D )
  			{
  				if ( targetMap == null )
  					CommandLogging.WriteLine( from, "{0} {1} invoking freeze for every item in every map", from.AccessLevel, CommandLogging.Format( from ) );
  				else
  					CommandLogging.WriteLine( from, "{0} {1} invoking freeze for every item in {0}", from.AccessLevel, CommandLogging.Format( from ), targetMap );
  
  				foreach ( Item item in World.Items.Values )
  				{
  					if ( targetMap != null && item.Map != targetMap )
  						continue;
  
  					if ( item.Parent != null )
  						continue;
  
  					if ( item is Static || item is BaseFloor || item is BaseWall )
  					{
  						Map itemMap = item.Map;
  
  						if ( itemMap == null || itemMap == Map.Internal )
  							continue;
  
  						Hashtable table = (Hashtable)mapTable[itemMap];
  
  						if ( table == null )
  							mapTable[itemMap] = table = new Hashtable();
  
  						Point2D p = new Point2D( item.X >> 3, item.Y >> 3 );
  
  						DeltaState state = (DeltaState)table[p];
  
  						if ( state == null )
  							table[p] = state = new DeltaState( p );
  
  						state.m_List.Add( item );
  					}
  				}
  			}
  			else if ( targetMap != null )
  			{
  				Point2D start = targetMap.Bound( new Point2D( start3d ) ), end = targetMap.Bound( new Point2D( end3d ) );
  
  				CommandLogging.WriteLine( from, "{0} {1} invoking freeze from {2} to {3} in {4}", from.AccessLevel, CommandLogging.Format( from ), start, end, targetMap );
  
  				IPooledEnumerable eable = targetMap.GetItemsInBounds( new Rectangle2D( start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1 ) );
  
  				foreach ( Item item in eable )
  				{
  					if ( item is Static || item is BaseFloor || item is BaseWall )
  					{
  						Map itemMap = item.Map;
  
  						if ( itemMap == null || itemMap == Map.Internal )
  							continue;
  
  						Hashtable table = (Hashtable)mapTable[itemMap];
  
  						if ( table == null )
  							mapTable[itemMap] = table = new Hashtable();
  
  						Point2D p = new Point2D( item.X >> 3, item.Y >> 3 );
  
  						DeltaState state = (DeltaState)table[p];
  
  						if ( state == null )
  							table[p] = state = new DeltaState( p );
  
  						state.m_List.Add( item );
  					}
  				}
  
  				eable.Free();
  			}
  
  			if ( mapTable.Count == 0 )
  			{
  				from.SendGump( new NoticeGump( 1060637, 30720, "No freezable items were found.  Only the following item types are frozen:<br> - Static<br> - BaseFloor<br> - BaseWall", 0xFFC000, 320, 240, null, null ) );
  				return;
  			}
  
  			bool badDataFile = false;
  
  			int totalFrozen = 0;
  
  			foreach ( DictionaryEntry de in mapTable )
  			{
  				Map map = (Map)de.Key;
  				Hashtable table = (Hashtable)de.Value;
  
  				TileMatrix matrix = map.Tiles;
  
  				using ( FileStream idxStream = OpenWrite( matrix.IndexStream ) )
  				{
  					using ( FileStream mulStream = OpenWrite( matrix.DataStream ) )
  					{
  						if ( idxStream == null || mulStream == null )
  						{
  							badDataFile = true;
  							continue;
  						}
  
  						BinaryReader idxReader = new BinaryReader( idxStream );
  
  						BinaryWriter idxWriter = new BinaryWriter( idxStream );
  						BinaryWriter mulWriter = new BinaryWriter( mulStream );
  
  						foreach ( DeltaState state in table.Values )
  						{
  							int oldTileCount;
  							StaticTile[] oldTiles = ReadStaticBlock( idxReader, mulStream, state.m_X, state.m_Y, matrix.BlockWidth, matrix.BlockHeight, out oldTileCount );
  
  							if ( oldTileCount < 0 )
  								continue;
  
  							int newTileCount = 0;
  							StaticTile[] newTiles = new StaticTile[state.m_List.Count];
  
  							for ( int i = 0; i < state.m_List.Count; ++i )
  							{
  								Item item = state.m_List[i];
  
  								int xOffset = item.X - (state.m_X * 8);
  								int yOffset = item.Y - (state.m_Y * 8);
  
  								if ( xOffset < 0 || xOffset >= 8 || yOffset < 0 || yOffset >= 8 )
  									continue;
  
  								StaticTile newTile = new StaticTile( (ushort)item.ItemID, (byte)xOffset, (byte)yOffset, (sbyte)item.Z, (short)item.Hue );
  
  								newTiles[newTileCount++] = newTile;
  
  								item.Delete();
  
  								++totalFrozen;
  							}
  
  							int mulPos = -1;
  							int length = -1;
  							int extra = 0;
  
  							if ( (oldTileCount + newTileCount) > 0 )
  							{
  								mulWriter.Seek( 0, SeekOrigin.End );
  
  								mulPos = (int)mulWriter.BaseStream.Position;
  								length = (oldTileCount + newTileCount) * 7;
  								extra = 1;
  
  								for ( int i = 0; i < oldTileCount; ++i )
  								{
  
gt: 									StaticTile toWrite = oldTiles[i];