using Microsoft;
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Caching;
using System.ComponentModel;
using System.Xml;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using System.Web.UI.Design;
using System.IO;
using System.Configuration;

namespace Microsoft {

	// Enum used during design mode to switch
	// views of the control
	//
	public enum DesignMode {
		Question,
		Results
	}

	/// <summary>
	/// The Vote control is allows for users to vote on topics. The results are stored in 
	/// a SQL Server table and a HTML graph is used to display the selections/percentages.
	/// 
	/// Author: rhoward@microsoft.com
	/// Date: 11/2/2001
	/// </summary>
	[
		Designer(typeof(VoteDesigner)),
		ToolboxData("<{0}:Vote runat=\"server\" />")
	]
	public class Vote : WebControl, INamingContainer {

		// Internal members
		//
		RadioButtonList radioButtonList;
		PollDetails polldetails;
		SortedList pollresults;
		string pathToXml = "I:\\Inetpub\\wwwroot\\poll\\poll.xml";
		bool isDesignTime = true;
		HttpCookie cookie;
		DesignMode designMode = DesignMode.Question;
		System.Drawing.Color barBackColor = System.Drawing.Color.LightGray;
		System.Drawing.Color barForeColor = System.Drawing.Color.Blue;
		Style questionStyle;
		Style itemStyle;
		Style buttonStyle;
		bool allowMultipleVotes = false;
		string buttonText = "Vote";
		string dbConnectionString;

		/// <summary>
		/// Constructor
		/// </summary>
		public Vote() {

			// If Context is available we're not in design time.
			// It is important to determine if we are in design time
			// because this controls whether or not cookies are read/written
			// and whether or not the ASP.NET Cache is utilized for perf.
			//
			if (Context != null)
				isDesignTime = false;

		}

		protected override void OnInit(EventArgs e) {

			InitializeControl();

		}

		private void InitializeControl() {

			// Get Poll Details reads the XML file (or cache)
			// and fills an instance class with details
			//
			polldetails = GetPollDetails();

			// Connects to the database (or cache)
			// and gets the current results for the poll
			//
			pollresults = GetPollResults();

			// Gets the Cookie used to control voting
			//
			cookie = GetCookie();

		}

		/// <summary>
		/// Used to indicate whether multiple votes are allowed per user. Default is false.
		/// </summary>
		[
	        Category("Behavior"),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.Attribute)
		]
		public bool AllowMultipleVote {
			get {
				return allowMultipleVotes;
			}
			set {
				allowMultipleVotes = value;
			}
		}

		/// <summary>
		/// Used to indicate whether multiple votes are allowed per user. Default is false.
		/// </summary>
		[
		Category("Data"),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		PersistenceMode(PersistenceMode.Attribute)
		]
		public string ConnectionString
		{
			get 
			{
				return dbConnectionString;
			}
			set 
			{
				dbConnectionString = value;
			}
		}

