/*******************************************************}
{                                                       }
{               Borland DB Web                          }
{           Data aware Web controls                     }
{  Copyright (c) 2003 Borland Software Corporation      }
{                                                       }
{*******************************************************/

using System;
using System.Data;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Drawing.Design;
using System.Text;


namespace Borland.Data.Web
{

   #region DBWebImage

	/// <summary>
	/// Data aware Image control for use with DBWebDataSource
	/// </summary>

   [DefaultProperty("Text"),
	Designer("Borland.Data.Web.DBWebControlDesigner"),
   ToolboxBitmap(typeof(Borland.Data.Web.DBWebImage),
   "Borland.Data.Web.DBWebImage.bmp"),
	ToolboxData("<{0}:DBWebImage runat=server></{0}:DBWebImage>")]
   public class DBWebImage : System.Web.UI.WebControls.Image, IDBWebColumnLink, IPostBackDataHandler
   {
   	const string TempImgFile = "_BrlndImg";
      const string jpgExt      = ".jpg";
      const string bmpExt      = ".bmp";
      const string pngExt      = ".png";
      const string gifExt      = ".gif";
      private string imageUrl;
      private string physicalImageFile;
      private bool FUseCacheFile;
      private DBWebColumnLink ColumnLink;
      private IDBWebColumnLink IColumnLink;
      private ImageType FImageType;
      private bool bTempFileNameSet;
      private string FImageExtension;

   	public DBWebImage(): base()
      {
         FUseCacheFile = false;
         ColumnLink = new DBWebColumnLink(this);
         IColumnLink = (ColumnLink as IDBWebColumnLink);
         bTempFileNameSet = false;
         FImageExtension = ".jpg";
      }

		protected override void OnInit(EventArgs e)
		{
			base.OnInit(e);
			if( Page != null )
				Page.RegisterRequiresPostBack(this);
      }

      public override void Dispose()
      {
         if( ClassUtils.IsDesignTime(Page) )
	      	DeleteTempFile();
         base.Dispose();
      }


      // figure out what the physical image file name should be,
      // and also what the url should be for that filename.
      protected void SetImageUrlAndFileName(int ImageIndex)
      {
         // get Unique temp file Name for designtime image
         string sPath;
         string fileName;
         if( ClassUtils.IsDesignTime(Page) )
         {
         	sPath = Directory.GetCurrentDirectory() + "\\";
         	fileName = TempImgFile + this.ID;
         }
         else  // at runtime, use Session.SessionID
         {
            Object oParentRow = Page.Session[TableName + DBWebConst.sParentRow];
            string sParentRow = "_";
            if( oParentRow != null )
            {
            	int iParentRow = Convert.ToInt32(oParentRow);
               if( iParentRow >= 0 )
            		sParentRow = Convert.ToString(iParentRow) + "_";
            }
            sPath = Page.Request.PhysicalApplicationPath;
         	fileName = TempImgFile + this.ID + Convert.ToString(Page.Session.SessionID) +
            				sParentRow + Convert.ToString(ImageIndex);;
            object lastFile = Page.Session[this.ID + DBWebConst.sBorlandImageFile];
            if( lastFile != null )
            	DeleteFile(Convert.ToString(lastFile) );
         }
         int iFileIndex = 1;
         physicalImageFile = fileName;
      	while( File.Exists(sPath + physicalImageFile) )
         {
         	{  // if DeleteFile fails, increment the FileIndex
            	if(! DeleteFile(sPath + physicalImageFile) )
               {
		         	physicalImageFile = fileName + Convert.ToString(iFileIndex);
   		         iFileIndex++;
               }
            }
         }
         if( ClassUtils.IsDesignTime(Page) )
         {
         	imageUrl = "file:///" + sPath + physicalImageFile;
            physicalImageFile = sPath + physicalImageFile;
         }
         else
         {
         	imageUrl = "http://" + Page.Request.Url.Host + Page.Request.ApplicationPath + "/" + physicalImageFile;
            physicalImageFile = sPath + physicalImageFile;
            Page.Session[this.ID + DBWebConst.sBorlandImageFile] = physicalImageFile;
         }
         bTempFileNameSet = true;
      }

      #region IDBWebDataLink
      string IDBWebDataLink.TableName
      {
      	get
         {
         	return IColumnLink.TableName;
         }
         set
         {
         	IColumnLink.TableName = value;
         }
      }
      IDBDataSource IDBWebDataLink.DBDataSource
      {
      	get
         {
         	return IColumnLink.DBDataSource;
         }
         set
         {
         	IColumnLink.DBDataSource = value;
         }
      }
      #endregion
      #region IDBWebColumnLink
      string IDBWebColumnLink.ColumnName
      {
      	get
         {
         	return IColumnLink.ColumnName;
         }
         set
         {
            if( !ClassUtils.SameValue(IColumnLink.ColumnName, value ) )
               DeleteTempFile();
         	IColumnLink.ColumnName = value;
         }
      }
      #endregion

