// Copyright (C) 2003,2005 Daisuke Arai <darai@users.sourceforge.jp>
// Copyright (C) 2004 M. Ishiguro <mishiguro@users.sourceforge.jp>
// Copyright (C) 2004-2008 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: SimulateTextBox.cs 306 2010-03-19 13:21:22Z panacoran $

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Protra.Lib.Db;
using Protra.Lib.Lang;

namespace Protra.Lib.Controls
{
	/// <summary>
	/// システムのシミュレーション用のテキストボックスです。
	/// </summary>
	public class SimulateTextBox : System.Windows.Forms.TextBox
	{
		/// <summary>
		/// トレーディングシステム
		/// </summary>
		private TradingSystem system;
		/// <summary>
		/// インタプリタ
		/// </summary>
		private Interpreter interpreter;

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

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public SimulateTextBox()
		{
			// この呼び出しは、Windows.Forms フォーム デザイナで必要です。
			InitializeComponent();
		}
		
		/// <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();
		}
		#endregion

		/// <summary>
		/// 全システムログを削除します。
		/// </summary>
		public static void DeleteLog()
		{
			SystemLogTable.Delete();
			if(Directory.Exists(Global.DirGlobalCache))
				Directory.Delete(Global.DirGlobalCache, true);
		}

		/// <summary>
		/// 指定されたシステムのシステムログを削除します。
		/// </summary>
		public static void DeleteLog(int systemId)
		{
			SystemLogTable.Delete(systemId);
			string dir = Path.Combine(Global.DirGlobalCache, systemId.ToString());
			if(Directory.Exists(dir))
				Directory.Delete(dir, true);
		}

		/// <summary>
		/// シミュレーションを実行します。
		/// </summary>
		/// <param name="prices">売買対象となるPriceの配列</param>
        /// <param name="worker">このメソッドを実行するBackgroundWorker</param>
        /// <param name="e">BackgroundWorkerのDoWorkイベントの引数</param>
		public void Simulate(Price[] prices, BackgroundWorker worker, DoWorkEventArgs e)
		{
			if(prices.Length == 0)
				return;
            SimulateBuiltins bltin = new SimulateBuiltins();
            bltin.Prices = prices;
            bltin.RightIndex = prices.Length - 1;
            bltin.System = system;
            bltin.AppendText = AppendText;
            interpreter.Builtins = bltin;
			interpreter.GlobalVariableTable.Clear();
			bltin.Index = 0;

            LoadGlobalCache();
            Split[] split = SplitTable.GetRecords(bltin.Brand.Code);
            if (bltin.Index > 0 && split.Length > 0 &&
                prices[prices.Length - 1].Date >= split[split.Length - 1].Date &&
                prices[bltin.Index - 1].Date < split[split.Length - 1].Date)
            {
                string msg = string.Format(
                    "{0} {1} 分割調整のためログを削除して再実行\r\n",
                    bltin.Brand.Code, bltin.Brand.Name);
                AppendText(msg);
                SystemLogTable.Delete(system.Id, bltin.Brand.Id);
                bltin.Index = 0;
                interpreter.GlobalVariableTable.Clear();
            }

			while (bltin.Index < prices.Length)
			{
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }
				interpreter.Execute();
				bltin.Index++;
			}
            SaveGlobalCache();
		}

		/// <summary>
		/// グローバルキャッシュをロードします。
		/// </summary>
		private void LoadGlobalCache()
		{
            int brandId = interpreter.Builtins.Brand.Id;
            string file =
                Path.Combine(Global.DirGlobalCache,
                system.Id.ToString() + Path.DirectorySeparatorChar
                + (brandId / 1000) + Path.DirectorySeparatorChar + brandId);
			if (File.Exists(file))
			{
                Stream stream = new FileStream(file, FileMode.Open);
                BinaryFormatter formatter = new BinaryFormatter();
                try
                {
                    interpreter.Builtins.Index = (int)formatter.Deserialize(stream);
                    interpreter.GlobalVariableTable =
                        (Dictionary<string, Value>)formatter.Deserialize(stream);
                    stream.Close();
                }
                catch (SerializationException)
                {
                    stream.Close();
                    AppendText("ログに互換性がないのですべて削除します。\r\n");
                    DeleteLog();
                    interpreter.Builtins.Index = 0;
                }
            }
		}

		/// <summary>
		/// グローバルキャッシュをセーブします。
		/// </summary>
		private void SaveGlobalCache()
		{
			if(! Directory.Exists(Global.DirGlobalCache))
				Directory.CreateDirectory(Global.DirGlobalCache);
			string dir = Path.Combine(Global.DirGlobalCache, system.Id.ToString());
			if(! Directory.Exists(dir))
				Directory.CreateDirectory(dir);
            int brandId = interpreter.Builtins.Brand.Id;
			dir = Path.Combine(dir, (brandId / 1000).ToString());
			if(! Directory.Exists(dir))
				Directory.CreateDirectory(dir);
			string file = Path.Combine(dir, brandId.ToString());
			Stream stream = new FileStream(file, FileMode.Create);
			BinaryFormatter formatter = new BinaryFormatter();
			formatter.Serialize(stream, interpreter.Builtins.Index);
			formatter.Serialize(stream, interpreter.GlobalVariableTable);
			stream.Close();
		}

		/// <summary>
		/// トレーディングシステムを取得または設定します。
		/// </summary>
		public TradingSystem System
		{
			get { return system; }
			set
			{
				system = value;
				if(system == null)
				{
					interpreter = null;
					return;
				}
				// プログラムのロード
				string programFile = Path.Combine(Global.DirSystem, system.File);
				interpreter = new Interpreter(programFile);
			}
		}

        /// <summary>
        /// 文字列を表示する。
        /// </summary>
        /// <param name="msg"></param>
        public new void AppendText(string msg)
        {
            BeginInvoke((AppendTextDelegate)base.AppendText, new object[] {msg});
        }
	}
}
