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

using System;
using System.Collections;
using System.Globalization;

namespace Protra.Lib.Db
{
	/// <summary>
	/// Brandテーブルのレコードを表す。
	/// </summary>
	public class Brand: IRecord, IComparable
	{
		private int id;
		private int code;
		private MarketId marketId;
		private string name;

		/// <summary>
		/// 既定のコンストラクタ
		/// </summary>
		public Brand() {}

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public Brand(int id, int code, MarketId marketId, string name)
		{
			this.id = id;
			this.code = code;
			this.marketId = marketId;
			this.name = name;
		}

		/// <summary>
		/// IDを取得する。
		/// </summary>
		public int Id
		{
			get { return id; }
		}

        /// <summary>
        /// 新しいIDを取得する。
        /// </summary>
        public string NewId
        {
            get { return (code * 10 + (int)marketId).ToString(); }
        }

		/// <summary>
		/// 銘柄コードを取得する。
		/// </summary>
		public int Code
		{
			get { return code; }
		}

		/// <summary>
		/// 市場IDを取得する。
		/// </summary>
		public MarketId MarketId
		{
			get { return marketId; }
		}

		/// <summary>
		/// 市場IDに対応するMarketオブジェクトを取得する。
		/// </summary>
		public Market Market
		{
			get { return MarketTable.GetRecord(marketId); }
		}

		/// <summary>
		/// 銘柄名を取得あるいは変更する。
		/// </summary>
		public string Name
		{
			get { return name; }
			set { 
				BrandTable.ChangeBrandName(id, value);
				name = value;
			}
		}

		/// <summary>
		/// データベースのレコードをオブジェクトに変換する。
		/// </summary>
		/// <param name="values">ネイティブ形式の値の配列</param>
		/// <returns>変換後のBrandオブジェクトを返す。</returns>
		public Object ToObject(Object[] values)
		{
			return new Brand((int)values[0], // @Id
				(int)values[1], // @Code
				(MarketId)values[2], // @MarketId
				(string)values[3]); // @Name
		}

		/// <summary>
		/// インスタンスを文字列表現に変換する。
		/// </summary>
		/// <returns>文字列表現</returns>
		public override string ToString()
		{
            return Code + " " + Market.Name + " " + Name;
		}

		#region IComparable メンバ
		/// <summary>
		/// 指定されたオブジェクトと自分を比較する。
		/// </summary>
		/// <param name="obj">比較対象のオブジェクト</param>
		/// <returns>自分が小さければ0より小さな値を、自分が大きければ0より大きな値を、等しければ0を返す。</returns>
		public int CompareTo(object obj)
		{
			Brand brand = (Brand)obj;
			if (code == brand.Code)
				return marketId - brand.MarketId;
			return code - brand.Code;
		}
		#endregion
	}

	/// <summary>
	/// Brandテーブルを操作する。
	/// このクラスは最初にすべてのレコードをArrayListに読み込む。
	/// 一部のメソッドがArrayListのインデックス+1がレコードの@Idと一致することを仮定している。
	/// Brandテーブルを不用意に編集するとこの仮定が崩れるので要注意。
	/// </summary>
	public class BrandTable
	{
		private static ArrayList byId;
		private static SortedList[] byMarketId;

		private static Connection conn;
		private static Connection Conn
		{
			get 
			{
				if (conn != null)
					return conn;
				conn = new Connection("protra");
				return conn;
			}
		}

		/// <summary>
		/// すべてのレコードを保持するArrayListを取得する。
		/// </summary>
		private static ArrayList ById
		{
			get {
				if (byId != null)
					return byId;

				string sql = "SELECT * FROM Brand ORDER BY @Id";
				byId = Conn.Query(sql, new Brand());
				return byId;
			}
		}

		/// <summary>
		/// すべてのレコードを保持する配列を取得する。
		/// この配列は市場ID番目に、銘柄コードとレコードを対応付けるSortedListを保持する。
		/// </summary>
		private static SortedList[] ByMarketId
		{
			get {
				if (byMarketId != null)
					return byMarketId;

				byMarketId = new SortedList[(int)MarketId.Max];
				for (int i = 0; i < (int)MarketId.Max; i++)
					byMarketId[i] = new SortedList();
				foreach (Brand brand in ById)
				{
					SortedList byCode = byMarketId[(int)brand.MarketId - 1];
					byCode[brand.Code] = brand;
				}
				return byMarketId;
			}
		}

		// 読む

		/// <summary>
		/// 指定された銘柄IDのレコードを取得する。
		/// </summary>
		/// <param name="id">銘柄ID</param>
		/// <returns>Brandオブジェクトを返す。</returns>
		//
		public static Brand GetRecord(int id) 
		{
            try
            {
                return (Brand)ById[id - 1];
            }
            catch (ArgumentOutOfRangeException)
            {
                return null;
            }
		}

        /// <summary>
        /// 新形式のIDの示すレコードを取得する。
        /// </summary>
        /// <param name="newid">新形式のID</param>
        /// <returns></returns>
        public static Brand GetRecordByNewId(string newid)
        {
            int i = int.Parse(newid);
            return GetRecord((MarketId)(i % 10), i / 10);
        }

