// Copyright (C) 2003 Daisuke Arai <darai@users.sourceforge.jp>
// Copyright (C) 2004 M. Ishiguro <mishiguro@users.sourceforge.jp>
// Copyright (C) 2004, 2007 panacoran <panacoran@users.sourceforge.jp>
// 
// This program is part of Protra.
//
// Protra is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
// 
// $Id: ChartBox.cs,v 1.7 2007-12-27 08:34:39 panacoran Exp $

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
using Protra.Lib.Db;
using Protra.Lib.Lang;
using Protra.Lib.Lang.Executer;

namespace Protra.Lib.Controls
{
	/// <summary>
	/// チャートを描画するコントロールです。
	/// </summary>
	public class ChartBox : System.Windows.Forms.UserControl, IExecuterFactory
	{
		/// <summary>
		/// 描画関数の呼び出しを表すクラスです。
		/// </summary>
		private class RenderCall
		{
			private DelegateExecuter.Delegate del;
			private Value[] args;
			
			/// <summary>
			/// コンストラクタ
			/// </summary>
			/// <param name="del">デリゲート</param>
			/// <param name="args">引数</param>
			public RenderCall(DelegateExecuter.Delegate del, Value[] args)
			{
				this.del = del;
				this.args = args;
			}

			/// <summary>
			/// 呼び出しを実行します。
			/// </summary>
			public void Execute()
			{
				del(args, 0);
			}
		}

		/// <summary>
		/// 左側にあける間隔
		/// </summary>
		private const int LeftMargin = 55;
		/// <summary>
		/// 上側にあける間隔
		/// </summary>
		private const int TopMargin = 10;
		/// <summary>
		/// 右側にあける間隔
		/// </summary>
		private const int RightMargin = 10;
		/// <summary>
		/// 下側にあける間隔
		/// </summary>
		private const int BottomMargin = 20;

		/// <summary>
		/// プログラムファイル名
		/// </summary>
		private string programFile;
		/// <summary>
		/// インタプリタファサード
		/// </summary>
		private InterpreterFacade facade;
		/// <summary>
		/// 描画対象となるPriceの配列
		/// </summary>
		private Price[] prices;
		/// <summary>
		/// チャートの右端のインデックス
		/// </summary>
		private int rightIndex;
		/// <summary>
		/// 現在描画中のインデックス
		/// </summary>
		private int index;
		/// <summary>
		/// 横軸の間隔
		/// </summary>
		private int dx = 6;
		/// <summary>
		/// 現在描画中のX座標
		/// </summary>
		private int x;
		/// <summary>
		/// 何ヶ月ごとに月を描画するか
		/// </summary>
		private int monthInterval = 1;
		/// <summary>
		/// 描画対象のGraphics
		/// </summary>
		private Graphics g;
		/// <summary>
		/// チャートの矩形
		/// </summary>
		private Rectangle chartRect;
		/// <summary>
		/// 縦軸の最大値と最小値
		/// </summary>
		private double maxY, minY;
		/// <summary>
		/// 仮想描画中か？
		/// </summary>
		private bool isVirtualRendering;
		/// <summary>
		/// 描画関数の呼び出しリスト
		/// </summary>
		private ArrayList renderCalls;
		/// <summary>
		/// FunctionTypeとDelegateExecuter.Delegate
		/// を対応させるハッシュ
		/// </summary>
		private Hashtable hash = new Hashtable();
		/// <summary>
		/// IExecuterを生成するFactory
		/// </summary>
		private ExecuterFactoryUnion factory;

