context: 
  #region ================== Copyright (c) 2007 Pascal vd Heiden
  
  /*
   * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
   * This program is released under GNU General Public License
   * 
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   * 
   */
  
  #endregion
  
  #region ================== Namespaces
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Globalization;
  using System.Text;
  using CodeImp.DoomBuilder.IO;
  using CodeImp.DoomBuilder.Geometry;
  using CodeImp.DoomBuilder.Types;
  
  #endregion
  
  namespace CodeImp.DoomBuilder.Map
  {
  	public sealed class Sidedef : MapElement
  	{
  		#region ================== Constants
  
  		#endregion
  
  		#region ================== Variables
  
  		// Map
  		private MapSet map;
  
  		// List items
  		private LinkedListNode<Sidedef> sectorlistitem;
  
  		// Owner
  		private Linedef linedef;
  		
  		// Sector
  		private Sector sector;
  
  		// Properties
  		private int offsetx;
  		private int offsety;
  		private string texnamehigh;
  		private string texnamemid;
  		private string texnamelow;
  		private long longtexnamehigh;
  		private long longtexnamemid;
  		private long longtexnamelow;
  
  		// Clone
  		private int serializedindex;
  		
  		#endregion
  
  		#region ================== Properties
  
  		public MapSet Map { get { return map; } }
  		public bool IsFront { get { return (linedef != null) ? (this == linedef.Front) : false; } }
  		public Linedef Line { get { return linedef; } }
  		public Sidedef Other { get { if(this == linedef.Front) return linedef.Back; else return linedef.Front; } }
  		public Sector Sector { get { return sector; } }
  		public float Angle { get { if(IsFront) return linedef.Angle; else return Angle2D.Normalized(linedef.Angle + Angle2D.PI); } }
  		public int OffsetX { get { return offsetx; } set { BeforePropsChange(); offsetx = value; } }
  		public int OffsetY { get { return offsety; } set { BeforePropsChange(); offsety = value; } }
  		public string HighTexture { get { return texnamehigh; } }
  		public string MiddleTexture { get { return texnamemid; } }
  		public string LowTexture { get { return texnamelow; } }
  		public long LongHighTexture { get { return longtexnamehigh; } }
  		public long LongMiddleTexture { get { return longtexnamemid; } }
  		public long LongLowTexture { get { return longtexnamelow; } }
  		internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } }
  		
  		#endregion
  
  		#region ================== Constructor / Disposer
  
  		// Constructor
  		internal Sidedef(MapSet map, int listindex, Linedef l, bool front, Sector s)
  		{
  			// Initialize
  			this.map = map;
  			this.listindex = listindex;
  			this.texnamehigh = "-";
  			this.texnamemid = "-";
  			this.texnamelow = "-";
  			this.longtexnamehigh = MapSet.EmptyLongName;
  			this.longtexnamemid = MapSet.EmptyLongName;
  			this.longtexnamelow = MapSet.EmptyLongName;
  			
  			// Attach linedef
  			this.linedef = l;
  			if(l != null)
  			{
  				if(front)
  					l.AttachFrontP(this);
  				else
  					l.AttachBackP(this);
  			}
  			
  			// Attach sector
  			SetSectorP(s);
  			
  			if(map == General.Map.Map)
  				General.Map.UndoRedo.RecAddSidedef(this);
  			
  			// We have no destructor
  			GC.SuppressFinalize(this);
  		}
  
  		// Disposer
  		public override void Dispose()
  		{
  			// Not already disposed?
  			if(!isdisposed)
  			{
  				// Already set isdisposed so that changes can be prohibited
  				isdisposed = true;
  
  				if(map == General.Map.Map)
  					General.Map.UndoRedo.RecRemSidedef(this);
  
  				// Remove from main list
  				map.RemoveSidedef(listindex);
  
  				// Detach from linedef
  				if(linedef != null) linedef.DetachSidedefP(this);
  				
  				// Detach from sector
  				SetSectorP(null);
  
  				// Clean up
  				sectorlistitem = null;
  				linedef = null;
  				map = null;
  				sector = null;
  
  				// Dispose base
  				base.Dispose();
  			}
  		}
  
  		#endregion
  
  		#region ================== Management
  		
  		// Call this before changing properties
  		protected override void BeforePropsChange()
  		{
  			if(map == General.Map.Map)
  				General.Map.UndoRedo.RecPrpSidedef(this);
  		}
  
          // Serialize / deserialize (passive: this doesn't record)
          // ano - using new keyword to shadow (and get rid of old DB2 warning)
          internal new void ReadWrite(IReadWriteStream s)
  		{
  			if(!s.IsWriting) BeforePropsChange();
  			
  			base.ReadWrite(s);
  
  			s.rwInt(ref offsetx);
  			s.rwInt(ref offsety);
  			s.rwString(ref texnamehigh);
  			s.rwString(ref texnamemid);
  			s.rwString(ref texnamelow);
  			s.rwLong(ref longtexnamehigh);
  			s.rwLong(ref longtexnamemid);
  			s.rwLong(ref longtexnamelow);
  		}
  		
  		// This copies all properties to another sidedef
  		public void CopyPropertiesTo(Sidedef s)
  		{
  			s.BeforePropsChange();
  			
  			// Copy properties
  			s.offsetx = offsetx;
  			s.offsety = offsety;
  			s.texnamehigh = texnamehigh;
  			s.texnamemid = texnamemid;
  			s.texnamelow = texnamelow;
  			s.longtexnamehigh = longtexnamehigh;
  			s.longtexnamemid = longtexnamemid;
  			s.longtexnamelow = longtexnamelow;
  			base.CopyPropertiesTo(s);
  		}
  
  		// This changes sector
  		public void SetSector(Sector newsector)
  		{
  			if(map == General.Map.Map)
  				General.Map.UndoRedo.RecRefSidedefSector(this);
  			
  			// Change sector
  			SetSectorP(newsector);
  		}
  
  		internal void SetSectorP(Sector newsector)
  		{
  			// Detach from sector
  			if(sector != null) sector.DetachSidedefP(sectorlistitem);
  
  			// Change sector
  			sector = newsector;
  
  			// Attach to sector
  			if(sector != null)
  				sectorlistitem = sector.AttachSidedefP(this);
  
  			General.Map.IsChanged = true;
  		}
  
  		// This sets the linedef
  		public void SetLinedef(Linedef ld, bool front)
  		{
  			if(linedef != null) linedef.DetachSidedefP(this);
  			
  			if(ld != null)
  			{
  				if(front)
  					ld.AttachFront(this);
  				else
  					ld.AttachBack(this);
  			}
  		}
  
  		// This sets the linedef (passive: this doesn't tell the linedef and doesn't record)
  		internal void SetLinedefP(Linedef ld)
  		{
  			linedef = ld;
  		}
  		
  		#endregion
  
  		#region ================== Methods
  		
  		// This removes textures that are not required
  		public void RemoveUnneededTextures(bool removemiddle)
  		{
  			RemoveUnneededTextures(removemiddle, false);
  		}
  		
  		// This removes textures that are not required
  		public void RemoveUnneededTextures(bool removemiddle, bool force)
  		{
  			BeforePropsChange();
  			
  			// The middle texture can be removed regardless of any sector tag or linedef action
  			if(!MiddleRequired() && removemiddle)
  			{
  				this.texnamemid = "-";
  				this.longtexnamemid = MapSet.EmptyLongName;
  				General.Map.IsChanged = true;
  			}
  
  			// Check if the line or sectors have no action or tags because
  			// if they do, any texture on this side could be needed
  			if(((linedef.Tag <= 0) && (linedef.Action == 0) && (sector.Tag <= 0) &&
  			    ((Other == null) || (Other.sector.Tag <= 0))) ||
  			   force)
  			{
  				if(!HighRequired())
  				{
  					this.texnamehigh = "-";
  					this.longtexnamehigh = MapSet.EmptyLongName;
  					General.Map.IsChanged = true;
  				}
  
  				if(!LowRequired())
  				{
  					this.texnamelow = "-";
  					this.longtexnamelow = MapSet.EmptyLongName;
  					General.Map.IsChanged = true;
  				}
  			}
  		}
  		
  		/// <summary>
  		/// This checks if a texture is required
  		/// </summary>
  		public bool HighRequired()
  		{
  			// Doublesided?
  			if(Other != null)
  			{
  				// Texture is required when ceiling of other side is lower
  				return (Other.sector.CeilHeight < this.sector.CeilHeight);
  			}
  			else
  			{
  				return false;
  			}
  		}
  
  		/// <summary>
  		/// This checks if a texture is required
  		/// </summary>
  		public bool MiddleRequired()
  		{
  			// Texture is required when the line is singlesided
  
gt: 			return (Other == null);