		/// <summary>
		/// 指定された市場IDと銘柄コードのレコードを取得する。
		/// </summary>
		/// <param name="marketId">市場ID</param>
		/// <param name="code">銘柄コード</param>
		/// <returns>Brandオブジェクトを返す。レコードが存在しない場合にはnullを返す。</returns>
		public static Brand GetRecord(MarketId marketId, int code)
		{
            try
            {
                return (Brand)ByMarketId[(int)marketId - 1][code];
            }
            catch (ArgumentOutOfRangeException)
            {
                return null;
            }
		}

		/// <summary>
		/// 指定された市場の銘柄をすべて取得する。
		/// </summary>
		/// <param name="marketId">市場ID</param>
		/// <returns>Brandオブジェクトの配列。</returns>
		public static Brand[] GetRecords(MarketId marketId)
		{
			SortedList byCode = ByMarketId[(int)marketId - 1];
			Brand[] result = new Brand[byCode.Count];
			byCode.Values.CopyTo(result, 0);
			return result;
		}

		/// <summary>
		/// 指定された銘柄コードを持つレコードをすべて取得する。
		/// </summary>
		/// <param name="code"></param>
		/// <returns></returns>
		public static Brand[] GetRecords(int code)
		{
			ArrayList result = new ArrayList();
			foreach (SortedList byCode in ByMarketId)
				if (byCode[code] != null)
					result.Add(byCode[code]);
			return (Brand[])result.ToArray(typeof(Brand));
		}

		/// <summary>
		/// 指定された文字列を銘柄名に含むレコードをすべて取得する。
		/// </summary>
		/// <param name="name">検索する文字列</param>
		/// <returns>Brandオブジェクトの配列</returns>
		public static Brand[] GetRecords(string name)
		{
			ArrayList result = new ArrayList();
			CompareInfo comp = CompareInfo.GetCompareInfo("ja-JP");
			foreach (SortedList byCode in ByMarketId)
				foreach (Brand brand in byCode.Values)
					if (comp.IndexOf(brand.Name, name,
							CompareOptions.IgnoreCase |	CompareOptions.IgnoreWidth) != -1)
						result.Add(brand);
			result.Sort();
			return (Brand[])result.ToArray(typeof(Brand));
		}

		/// <summary>
		/// 指定された市場の指定範囲の銘柄コードのレコードを取得する。
		/// </summary>
		/// <param name="marketId">市場ID</param>
		/// <param name="minCode">銘柄コードの最小値</param>
		/// <param name="maxCode">銘柄コードの最大値</param>
		/// <returns>Brandオブジェクトの配列</returns>
		public static Brand[] GetRecords(MarketId marketId, int minCode, int maxCode)
		{
			ArrayList result = new ArrayList();
			foreach (Brand brand in ByMarketId[(int)marketId - 1].Values)
				if (brand.Code >= minCode && brand.Code <= maxCode)
					result.Add(brand);
			return (Brand[])result.ToArray(typeof(Brand));
		}

		/// <summary>
		/// 銘柄IDの最大値を取得する。
		/// </summary>
		public static int MaxId
		{
			get { return ById.Count; }
		}

		// 書く

		/// <summary>
		/// 市場IDと銘柄コードに対応するレコードを取得すると同時に、
		/// 銘柄名が<code>name</code>と異なる場合には変更する。
		/// レコードが存在しない場合には作成する。
		/// </summary>
		/// <param name="marketId">市場ID</param>
		/// <param name="code">銘柄コード</param>
		/// <param name="name">銘柄名</param>
		/// <returns>追加したレコードのBrandオブジェクト。</returns>
		public static Brand GetRecordOrCreate(MarketId marketId, int code, string name)
		{
			Brand record = GetRecord(marketId, code);
			if (record != null)
			{
				if (record.Name != name)
					record.Name = name;
				return record;
			}
			string sql = string.Format(
				"INSERT INTO Brand(@Code, @MarketId, @Name) " +
				"VALUES({0}, {1}, '{2}')", code, (int)marketId, name);
			Conn.Execute(sql);
			record = new Brand(ById.Count + 1, code, marketId, name);
			ById.Add(record);
			ByMarketId[(int)marketId - 1][code] = record;
			return record;
		}

		/// <summary>
		/// 銘柄名を変更する。
		/// </summary>
		/// <param name="id">銘柄ID</param>
		/// <param name="name">新しい名前</param>
		public static void ChangeBrandName(int id, string name)
		{
			string sql = string.Format(
				"UPDATE Brand SET @Name='{0}' WHERE @Id={1}", name, id);
			Conn.Execute(sql);
		}

		/// <summary>
		/// 全データを削除する。
		/// </summary>
		public static void Delete()
		{
			string sql = "DELETE FROM Brand";
			Conn.Execute(sql);
			byId = null;
			byMarketId = null;
		}
	}
}