      [Editor(typeof(Borland.Data.Web.TableNamePropEditor), typeof(UITypeEditor)),
		LocalizableCategoryAttribute("DBWebControl"),
		DefaultValue(null)]
      public string TableName
      {
      	get
         {
	      	return IColumnLink.TableName;
         }
      	set
         {
            if( !ClassUtils.SameValue(IColumnLink.TableName, value ) )
               DeleteTempFile();
	      	IColumnLink.TableName = value;
         }
      }
		[LocalizableCategoryAttribute("DBWebControl"),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
		DefaultValue(null)]
      public IDBDataSource DBDataSource
      {
      	get
         {
         	return IColumnLink.DBDataSource;
         }
         set
         {
         	IColumnLink.DBDataSource = value;
         }
      }


		[LocalizableCategoryAttribute("DBWebControl"),
      Editor(typeof(Borland.Data.Web.ImageColumnPropEditor), typeof(UITypeEditor)),
		DefaultValue(null)]
      public string ColumnName
      {
      	get
         {
         	return IColumnLink.ColumnName;
         }
         set
         {
         	IColumnLink.ColumnName = value;
         }
      }
		[LocalizableCategoryAttribute("DBWebControl"),
      DefaultValue(false)]
      public bool UseCacheFile
      {
      	get
         {
         	return FUseCacheFile;
         }
         set
         {
         	FUseCacheFile = value;
         }
      }

      #region IPostBackDataHandler
      // RaisePostDataChangedEvent is called prior to DataBind()
      // DataSet and related properties are NOT available here
      // Child controls are not available
		bool IPostBackDataHandler.LoadPostData(string postDataKey,
			NameValueCollection postCollection)
      {
         if( this.Visible )
	      	ColumnLink.LoadPostData(postDataKey, postCollection);
         return false;
      }

	   void IPostBackDataHandler.RaisePostDataChangedEvent()
   	{
	   }
      #endregion

		protected override void OnPreRender(EventArgs args)
		{
			base.OnPreRender(args);
         bool IsPostBack = !ClassUtils.IsDesignTime(Page) && Page.IsPostBack;
         if( !IsPostBack )
         	DeleteTempFiles();
			string s = Page.Request.QueryString[DBWebConst.sBorlandImageID];
         if( s != null && s == this.ID )
         {
            Object o = Page.Session["Img_" + this.ID];
         	Page.Response.BinaryWrite((byte[])o);
            Page.Response.End();
         }
         else
         {
            DataBind();
         }
		}

      // design-time or FUseCacheFile is true;
      protected void CreateImageFile(byte[] byteBlobData)
      {
         bool bFileInUse = false;
      	if( ClassUtils.IsDesignTime(Page) || FUseCacheFile )
         {
	         if( File.Exists(physicalImageFile) )
   	      {
      	   	try
         	   {
            		File.Delete(physicalImageFile);
	            }
   	         catch
      	      {
         	   	// ignore error if file is in use by the IDE
            	   bFileInUse = true;
	            }
   	      }
         }
         if( !bFileInUse )
         {
         	FileStream s = File.Create(physicalImageFile);
            try
            {
            	int bytes = byteBlobData.Length;
               s.Write(byteBlobData, 0, bytes);
            }
            finally
            {
            	s.Close();
            }
         }
      }

      protected ImageType GetImageType( byte[] blobData, out int iStartBlobBytesAt )
      {
      	const int maxBytes = 12;
         iStartBlobBytesAt = 0;
         if( blobData.Length < maxBytes )
         {
         	FImageExtension = jpgExt;
          	return ImageType.itNone;
         }
      	String s = Encoding.ASCII.GetString( blobData, 0, maxBytes);
         string sTemp = s.Substring(0, 3);
         if( sTemp.ToUpper() == "GIF" )
         {
         	FImageExtension = gifExt;
         	return ImageType.itGif;
         }
         sTemp = s.Substring(1, 3);
         if( sTemp.ToUpper() == "PNG" )
         {
         	FImageExtension = pngExt;
         	return ImageType.itGif;
         }
         sTemp = s.Substring(0, 2);
         if( sTemp.ToUpper() == "BM" )
         {
         	FImageExtension = bmpExt;
         	return ImageType.itBmp;
         }
         sTemp = s.Substring(0, 1);
         if( s == "ÿ" )
         {
         	FImageExtension = jpgExt;
	         return ImageType.itJpg;
         }
         // Old Paradox tables left an 8 byte header
         // before the "BM".  This code should be
         // changed to identify the header format
         string s1 = s.Substring(8, 1);
         string s2 = s.Substring(9, 1);
         if( s1 == "B" && s2 == "M" )
         {
         	iStartBlobBytesAt = 8;
            FImageExtension = bmpExt;
            return ImageType.itBmp;
         }
         FImageExtension = jpgExt;
         return ImageType.itJpg;
      }