		/// <summary>
		/// 必要なデザイナ変数です。
		/// </summary>
		private System.ComponentModel.Container components = null;

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public ChartBox()
		{
			// この呼び出しは、Windows.Forms フォーム デザイナで必要です。
			InitializeComponent();
			
			// ハッシュにデリゲートを登録します。
			hash.Add(new FunctionType("X", 0), new DelegateExecuter.Delegate(X));
			hash.Add(new FunctionType("Dx", 0), new DelegateExecuter.Delegate(_Dx));
			hash.Add(new FunctionType("Index", 0), new DelegateExecuter.Delegate(Index));
			hash.Add(new FunctionType("RightIndex", 0), new DelegateExecuter.Delegate(_RightIndex));
			hash.Add(new FunctionType("Year", 0), new DelegateExecuter.Delegate(Year));
			hash.Add(new FunctionType("Month", 0), new DelegateExecuter.Delegate(Month));
			hash.Add(new FunctionType("Day", 0), new DelegateExecuter.Delegate(Day));
			hash.Add(new FunctionType("DayOfWeek", 0), new DelegateExecuter.Delegate(DayOfWeek));
			hash.Add(new FunctionType("Open", 0), new DelegateExecuter.Delegate(Open));
			hash.Add(new FunctionType("High", 0), new DelegateExecuter.Delegate(High));
			hash.Add(new FunctionType("Low", 0), new DelegateExecuter.Delegate(Low));
			hash.Add(new FunctionType("Close", 0), new DelegateExecuter.Delegate(Close));
			hash.Add(new FunctionType("Volume", 0), new DelegateExecuter.Delegate(Volume));
			hash.Add(new FunctionType("Code", 0), new DelegateExecuter.Delegate(Code));
			hash.Add(new FunctionType("Rgb", 3), new DelegateExecuter.Delegate(Rgb));
			hash.Add(new FunctionType("DrawLine", 5), new DelegateExecuter.Delegate(DrawLine));
			hash.Add(new FunctionType("DrawRectangle", 5), new DelegateExecuter.Delegate(DrawRectangle));
			hash.Add(new FunctionType("DrawEllipse", 5), new DelegateExecuter.Delegate(DrawEllipse));
			hash.Add(new FunctionType("DrawString", 4), new DelegateExecuter.Delegate(DrawString));
			hash.Add(new FunctionType("DrawString", 5), new DelegateExecuter.Delegate(DrawString));
			hash.Add(new FunctionType("FillRectangle", 5), new DelegateExecuter.Delegate(FillRectangle));
			hash.Add(new FunctionType("FillEllipse", 5), new DelegateExecuter.Delegate(FillEllipse));

			// 数学関数との合同Factoryを形成
			factory = new ExecuterFactoryUnion();
			factory.Add(this);
			factory.Add(new MathExecuterFactory());
		}

		/// <summary>
		/// 使用されているリソースに後処理を実行します。
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary>
		/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
		/// コード エディタで変更しないでください。
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
			// 
			// ChartBox
			// 
			this.Name = "ChartBox";
			this.SizeChanged += new System.EventHandler(this.ChartBox_SizeChanged);
			this.Paint += new System.Windows.Forms.PaintEventHandler(this.ChartBox_Paint);

		}
		#endregion

		/// <summary>
		/// プログラムファイル名を取得または設定します。
		/// </summary>
		public string ProgramFile
		{
			get { return programFile; }
			set
			{
				programFile = value;
				if(programFile == null)
				{
					facade = null; return;
				}

				try
				{
					facade = new InterpreterFacade(programFile, factory);
				}
				catch(ParseException exc)
				{
					facade = null;
					MessageBox.Show(exc.Message);
				}

				this.Invalidate();
			}
		}

		/// <summary>
		/// 描画対象となるPriceの配列を取得または設定します。
		/// </summary>
		public Price[] Prices
		{
			get { return prices; }
			set
			{
				prices = value;
				if(prices != null)
					rightIndex = prices.Length - 1;
				this.Invalidate();
			}
		}

		/// <summary>
		/// チャートの右端のインデックスを取得または設定します。
		/// </summary>
		public int RightIndex
		{
			get { return rightIndex; }
			set
			{
				rightIndex = value;
				this.Invalidate();
			}
		}

		/// <summary>
		/// 横軸の間隔を取得または設定します。
		/// </summary>
		public int Dx
		{
			get { return dx; }
			set
			{
				dx = value;
				this.Invalidate();
			}
		}