		/// <summary>
		/// Controls the height of the control
		/// </summary>
		[
	        Category("Layout"),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.Attribute)
		]
		public override Unit Height {
			get {
				return base.Height;
			}
			set {
				base.Height = value;
			}
		}

		/// <summary>
		/// Controls the width of the control
		/// </summary>
		[
	        Category("Layout"),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.Attribute)
		]
		public override Unit Width {
			get {
				return base.Width;
			}
			set {
				base.Width = value;
			}
		}

		/// <summary>
		/// Used to control the text on the button. Default is 'Vote'
		/// </summary>
		[
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.Attribute)
		]
		public String ButtonText {
			get {
				return buttonText;
			}
			set {
				buttonText = value;
			}
		}

		/// <summary>
		/// Sets the style for the question
		/// </summary>
		[
	        Category("Style"),
			DefaultValue(null),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.InnerProperty)
		]
		public Style QuestionStyle {
			get {
				if (questionStyle == null)
					questionStyle = new Style();

				return questionStyle;
			}
		}

		/// <summary>
		/// Sets the general style for displayed items
		/// </summary>
		[
	        Category("Style"),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.InnerProperty),
			DefaultValue(null),
		]
		public Style ItemStyle {
			get {
				if (itemStyle == null)
					itemStyle = new Style();
					
				return itemStyle;
			}
		}

		/// <summary>
		/// Sets the style for the button used in voting
		/// </summary>
		[
	        Category("Style"),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.InnerProperty),
			DefaultValue(null),
		]
		public Style ButtonStyle {
			get {
				if (buttonStyle == null)
					buttonStyle = new Style();
					
				return buttonStyle;
			}
		}

		/// <summary>
		/// Used control display in design time. 
		/// </summary>
		[
	        Category("Designer"),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
		]
		public DesignMode DesignMode {
			get {
				return designMode;
			}
			set {
				designMode = value;
			}
		}

		/// <summary>
		/// Path to the XML file containing the poll details.
		/// </summary>
		[
	        Category("Data"),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
			PersistenceMode(PersistenceMode.InnerProperty)
		]
		public string Path {
			get {
				return pathToXml;
			}
			set {
				if (!isDesignTime)
					pathToXml = Context.Server.MapPath(value);
			}
		}

		/// <summary>
		/// Override control collection and return base collection
		/// </summary>
		public override ControlCollection Controls {
			get {
				EnsureChildControls();
				return base.Controls;
			}
		}

		/// <summary>
		/// Controls the back ground color of the rendered bar(s).
		/// </summary>
		[
	        Category("Style"),
			PersistenceMode(PersistenceMode.Attribute)
		]
		public System.Drawing.Color BarBackColor {
			get {
				return barBackColor;
			}
			set {
				barBackColor = value;
			}
		}

		/// <summary>
		/// Controls the foreground color of the rendered bar(s).
		/// </summary>
		[
	        Category("Style"),
			PersistenceMode(PersistenceMode.Attribute)
		]
		public System.Drawing.Color BarForeColor {
			get {
				return barForeColor;
			}
			set {
				barForeColor = value;
			}
		}

		public void RenderAtDesignTime() {

			// In design time?
			//
			if (!isDesignTime)
				return;

			// If we are in design time, go ahead and initialize the control
			//
			if (isDesignTime)
				InitializeControl();

			// Clear the controls collection
			//
			this.Controls.Clear();

			// Render the appropriate control
			//
			if (DesignMode.Question == designMode)
				this.HasNotVoted();
			else
				this.HasVoted(null);

		}
		

		private HttpCookie GetCookie() {

			// If we're in design time just return null
			//
			if (isDesignTime)
				return null;

			// Attempt to retrieve the cookie
			//
			return Context.Request.Cookies["ASPNETPoll_" + polldetails.Name];

		}

		private PollDetails GetPollDetails() {

			PollDetails p;

			// If cache is available, see if we have it 
			// in the cache
			//
			if (!isDesignTime)
				if (Context.Cache[pathToXml] != null)
					return (PollDetails) Context.Cache[pathToXml];

			// Not in the Cache, let's create it

			// Attempt to load the required XML Document
			//
			XmlDocument dom = new XmlDocument();
			dom.Load(pathToXml);
			
			// Create the poll details
			//
			p = new PollDetails(dom);

			// Can we cache it?
			//
			if (!isDesignTime) {
				// Create a Cache File Dependency on the XML file
				//
				CacheDependency fileDependency = new CacheDependency(pathToXml);

				// Insert the Cache entry
				//
				Context.Cache.Insert(pathToXml, p, fileDependency);
			}

			// Return the internal PollDetails instance
			//
			return p;

		}
			
		private SortedList GetPollResults() {

			SortedList s = new SortedList();
			SqlDataReader reader;
			SqlParameter param;
			SqlConnection connection = null;
			SqlCommand command;

			// If cache is available, see if we have it 
			// in the cache
			//
			if (!isDesignTime)
				if (Context.Cache[polldetails.Name] != null)
					return (SortedList) Context.Cache[polldetails.Name];

			// Connect to SQL and get values
			//
			if (null != dbConnectionString)
				connection = new SqlConnection(dbConnectionString);
			else if (!isDesignTime)
				connection = new SqlConnection(ConfigurationSettings.AppSettings[polldetails.ConnectionAppSettingKey]);	

			// Use a stored procedure to retrieve the value
			//
			command = new SqlCommand("GetVoteCount", connection);
			command.CommandType = CommandType.StoredProcedure;
	
			// Set the parameter to name the poll that we want values for
			//
			param = command.Parameters.Add(new SqlParameter("@PollName", SqlDbType.NVarChar, 128));
			param.Value = polldetails.Name;

			// Open the connection and execute reader
			//
			connection.Open();
			reader = command.ExecuteReader(CommandBehavior.CloseConnection);

			// Read the values
			//
			while (reader.Read())
				s.Add((string) reader["Vote"], (int) reader["VoteCount"]);
			
			// Is the Cache available?
			//
			if (!isDesignTime)
	            Context.Cache.Insert(polldetails.Name, s);

			return s;

		}

		protected override void CreateChildControls() {

			if (isDesignTime)
				return;

			if (cookie == null)
				HasNotVoted();
			else if (allowMultipleVotes)
				HasNotVoted();
			else
				HasVoted(cookie.Value);

		}

		protected void HasNotVoted() {

			// Member variables
			//
			Table t = new Table();
			TableRow questionRow = new TableRow();
			TableRow choicesRow = new TableRow();
			TableRow buttonRow = new TableRow();
			TableCell questionCell = new TableCell();
			TableCell choicesCell = new TableCell();
			TableCell buttonCell = new TableCell();
			Button button = new Button();
			radioButtonList = new RadioButtonList();

			// Wire up the button click event
			//
			button.Click += new EventHandler(VoteButtonClick);
			
			// Format Question
			//
			questionCell.Text = polldetails.Question;
			questionRow.ControlStyle.CopyFrom(questionStyle);

			// Insert question cell and row into table
			//
			questionRow.Cells.Add(questionCell);
			t.Rows.Add(questionRow);

			// Format Radio Button
			//
			radioButtonList.ControlStyle.CopyFrom(itemStyle);

			// Populate the radioButtonList
			//
			foreach (string key in polldetails.Choices.Keys)
				radioButtonList.Items.Add(new ListItem((string)polldetails.Choices[key], key));

			// Insert the radio button into the cell
			//
			choicesCell.Controls.Add(radioButtonList);

			// Add row/cell to table
			//
			choicesRow.Cells.Add(choicesCell);
			t.Rows.Add(choicesRow);

			// Format Button
			//
			button.Text = buttonText;
			button.ControlStyle.CopyFrom(buttonStyle);

			// Insert button into cell
			//
			buttonCell.Controls.Add(button);

			// Add button cell/row to table
			//
			buttonRow.Cells.Add(buttonCell);
			t.Rows.Add(buttonRow);

			// Add table to the controls collection
			//
			this.Controls.Add(t);

		}

		protected void HasVoted(string votedFor) {

			// Member Variables
			//
			int sum = 0;
			Table t = new Table();
			TableRow questionRow = new TableRow();
			TableRow resultsRow = new TableRow();
			TableRow footerRow = new TableRow();
			TableCell questionCell = new TableCell();
			TableCell resultsCell = new TableCell();
			TableCell footer = new TableCell();

			// Format the table
			//
			t.BorderWidth = 0;

			// Question
			//
			questionCell.Text = polldetails.Question;
			questionCell.ColumnSpan = 3;
			questionCell.ControlStyle.CopyFrom(questionStyle);

			// Add the question cell/row to the table
			//
			questionRow.Cells.Add(questionCell);
			t.Rows.Add(questionRow);

			// Calculate the sum
			//
			foreach (string key in pollresults.Keys)
				sum = sum + (int)pollresults[key];

			// Calculate percentage
			//
			foreach (string key in polldetails.Choices.Keys) {

				// Internal variables/instances
				//
				double d = 0;
				int pollValue = 0;
				TableRow row = new TableRow();
				TableCell progressCell = new TableCell();
				TableCell percentageCell = new TableCell();
				TableCell choiceCell = new TableCell();
				ProgressBar bar = new ProgressBar();
				
				// Get the poll value
				//
				if (null != pollresults[key])
					pollValue = (int) pollresults[key];

				// Percentage for this poll value
				//
				d = ((double)pollValue / (double)sum) * 100;

				// Display the choice in cell and format
				//
				choiceCell.Text = (string)polldetails.Choices[key] + ": ";
				choiceCell.VerticalAlign = VerticalAlign.Top;
				choiceCell.ControlStyle.CopyFrom(itemStyle);
				choiceCell.Wrap = false;

				// Set properties for each bar
				//
				bar.PercentageOfProgress = (int)d;
				bar.BackColor = this.BarBackColor;
				bar.ForeColor = this.BarForeColor;

				// Add the bar and set properties of the cell
				//
				progressCell.Controls.Add(bar);
				progressCell.HorizontalAlign = HorizontalAlign.Right;
				progressCell.VerticalAlign = VerticalAlign.Top;
				progressCell.Width = 100;

				// Special case 0
				//
				if ((double.IsNaN(d)) || (0 == d))
					percentageCell.Text = "(0%)";
				else
					percentageCell.Text = "(" + d.ToString("##.#") + "%)";

				// Format percentage cell
				//
				percentageCell.HorizontalAlign = HorizontalAlign.Left;
				percentageCell.VerticalAlign = VerticalAlign.Top;
				percentageCell.ControlStyle.CopyFrom(itemStyle);

				// Add the cells to the row
				//
				row.Cells.Add(choiceCell);
				row.Cells.Add(progressCell);
				row.Cells.Add(percentageCell);

				// Add the row to the table
				//
				t.Rows.Add(row);

			}

			// What you voted for
			//
			resultsCell.ColumnSpan = 3;
			resultsCell.HorizontalAlign = HorizontalAlign.Right;
			resultsCell.ControlStyle.CopyFrom(itemStyle);

			// Special case design time
			//
			if (isDesignTime)
				resultsCell.Text = "Your vote:";
			else
				resultsCell.Text = "Your vote: <b>" + polldetails.Choices[votedFor] + "</b>";

			// Add results cell/row to table
			//
			resultsRow.Cells.Add(resultsCell);
			t.Rows.Add(resultsRow);
			
			// Set footer properties
			//
			footer.Text = "Total votes: <b>" + sum + "</b>";
			footer.ControlStyle.CopyFrom(itemStyle);
			footer.ColumnSpan = 3;
			footer.HorizontalAlign = HorizontalAlign.Right;

			// Add footer cell and add to table
			//
			footerRow.Cells.Add(footer);
			t.Rows.Add(footerRow);

			this.Controls.Add(t);
		}

		void VoteButtonClick(Object sender, EventArgs e) {
			
			// Member variables
			//
			SqlConnection connection;
			SqlCommand command;
			SqlParameter param;

			// We don't need to check if an item is selected, as the 
			// first item is always pre-selected

			// Create connection and command
			//
			connection = new SqlConnection(@"data source=COOLBEE\COOLBEE;initial catalog=Poll;persist security info=False;user id=sa;workstation id=COOLBEE;packet size=4096");
			command = new SqlCommand("IncrementPoll", connection);
			command.CommandType = CommandType.StoredProcedure;

			// Set the parameter to name the poll name
			//
			param = command.Parameters.Add(new SqlParameter("@PollName", SqlDbType.NVarChar, 128));
			param.Value = polldetails.Name;

			// Set the parameter to the voted item
			//
			param = command.Parameters.Add(new SqlParameter("@Vote", SqlDbType.Char));
			param.Value = radioButtonList.SelectedItem.Value;

			// Open and execute
			//
			connection.Open();
			command.ExecuteNonQuery();
			connection.Close();

			// Increment our internal poll results
			//
			if (null == pollresults[radioButtonList.SelectedItem.Value])
				pollresults[radioButtonList.SelectedItem.Value] = 1;
			else
				pollresults[radioButtonList.SelectedItem.Value] = (int)pollresults[radioButtonList.SelectedItem.Value] + 1;

			// Write the Cookie to indicate that this user
			// has voted for this Poll
			//
            String cookieName = "ASPNETPoll_" + polldetails.Name;
			Context.Response.Clear();
            Context.Response.Cookies[cookieName].Expires = DateTime.Now.AddMonths(2);
			Context.Response.Cookies[cookieName].Value = radioButtonList.SelectedItem.Value;

			// Clear the controls collection
			//
			this.Controls.Clear();

			// 
			HasVoted(radioButtonList.SelectedItem.Value);
		}
	}
}