      protected byte[] GetImage( out int FCurrentRow )
      {
      	FCurrentRow = -1;
         if( IColumnLink.DBDataSource != null && IColumnLink.DBDataSource.GetDataSource(Page) != null )
         {
				if( IColumnLink.DBDataSource.GetRowCount(Page, IColumnLink.TableName) == 0 )
            	return null;
            DataRow row = null;
            FCurrentRow = IColumnLink.DBDataSource.GetCurrentRow(Page, IColumnLink.TableName);
            if( FCurrentRow < 0 )
            	return null;
            Object table = IColumnLink.DBDataSource.GetTableOrView(Page, IColumnLink.TableName);
            if( table is DataView )
            {
               DataView view = (table as DataView);
            	row = view[FCurrentRow].Row;
            }
            else
            {
               row = (table as DataTable).Rows[FCurrentRow];
            }
            if( row != null )
            {
            	if( ! row.IsNull(IColumnLink.ColumnName) )
               {
               	Byte[] byteBlobData = new Byte[0];
                  try
                  {
                  	byteBlobData = (Byte[])row[IColumnLink.ColumnName];
                     return byteBlobData;
                  }
                  catch
                  {
                     return null;
                  }
               }
            }
         }
         return null;
      }

  		protected override void Render(HtmlTextWriter output)
		{
      	bool error = ClassUtils.OutputErrors(Page, output, IColumnLink);
         if( !error || (IColumnLink.DBDataSource as DBWebDataSource).ErrorOption != ErrorHtmlOption.logOnErrorPage )
         	base.Render(output);
         else  // if going to a separate error hmtl page, output "OK" button.
         {
         	ClassUtils.OutputOKButton(output);
            Page.Response.End();
         }
		}

      private byte [] StartAt(byte [] bytes, int iStart)
      {
      	byte [] retBytes = new byte[bytes.Length - iStart];
         for( int i = 0; i < (bytes.Length - iStart); i ++ )
         	retBytes[i] = bytes[i+iStart];
         if( retBytes.Length < (bytes.Length - iStart) )
         	return bytes;
         else
         	return retBytes;
      }


		public override void DataBind()
		{
      	try
         {
	         if( !ClassUtils.IsDesignTime(Page) )
   	      	Page.Session.Remove("Img_" + this.ID);
      	   else if( ! ColumnLink.IsDataBound )
         		ImageUrl = "";
	         base.DataBind();
   	      if( ColumnLink.IsDataBound )
      	   {
               int CurrentRow;
         		Byte[] byteBlobData = GetImage(out CurrentRow);
	         	if( byteBlobData != null )
	   	      {
               	int iStartBlobBytesAt;
                  GetImageType(byteBlobData, out iStartBlobBytesAt);
                  if( iStartBlobBytesAt > 0 )
                  	byteBlobData = StartAt(byteBlobData, iStartBlobBytesAt);
                  if( ClassUtils.IsDesignTime(Page) || FUseCacheFile )
                  {
                  	int ImageIndex = 0;
                     if( !ClassUtils.IsDesignTime(Page) )
                     {
	                     object o = Page.Session[TableName + DBWebConst.sImageIndex];
   	                  if( o != null )
      	               	ImageIndex = Convert.ToInt32(o) + 1;
                     	Page.Session[TableName + DBWebConst.sImageIndex] = ImageIndex;
                     }
                     SetImageUrlAndFileName(ImageIndex);
                     CreateImageFile(byteBlobData);
				         if( ClassUtils.IsDesignTime(Page) )
                     {
                        ImageUrl = imageUrl;
	                  }
                     else
                        ImageUrl = imageUrl;
                  }
      	      	else
	      	      {
   	      			Page.Session["Img_" + this.ID] = byteBlobData;
      	      		ImageUrl = "http://" + Page.Request.Url.Host +
         	      			Page.Request.Url.AbsolutePath + "?" + DBWebConst.sBorlandImageID + "=" + this.ID;  // { do not localize}
	            	}
   	   			base.DataBind();
               }
            }
         	ChildControlsCreated = true;
         }
         catch(Exception ex)
         {
         	if( !ClassUtils.IsDesignTime(Page) )
            {
	            Page.Response.Write(ClassUtils.GetInternalError(Page, IColumnLink, ex, this.ID));
            }
            else
            	throw new Exception(ex.Message);
         }
		}


      private bool DeleteFile(string fileName)

      {

      	try

         {

         	if( File.Exists(fileName) )

            	File.Delete(fileName);

            return true;

         }

         catch

         {

         	// ignore file in use exceptions

         }

         return false;

      }


      private void DeleteTempFile()

      {

      	if( ClassUtils.IsDesignTime(Page) )

         	DeleteFile(physicalImageFile);

      }


      private void DeleteTempFiles()

      {

      	DirectoryInfo de = new DirectoryInfo(Page.Request.PhysicalApplicationPath);

         FileInfo[] files = de.GetFiles(TempImgFile + this.ID + "*");

         foreach( FileInfo file in files )

	      	DeleteFile(file.FullName);

   	}



   }
   #endregion
}

