using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CookComputing.XmlRpc;

namespace BTS.Trac
{
    /// <summary>
    /// チケット
    /// </summary>
    public class Ticket
    {
        /// <summary>
        /// チケット属性
        /// </summary>
        class TicketAttributes
        {
            public const string SUMMARY = "summary";            // 概要
            public const string DESCRIPTION = "description";    // 説明
            public const string TYPE = "type";                  // 分類
            public const string REPORTER = "reporter";          // 報告者
            public const string OWNER = "owner";                // 担当者
            public const string CC = "cc";                      // 関係者
            public const string PRIORITY = "priority";          // 優先度
            public const string STATUS = "status";              // 状態
            public const string COMPONENT = "component";        // コンポーネント
            public const string MILESTONE = "milestone";        // マイルストーン
            public const string VERSION = "version";            // バージョン
            public const string KEYWORDS = "keywords";          // キーワード
            public const string RESOLUTION = "resolution";      // 解決方法
            public const string DUE_ASSIGN = "due_assign";      // 開始予定日
            public const string DUE_CLOSE = "due_close";        // 終了予定日
            public const string COMPLETE = "complete";          // 進捗率
        }

        #region フィールド

        /// <summary>
        /// 属性
        /// </summary>
        XmlRpcAttributes Attributes = new XmlRpcAttributes();

        /// <summary>
        /// ID
        /// </summary>
        public int ID
        {
            get;
            private set;
        }

        /// <summary>
        /// 作成年月日
        /// </summary>
        public DateTime CreateTime
        {
            get;
            private set;
        }

        /// <summary>
        /// 更新年月日
        /// </summary>
        public DateTime ModifiedTime
        {
            get;
            private set;
        }