		/// <summary>
		/// 何ヶ月ごとに月を描画するかを示す値を取得または設定します。
		/// </summary>
		public int MonthInterval
		{
			get { return monthInterval; }
			set
			{
				monthInterval = value;
				this.Invalidate();
			}
		}

		/// <summary>
		/// 何個分の価格が描画されるかを取得します。
		/// </summary>
		public int Count
		{
			get { return (this.Width - LeftMargin - RightMargin - 1)/dx; } 
		}

		private void ChartBox_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			if(facade == null || prices == null || prices.Length == 0)
				return;
			
			g = e.Graphics;
			Brush brush = new SolidBrush(this.ForeColor);
			Pen pen = new Pen(this.ForeColor);
			Color dotColor = Color.FromArgb(
				(this.BackColor.A + this.ForeColor.A)/2,
				(this.BackColor.R + this.ForeColor.R)/2,
				(this.BackColor.G + this.ForeColor.G)/2,
				(this.BackColor.B + this.ForeColor.B)/2);
			Pen dotPen = new Pen(dotColor);
			dotPen.DashStyle = DashStyle.Dot;
			chartRect = new Rectangle(
				LeftMargin, TopMargin,
				this.Width - LeftMargin - RightMargin - 1,
				this.Height - TopMargin - BottomMargin);
			
			// グローバル変数の設定
			facade.GlobalVariableTable.Clear();
			facade.GlobalVariableTable["$Names"] = new Value(new Value[6]);
			facade.GlobalVariableTable["$Colors"] = new Value(new Value[6]);
			facade.GlobalVariableTable["$IsTimeSeries"] = new Value(true);

			// 仮想描画によって縦軸のスケールを得る
			int count = chartRect.Width/dx;
			maxY = double.MinValue;
			minY = double.MaxValue;
			isVirtualRendering = true;
			renderCalls = new ArrayList();
			index = Math.Max(0, rightIndex - count + 1);
			x = chartRect.Right - (rightIndex - index + 1)*dx;
			while(index <= rightIndex)
			{
				try
				{
					facade.Execute();
				}
				catch(Exception exc)
				{
					string msg = exc.Message;
					g.DrawString(msg, this.Font, brush, 0, 0);
					return;
				}
				x += dx;
				index++;
			}

			// 縦軸の値の描画
			if(maxY != double.MinValue)
			{
				if(maxY == minY)
				{
					maxY += 1;
					minY -= 1;
				}
				double band = (maxY - minY)/5;
				double a = 0.01;
				while(true)
				{
					if(band < 10*a)
					{
						for(double b = a; b <= 10*a; b += a)
							if(b >= band)
							{
								band = b;
								break;
							}
						break;
					}
					a *= 10;
				}
				if(maxY < 0)
					maxY = (int)(maxY/band)*band;
				else
					maxY = Math.Ceiling(maxY/band)*band;
				if(minY < 0)
					minY = -Math.Ceiling(-minY/band)*band;
				else
					minY = (int)(minY/band)*band;
				double m = minY + band;
				double ratio = chartRect.Height/(maxY - minY);
				while(m < maxY)
				{
					m = (long)(100*m)/100.0;
					string text = m.ToString();
					
					SizeF size = g.MeasureString(text, this.Font);
					g.DrawString(text, this.Font, brush,
						chartRect.Left - size.Width,
						(float)(chartRect.Bottom - (m - minY)*ratio - size.Height/2));
					g.DrawLine(dotPen,
						chartRect.Left, (float)(chartRect.Bottom - (m - minY)*ratio),
						chartRect.Right, (float)(chartRect.Bottom - (m - minY)*ratio));
					m += band;
				}
			}

