using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.IO;
using RTE;

public class SqlFileProvider : RTE.EditorFileProvider
{
	static public string ConnectionString
	{
		get
		{
			return "server=(local);database=rtedb;trusted_connection=false;uid=test;pwd=test;";
		}
	}

	HttpContext _context;

	Hashtable _zonetoweb = new Hashtable();
	Hashtable _zonetophy = new Hashtable();

	SqlConnection _conn;

	private SqlConnection GetConnection()
	{
		if (_conn != null)
			return _conn;
		SqlConnection conn = new SqlConnection(ConnectionString);
		conn.Open();
		_conn = conn;
		return _conn;
	}

	public override void Dispose()
	{
		if (_conn != null)
		{
			SqlConnection conn = _conn;
			_conn = null;
			conn.Dispose();
		}

		base.Dispose();
	}


	static public void SetParameter(SqlParameterCollection pcoll, string pname, object obj)
	{
		if (pcoll == null) throw (new ArgumentNullException("pcoll"));
		if (obj == null || Convert.IsDBNull(obj))
		{
			pcoll.Add(pname, SqlDbType.NVarChar, 50).Value = DBNull.Value;
			return;
		}
		if (obj is string)
		{
			if (obj.ToString().Length > 4000)
			{
				pcoll.Add(pname, SqlDbType.NText).Value = obj;
				return;
			}
		}
		else if (obj is byte[])
		{
			byte[] bs = (byte[])obj;
			if (bs.Length > 8000)
			{
				pcoll.Add(pname, SqlDbType.Binary).Value = bs;
				return;
			}
		}
		else if (obj is SqlParameter)
		{
			SqlParameter sp = (SqlParameter)obj;
			//pcoll.Add(sp);
			pcoll.Add(new SqlParameter(pname, sp.SqlDbType, sp.Size, sp.Direction, true, sp.Precision, sp.Scale, sp.SourceColumn, sp.SourceVersion, sp.Value));
			return;
		}
		else if (obj is DateTime)
		{
			pcoll.Add(pname, SqlDbType.NVarChar, 50).Value = ((DateTime)obj).ToString("yyyy-MM-dd HH:mm:ss") + "." + ((DateTime)obj).Millisecond.ToString().PadLeft(3, '0');
			return;
		}
		pcoll.Add(pname, obj);
	}

	SqlDataReader ExecuteReader(string commandtext, params object[] args)
	{
		int len = args == null ? 0 : args.Length;

		SqlCommand cmd = new SqlCommand(commandtext, GetConnection());

		object[] argnames = new object[len];

		SqlParameterCollection ps = cmd.Parameters;
		for (int i = 0; i < len; i++)
		{
			object val = args[i];
			string pname = "@p" + i;
			SetParameter(ps, pname, val);

			if (val is DateTime)
			{
				argnames[i] = "CONVERT(DATETIME," + pname + ")";
			}
			else
			{
				argnames[i] = pname;
			}
		}


		commandtext = string.Format(commandtext, argnames);
		cmd.CommandText = commandtext;

		return cmd.ExecuteReader();

	}

	public void ExecuteNonQuery(string commandtext, params object[] args)
	{
		ExecuteReader(commandtext, args).Close();
	}



	public override void Init(HttpContext context,ConfigSecurity security)
	{
		_context = context;
		string zoneid = security.StorageId;

		string webpath = security.StoragePath;

		if (webpath.StartsWith("~/"))
			webpath = webpath.Remove(0, 1);
		else if (!webpath.StartsWith("/")) 
			throw (new Exception("Invalid StoragePath"));

		string handler = security.FileProviderArg0;
		if (string.IsNullOrEmpty(handler))
			handler = "/download.ashx?file=";
		if (handler.StartsWith("~/"))
			handler = System.Web.HttpRuntime.AppDomainAppVirtualPath.TrimEnd('/') + handler.Remove(0, 1);

		_zonetoweb[zoneid] = handler + webpath;
		_zonetophy[zoneid] = webpath.TrimEnd('/')+'/';
	}

	private string GetZoneWebPath(string zoneid)
	{
		string webpath = (string)_zonetoweb[zoneid];
		if (webpath == null)
			throw (new Exception("Invalid zone"));
		return webpath;
	}
	private string GetZonePhyPath(string zoneid)
	{
		string phypath = (string)_zonetophy[zoneid];
		if (phypath == null)
			throw (new Exception("Invalid zone"));
		return phypath;
	}

	public override string GetUrlPrefix(Storage zone)
	{
		return GetZoneWebPath(zone.ID).TrimEnd('/', '\\');
	}
	public override long CalcStorageSize(Storage zone)
	{
		string path = GetZonePhyPath(zone.ID);
		return CalcDirectorySize(path);
	}

	long CalcDirectorySize(string path)
	{
		using (SqlDataReader reader = ExecuteReader("SELECT SUM([filesize]) FROM [fsitems] WHERE [path] LIKE @p0", path.TrimEnd('/','\\') + "/%"))
		{
			if (!reader.Read())
				return 0;
			return reader.IsDBNull(0)?0:Convert.ToInt64(reader[0]);
		}
	}