        /// <summary>
        /// 概要
        /// </summary>
        public string Summary
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.SUMMARY );
            }

            set
            {
                Attributes.Set( TicketAttributes.SUMMARY, value );
            }
        }

        /// <summary>
        /// 詳細
        /// </summary>
        public string Description
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.DESCRIPTION );
            }

            set
            {
                Attributes.Set( TicketAttributes.DESCRIPTION, value );
            }
        }

        /// <summary>
        /// 分類
        /// </summary>
        public string Type
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.TYPE );
            }

            set
            {
                Attributes.Set( TicketAttributes.TYPE, value );
            }
        }

        /// <summary>
        /// 報告者
        /// </summary>
        public string Reporter
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.REPORTER );
            }

            set
            {
                Attributes.Set( TicketAttributes.REPORTER, value );
            }
        }

        /// <summary>
        /// 担当者
        /// </summary>
        public string Owner
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.OWNER );
            }

            set
            {
                Attributes.Set( TicketAttributes.OWNER, value );
            }
        }

        /// <summary>
        /// 関係者
        /// </summary>
        public string Cc
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.CC );
            }

            set
            {
                Attributes.Set( TicketAttributes.CC, value );
            }
        }

        /// <summary>
        /// 優先度
        /// </summary>
        public string Priority
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.PRIORITY );
            }

            set
            {
                Attributes.Set( TicketAttributes.PRIORITY, value );
            }
        }

        /// <summary>
        /// ステータス
        /// </summary>
        public string Status
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.STATUS );
            }

            set
            {
                Attributes.Set( TicketAttributes.STATUS, value );
            }
        }

        /// <summary>
        /// コンポーネント
        /// </summary>
        public string Component
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.COMPONENT );
            }

            set
            {
                Attributes.Set( TicketAttributes.COMPONENT, value );
            }
        }

        /// <summary>
        /// マイルストーン
        /// </summary>
        public string Milestone
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.MILESTONE );
            }

            set
            {
                Attributes.Set( TicketAttributes.MILESTONE, value );
            }
        }

        /// <summary>
        /// バージョン
        /// </summary>
        public string Version
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.VERSION );
            }

            set
            {
                Attributes.Set( TicketAttributes.VERSION, value );
            }
        }

        /// <summary>
        /// キーワード
        /// </summary>
        public string Keywords
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.KEYWORDS );
            }

            set
            {
                Attributes.Set( TicketAttributes.KEYWORDS, value );
            }
        }

        /// <summary>
        /// 解決方法
        /// </summary>
        public string Resolution
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.RESOLUTION );
            }

            set
            {
                Attributes.Set( TicketAttributes.RESOLUTION, value );
            }
        }

        /// <summary>
        /// カスタムフィールドの取得
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T GetField<T>( string key )
        {
            return Attributes.Get<T>( key );
        }

        /// <summary>
        /// カスタムフィールドの設定
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetCustomField<T>( string key, T value )
        {
            Attributes.Set( key, value );
        }

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Ticket()
        {
            Initialize();
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="id">取得するチケットID</param>
        public Ticket( int id )
        {
            Get( id );
        }

        /// <summary>
        /// 初期化
        /// </summary>
        private void Initialize()
        {
            ID = 0;
            Attributes.value = new XmlRpcStruct();
        }

        /// <summary>
        /// 指定されたチケットが遷移できるワークフローの状態を取得する
        /// </summary>
        /// <returns>遷移できる状態</returns>
        public string[] GetAvailableActions()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            return Trac.Rpc.ticketGetAvailableActions( ID );
        }

        /// <summary>
        /// 指定されたIDのチケットを取得
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public void Get( int id )
        {
            object[] ret = Trac.Rpc.ticketGet( id );
            Parse( ret );
        }

        /// <summary>
        /// チケットの作成
        /// </summary>
        public void Create()
        {
            if ( string.IsNullOrEmpty( Summary ) ) {
                throw new Exception( "概要が設定されていません" );
            }

            ID = Trac.Rpc.ticketCreate( Summary, Description, Attributes.value );
        }

        /// <summary>
        /// チケットの更新
        /// </summary>
        /// <param name="comment">更新コメント</param>
        public void Update( string comment )
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            object[] ret = Trac.Rpc.ticketUpdate( ID, comment, Attributes.value );
            Parse( ret );
        }

        /// <summary>
        /// チケットの削除
        /// </summary>
        public void Delete()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            Delete( ID );
            Initialize();
        }

        /// <summary>
        /// 指定されたチケットの更新履歴を取得する
        /// </summary>
        /// <returns>更新履歴</returns>
        public List<TicketChangeLog> ChangeLog()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            object[] ret = Trac.Rpc.ticketChangeLog( ID );
            List<TicketChangeLog> TicketChangeLogs = new List<TicketChangeLog>( ret.Length );
            foreach ( object log in ret ) {
                TicketChangeLogs.Add( new TicketChangeLog( log as object[] ) );
            }

            return TicketChangeLogs;
        }

        /// <summary>
        /// 添付ファイルリストを取得する
        /// </summary>
        /// <returns>添付ファイルリスト</returns>
        public TicketAttachment[] ListAttachments()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            object[] ret = Trac.Rpc.ticketListAttachments( ID );

            // データを展開
            TicketAttachment[] attachments = new TicketAttachment[ret.Length];
            for ( int i = 0; i < attachments.Length; ++i ) {
                attachments[i] = new TicketAttachment( this, ret[i] as object[] );
            }

            return attachments;
        }

        /// <summary>
        /// チケットのパース
        /// </summary>
        /// <param name="param">ticket.get,ticket.update の戻り値</param>
        private void Parse( object[] param )
        {
            ID = (int)param[0];
            CreateTime = (DateTime)param[1];
            ModifiedTime = (DateTime)param[2];
            Attributes.value = (XmlRpcStruct)param[3];
        }

        #region 静的メソッド
        /// <summary>
        /// 指定されたクエリのチケットIDを取得
        /// </summary>
        /// <param name="qstr"></param>
        /// <returns></returns>
        public static int[] Query( string qstr )
        {
            return Trac.Rpc.ticketQuery( qstr );
        }

        /// <summary>
        /// 指定日以降に変更されたチケットを取得する
        /// </summary>
        /// <param name="since"></param>
        /// <returns></returns>
        public static int[] GetRecentChanges( DateTime since )
        {
            return Trac.Rpc.ticketGetRecentChanges( since );
        }

        /// <summary>
        /// チケットの削除
        /// </summary>
        /// <param name="id">削除するID</param>
        public static void Delete( int id )
        {
            Trac.Rpc.ticketDelete( id );
        }

        /// <summary>
        /// チケットフィールドの取得
        /// </summary>
        /// <returns></returns>
        public static XmlRpcStruct[] GetTicketFields()
        {
            XmlRpcStruct[] ret = Trac.Rpc.ticketGetTicketFields();
            return ret;
        }
        #endregion
    }
}