			// 日付の描画
			if(facade.GlobalVariableTable["$IsTimeSeries"].IsTrue)
			{
				index = Math.Max(0, rightIndex - count + 1);
				x = chartRect.Right - (rightIndex - index + 1)*dx;
				DateTime preDate = prices[index].Date;
				while(index <= rightIndex)
				{
					DateTime date = prices[index].Date;
					if((date.Month - 1) % monthInterval == 0)
						if(date.Month != preDate.Month)
						{
							string text = date.ToString("yy/MM");
							SizeF size = g.MeasureString(text, this.Font);
							g.DrawString(text, this.Font, brush,
								x - (dx + size.Width)/2, chartRect.Bottom);
							g.DrawLine(dotPen,
								x - dx/2, chartRect.Top,
								x - dx/2, chartRect.Bottom);
							preDate = date;
						}
					x += dx;
					index++;
				}
			}

			// 本番の描画
			Region oldClip = g.Clip;
			g.Clip = new Region(chartRect);
			isVirtualRendering = false;
			foreach(RenderCall call in renderCalls)
				call.Execute();
			g.Clip = oldClip;
			renderCalls = null;

			// 枠の描画
			g.DrawRectangle(pen, chartRect);

			// 指標名の描画
			Value[] names = (Value[])facade.GlobalVariableTable["$Names"].InnerValue;
			Value[] colors = (Value[])facade.GlobalVariableTable["$Colors"].InnerValue;
			x = chartRect.Left + 10;
			for(int i = 0; i < names.Length; i++)
				if(names[i] != null && colors[i] != null)
				{
					string text = (string)names[i].InnerValue;
					int rgb = (int)colors[i].InnerValue;
					SizeF size = g.MeasureString(text, this.Font);
					Rectangle rect = new Rectangle(x, chartRect.Top + 10, (int)size.Height, (int)size.Height);
					g.FillRectangle(new SolidBrush(Color.FromArgb(rgb)), rect);
					g.DrawRectangle(pen, rect);
					g.DrawString(text, this.Font, brush, rect.Right, rect.Top);
					x = (int)(rect.Right + size.Width); 
				}
		}

		private void ChartBox_SizeChanged(object sender, System.EventArgs e)
		{
			this.Invalidate();
		}

		/// <summary>
		/// IExecuterを生成します。
		/// </summary>
		/// <exception cref="Protra.Lib.Lang.NoSuchExecuterException">
		/// IExecuterが存在しない場合にthrowされます。
		/// </exception>
		/// <param name="ft">関数のタイプ</param>
		/// <returns>関数を実行するIExcecuterです。</returns>
		public IExecuter CreateExecuter(FunctionType ft)
		{
			if(hash.ContainsKey(ft))
				return new DelegateExecuter((DelegateExecuter.Delegate)hash[ft]);
			throw new NoSuchExecuterException();
		}

		/// <summary>
		/// 現在描画中のX座標を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>X座標です。</returns>
		private Value X(Value[] args, int at)
		{
			return new Value(x + at*dx);
		}

		/// <summary>
		/// 横軸の間隔を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>横軸の間隔です。</returns>
		private Value _Dx(Value[] args, int at)
		{
			return new Value(dx);
		}

		/// <summary>
		/// 現在描画中のインデックスを取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>インデックスです。</returns>
		private Value Index(Value[] args, int at)
		{
			return new Value(index + at);
		}

		/// <summary>
		/// 右端のインデックスを取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>右端のインデックスです。</returns>
		private Value _RightIndex(Value[] args, int at)
		{
			return new Value(rightIndex);
		}

		/// <summary>
		/// 現在描画中の年を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>年です。</returns>
		private Value Year(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].Date.Year);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 現在描画中の月を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>月です。</returns>
		private Value Month(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].Date.Month);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 現在描画中の日付を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>日付です。</returns>
		private Value Day(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].Date.Day);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 現在描画中の曜日を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>曜日です。</returns>
		private Value DayOfWeek(Value[] args, int at)
		{
			try
			{
				return new Value((int)prices[index + at].Date.DayOfWeek);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 始値を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>始値です。</returns>
		private Value Open(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].Open);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 高値を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>高値です。</returns>
		private Value High(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].High);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 安値を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>安値です。</returns>
		private Value Low(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].Low);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 終値を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>終値です。</returns>
		private Value Close(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].Close);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 出来高を取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>出来高です。</returns>
		private Value Volume(Value[] args, int at)
		{
			try
			{
				return new Value(prices[index + at].Volume);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}

		/// <summary>
		/// 銘柄コードを取得します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>銘柄コードです。</returns>
		private Value Code(Value[] args, int at)
		{
			try
			{
				return new Value(prices[0].Brand.Code);
			}
			catch(IndexOutOfRangeException)
			{
				return null;
			}
		}
		
		/// <summary>
		/// Rgb値を計算します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>計算結果です。</returns>
		private Value Rgb(Value[] args, int at)
		{
			int r = (int)args[0].InnerValue;
			int g = (int)args[1].InnerValue;
			int b = (int)args[2].InnerValue;
			int rgb = Color.FromArgb(r, g, b).ToArgb();
			return new Value(rgb);
		}

		/// <summary>
		/// 直線を描画します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>nullを返します。</returns>
		private Value DrawLine(Value[] args, int at)
		{
			int rgb = (int)args[0].InnerValue;
			double x1 = (double)args[1].Cast(Value.Type.Float).InnerValue;
			double y1 = (double)args[2].Cast(Value.Type.Float).InnerValue;
			double x2 = (double)args[3].Cast(Value.Type.Float).InnerValue;
			double y2 = (double)args[4].Cast(Value.Type.Float).InnerValue;
			if (isVirtualRendering)
			{
				maxY = Math.Max(Math.Max(y1, y2), maxY);
				minY = Math.Min(Math.Min(y1, y2), minY);
				renderCalls.Add(new RenderCall(new DelegateExecuter.Delegate(DrawLine), args));
			}
			else
			{
				y1 = chartRect.Bottom - chartRect.Height * (y1 - minY) / (maxY - minY);
				y2 = chartRect.Bottom - chartRect.Height * (y2 - minY) / (maxY - minY);
				this.g.DrawLine(new Pen(Color.FromArgb(rgb)),
					(float)x1, (float)y1, (float)x2, (float)y2);
			}
			return null;
		}

		/// <summary>
		/// 矩形を描画します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>nullを返します。</returns>
		private Value DrawRectangle(Value[] args, int at)
		{
			int rgb = (int)args[0].InnerValue;
			double x = (double)args[1].Cast(Value.Type.Float).InnerValue;
			double y = (double)args[2].Cast(Value.Type.Float).InnerValue;
			double width = (double)args[3].Cast(Value.Type.Float).InnerValue;
			double height = (double)args[4].Cast(Value.Type.Float).InnerValue;
			if (isVirtualRendering)
			{
				maxY = Math.Max(Math.Max(y, y + height), maxY);
				minY = Math.Min(Math.Min(y, y + height), minY);
				renderCalls.Add(new RenderCall(new DelegateExecuter.Delegate(DrawRectangle), args));
			}
			else
			{
				y = chartRect.Bottom - chartRect.Height * (y - minY) / (maxY - minY);
				height = chartRect.Height * height / (maxY - minY);
				this.g.DrawRectangle(new Pen(Color.FromArgb(rgb)),
					(float)x, (float)(y - height), (float)width, (float)height);
			}
			return null;
		}

		/// <summary>
		/// 楕円を描画します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>nullを返します。</returns>
		private Value DrawEllipse(Value[] args, int at)
		{
			int rgb = (int)args[0].InnerValue;
			double x = (double)args[1].Cast(Value.Type.Float).InnerValue;
			double y = (double)args[2].Cast(Value.Type.Float).InnerValue;
			double width = (double)args[3].Cast(Value.Type.Float).InnerValue;
			double height = (double)args[4].Cast(Value.Type.Float).InnerValue;
			if (isVirtualRendering)
			{
				maxY = Math.Max(Math.Max(y, y + height), maxY);
				minY = Math.Min(Math.Min(y, y + height), minY);
				renderCalls.Add(new RenderCall(new DelegateExecuter.Delegate(DrawEllipse), args));
			}
			else
			{
				y = chartRect.Bottom - chartRect.Height * (y - minY) / (maxY - minY);
				height = chartRect.Height*height / (maxY - minY);
				this.g.DrawEllipse(new Pen(Color.FromArgb(rgb)),
					(float)x, (float)(y - height), (float)width, (float)height);
			}
			return null;
		}

		/// <summary>
		/// 文字列を描画します。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>nullを返します。</returns>
		private Value DrawString(Value[] args, int at)
		{
			int i = 0;
			int rgb = Color.Black.ToArgb();
			if (args.Length > 4)
				rgb = (int)args[i++].InnerValue;
			string text = (string)args[i++].InnerValue;
			double x = (double)args[i++].Cast(Value.Type.Float).InnerValue;
			double y = (double)args[i++].Cast(Value.Type.Float).InnerValue;
			int dh = (int)args[i++].InnerValue;
			if(isVirtualRendering)
			{
				maxY = Math.Max(y, maxY);
				minY = Math.Min(y, minY);
				renderCalls.Add(new RenderCall(new DelegateExecuter.Delegate(DrawString), args));
			}
			else
			{
				SizeF size = g.MeasureString(text, this.Font);
				y = chartRect.Bottom - chartRect.Height * (y - minY) / (maxY - minY);
				this.g.DrawString(text, this.Font, new SolidBrush(Color.FromArgb(rgb)),
					(float)(x - size.Width / 2), (float)(y - size.Height / 2) - dh);
			}
			return null;
		}

		/// <summary>
		/// 矩形を塗りつぶします。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>nullを返します。</returns>
		private Value FillRectangle(Value[] args, int at)
		{
			int rgb = (int)args[0].InnerValue;
			double x = (double)args[1].Cast(Value.Type.Float).InnerValue;
			double y = (double)args[2].Cast(Value.Type.Float).InnerValue;
			double width = (double)args[3].Cast(Value.Type.Float).InnerValue;
			double height = (double)args[4].Cast(Value.Type.Float).InnerValue;
			if (isVirtualRendering)
			{
				maxY = Math.Max(Math.Max(y, y + height), maxY);
				minY = Math.Min(Math.Min(y, y + height), minY);
				renderCalls.Add(new RenderCall(new DelegateExecuter.Delegate(FillRectangle), args));
			}
			else
			{
				y = chartRect.Bottom - chartRect.Height * (y - minY) / (maxY - minY);
				height = chartRect.Height * height / (maxY - minY);
				if (-1 < height && height < 0)
					height = -1;
				if (0 < height && height < 1)
					height = 1;
				this.g.FillRectangle(new SolidBrush(Color.FromArgb(rgb)),
					(float)x, (float)(y - height), (float)width, (float)height);
			}
			return null;
		}

		/// <summary>
		/// 楕円を塗りつぶします。
		/// </summary>
		/// <param name="args">引数の配列</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>nullを返します。</returns>
		private Value FillEllipse(Value[] args, int at)
		{
			int rgb = (int)args[0].InnerValue;
			double x = (double)args[1].Cast(Value.Type.Float).InnerValue;
			double y = (double)args[2].Cast(Value.Type.Float).InnerValue;
			double width = (double)args[3].Cast(Value.Type.Float).InnerValue;
			double height = (double)args[4].Cast(Value.Type.Float).InnerValue;
			if(isVirtualRendering)
			{
				maxY = Math.Max(Math.Max(y, y + height), maxY);
				minY = Math.Min(Math.Min(y, y + height), minY);
				renderCalls.Add(new RenderCall(new DelegateExecuter.Delegate(FillEllipse), args));
			}
			else
			{
				y = chartRect.Bottom - chartRect.Height * (y - minY) / (maxY - minY);
				height = chartRect.Height * height / (maxY - minY);
				if (-1 < height && height < 0)
					height = -1;
				if (0 < height && height < 1)
					height = 1;
				this.g.FillEllipse(new SolidBrush(Color.FromArgb(rgb)),
					(float)x, (float)(y - height), (float)width, (float)height);
			}
			return null;
		}
	}
}