	public override FolderID GetRootId(Storage zone)
	{
		return new FolderID(zone.ID, GetZonePhyPath(zone.ID), zone.Category, zone.Extensions);
	}

	public override string GetUrlPath(FolderID folderid)
	{
		string phypath = GetZonePhyPath(folderid.ZoneID);
		return folderid.Value.Remove(0, phypath.Length - 1).Replace('\\', '/');
	}

	public override FolderID GetParentId(FolderID folderid)
	{
		string phypath = GetZonePhyPath(folderid.ZoneID);

		string path = folderid.Value.TrimEnd('/', '\\');
		int pos = path.LastIndexOfAny(new char[] { '/', '\\' });
		path = path.Substring(0, pos + 1);
		if (path.IndexOf(phypath) == -1)
			return null;
		return new FolderID(folderid.ZoneID, path, folderid.Category, folderid.Extensions);
	}
	public override FolderID HasFolder(FolderID folderid, string foldername)
	{
		string path = Path.Combine(folderid.Value, foldername) + '/';

		using (SqlDataReader reader = ExecuteReader("SELECT [id],[path],[isdir],[parentpath],[createdt],[filename],[filesize] FROM [fsitems] WHERE [path]=@p0 AND [isdir]=1", path))
		{
			if (!reader.Read())
				return null;
			return new FolderID(folderid.ZoneID, path, folderid.Category, folderid.Extensions);
		}
	}
	public override PathItem[] GetFolders(FolderID folderid, GetItemOption option)
	{
		ArrayList list = new ArrayList();
		using (SqlDataReader reader = ExecuteReader("SELECT [id],[path],[isdir],[parentpath],[createdt],[filename],[filesize] FROM [fsitems] WHERE [parentpath]=@p0 AND [isdir]=1", folderid.Value))
		{
			while (reader.Read())
			{
				string path=reader["path"].ToString();
				string[] parts=path.Split('/');
				string name = parts[parts.Length - 1];
				if (name.Length == 0) name = parts[parts.Length - 2];

				PathItem item = new PathItem();
				item.IsFolder = true;
				item.Time = GetItemOption.CalcTime(Convert.ToDateTime(reader["createdt"]));
				item.Name = name;
				if (this.IsHiddenItem(item))
					continue;
				list.Add(item);
			}
		}
		if (option.GetSize)
		{
			foreach (PathItem item in list)
			{
				using (SqlDataReader reader = ExecuteReader("SELECT COUNT(*) FROM [fsitems] WHERE [parentpath]=@p0", folderid.Value+item.Name+"/"))
				{
					if (reader.Read())
						item.Size = reader.IsDBNull(0)?0:Convert.ToInt32(reader[0]);
				}
			}
		}
		return (PathItem[])list.ToArray(typeof(PathItem));
	}
	public override PathItem[] GetFiles(FolderID folderid, GetItemOption option)
	{
		ArrayList list = new ArrayList();
		using (SqlDataReader reader = ExecuteReader("SELECT [id],[path],[isdir],[parentpath],[createdt],[filename],[filesize] FROM [fsitems] WHERE [parentpath]=@p0 AND [isdir]=0", folderid.Value))
		{
			while (reader.Read())
			{
				PathItem item = new PathItem();
				item.IsFolder = false;
				item.Time = GetItemOption.CalcTime(Convert.ToDateTime(reader["createdt"]));
				item.Name = reader["filename"].ToString();
				item.Size = Convert.ToInt64(reader["filesize"]);
				if (this.IsHiddenItem(item))
					continue;
				list.Add(item);
			}
		}
		return (PathItem[])list.ToArray(typeof(PathItem));
	}

	public override PathItem GetItem(FolderID folderid, GetItemOption option, string name)
	{
		if (name == "..")
			return null;

		PathItem item=null;
		using (SqlDataReader reader = ExecuteReader("SELECT [id],[path],[isdir],[parentpath],[createdt],[filename],[filesize] FROM [fsitems] WHERE [path]=@p0 AND [isdir]=1", folderid.Value+name+"/"))
		{
			if (reader.Read())
			{
				item = new PathItem();
				item.IsFolder = true;
				item.Time = GetItemOption.CalcTime(Convert.ToDateTime(reader["createdt"]));
				item.Name = name;
			}
		}

		if (item != null)
		{
			if (option.GetSize)
			{
				using (SqlDataReader reader = ExecuteReader("SELECT COUNT(*) FROM [fsitems] WHERE WHERE [parentpath]=@p0", folderid.Value + item.Name + "/"))
				{
					if (reader.Read())
						item.Size = reader.IsDBNull(0) ? 0 : Convert.ToInt32(reader[0]);
				}
			}
			return item;
		}

		using (SqlDataReader reader = ExecuteReader("SELECT [id],[path],[isdir],[parentpath],[createdt],[filename],[filesize] FROM [fsitems] WHERE [path]=@p0 AND [isdir]=0", folderid.Value + name))
		{
			if (reader.Read())
			{
				item = new PathItem();
				item.IsFolder = false;
				item.Time = GetItemOption.CalcTime(Convert.ToDateTime(reader["createdt"]));
				item.Name = reader["filename"].ToString();
				item.Size = Convert.ToInt64(reader["filesize"]);
				return item;
			}
		}

		return null;
	}

	public override byte[] LoadFile(FolderID folderid, string name)
	{
		using (SqlDataReader reader = ExecuteReader("SELECT [filedata] FROM [fsitems] WHERE [path]=@p0 AND [isdir]=0", folderid.Value + name))
		{
			if (reader.Read())
			{
				return (byte[])reader["filedata"];
			}
		}

		throw (new Exception(folderid.Value+name + " does not exist"));
	}

	public override void CreateFile(FolderID folderid, string filename, Stream stream)
	{
		string fullname = folderid.Value + filename;

		int pos = 0;
		int len = (int)stream.Length;
		byte[] buff = new byte[len];
		while (len > 0)
		{
			int rc = stream.Read(buff, pos, len);
			if (rc <= 0)
				throw (new Exception("faild to read stream"));
			pos += rc;
			len -= rc;
		}

		PathItem item=GetItem(folderid, GetItemOption.None, filename);
		if (item != null)
		{
			if (item.IsFolder)
				throw (new Exception(filename + " is a folder"));

			ExecuteNonQuery("UPDATE [fsitems] SET [filesize]=@p1,[filedata]=@p2 WHERE [path]=@p0",fullname, buff.Length, buff);
			return;
		}

		ExecuteNonQuery("INSERT INTO [fsitems] ([path],[isdir],[parentpath],[createdt],[filename],[filesize],[filedata]) VALUES (@p0,0,@p1,GetDate(),@p2,@p3,@p4)"
			, fullname, folderid.Value, filename, buff.Length, buff);
	}

	public override void CreateFolder(FolderID folderid, string filename)
	{
		PathItem item=GetItem(folderid, GetItemOption.None, filename);
		if (item != null)
		{
			if(!item.IsFolder)
				throw (new Exception(filename + " is a file"));
			return;
		}
		string fullname = folderid.Value + filename + "/";

		ExecuteNonQuery("INSERT INTO [fsitems] ([path],[isdir],[parentpath],[createdt]) VALUES (@p0,1,@p1,GetDate())"
			, fullname, folderid.Value);
	}
	public override void DeleteFile(FolderID folderid, string filename)
	{
		string fullname = folderid.Value + filename;
		ExecuteNonQuery("DELETE [fsitems] WHERE path=@p0", fullname);
	}
	public override void DeleteFolder(FolderID folderid, string filename)
	{
		string fullname = folderid.Value + filename+"/";
		ExecuteNonQuery("DELETE [fsitems] WHERE [path]=@p0 OR [parentpath] LIKE @p1", fullname,fullname+"%");
	}
	public override void ChangeFileName(FolderID folderid, string filename, string newname)
	{
		PathItem item = GetItem(folderid, GetItemOption.None, newname);
		if (item != null)
			throw (new Exception(filename + " exists"));

		string fullname1 = folderid.Value + filename;
		string fullname2 = folderid.Value + newname;
		ExecuteNonQuery("UPDATE [fsitems] SET [path]=@p0,[filename]=@p1 WHERE [path]=@p2"
		, fullname2, newname, fullname1);
	}
	public override void ChangeFolderName(FolderID folderid, string filename, string newname)
	{
		PathItem item = GetItem(folderid, GetItemOption.None, newname);
		if (item != null)
			throw (new Exception(filename + " exists"));

		string fullname1 = folderid.Value + filename+"/";
		string fullname2 = folderid.Value + newname+"/";

		ExecuteNonQuery("UPDATE [fsitems] SET [path]=@p2+SUBSTRING([path],@p1+1,LEN([path])-@p1) WHERE [path] LIKE @p0 ; UPDATE [fsitems] SET [parentpath]=@p2+SUBSTRING([parentpath],@p1+1,LEN([parentpath])-@p1) WHERE [parentpath] LIKE @p0"
		, fullname1 + "%", fullname1.Length, fullname2);
	}
	public override void MoveFile(FolderID folderid, FolderID targetid, string filename)
	{
		string fullname1 = folderid.Value + filename;
		string fullname2 = targetid.Value + filename;
		ExecuteNonQuery("UPDATE [fsitems] SET [path]=@p0,[parentpath]=@p1 WHERE [path]=@p2"
		, fullname2, targetid.Value, fullname1);
	}
	public override void MoveFolder(FolderID folderid, FolderID targetid, string filename)
	{
		string fullname1 = folderid.Value + filename;
		string fullname2 = targetid.Value + filename;
		throw(new NotImplementedException());
	}

	public override void CopyFile(FolderID folderid, FolderID targetid, string filename)
	{
		string fullname1 = folderid.Value + filename;
		string fullname2 = targetid.Value + filename;
		throw(new NotImplementedException());
	}
	public override void CopyFolder(FolderID folderid, FolderID targetid, string filename)
	{
		string fullname1 = folderid.Value + filename;
		string fullname2 = targetid.Value + filename;
		throw(new NotImplementedException());
	}


}

