using System;
using System.Collections;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.IO;
using System.Net;
using System.Web;
using System.Windows.Forms;
using Microsoft.VisualStudio.SourceSafe.Interop;
using TracXmlRpc;

namespace VSSLog2Trac
{
    /// <summary>
    /// VSSとTracを連携させるプラグインです。
    /// </summary>
    /// <remarks>
    /// VSSへのコミットイベント処理内で、Tracに対してXML-RPC経由で操作することで、
    /// VSSへのコミットログとTracのチケットを連携します。
    /// </remarks>
    /// <author>
    /// R.Sato, H.Nagashima
    /// </author>
    [ProgId("VSSLog2Trac")]
    [Guid("E1916BD8-A8AA-47b7-A862-C1AF995E64EF")]
    [ComVisible(true)]
    public class VSSLog2Trac : IVSSEventHandler, IVSSEvents 
    {
        private VSSApp vssApp;
        IConnectionPoint vssEvents;
        int cookie;

        public VSSLog2Trac() 
        {
        }

        ~VSSLog2Trac() 
        {
            // Unwire the connection point (see Init)
            try
            {
                vssEvents.Unadvise(cookie);
            }
            catch (Exception)
            {
            }
        }

        public void RegisterTicket(string vssAction, string vssComment, TracInfo tracInfo, VSSItem vssItem)
        {
            string loginUrl = tracInfo.URL;

            // 資格情報を作成
            NetworkCredential myCred = new NetworkCredential(tracInfo.UserId, tracInfo.Password);

            ITicket ticket = new TicketProxy();
            ticket.Url = loginUrl;
            ticket.Credentials = myCred;
            ticket.Proxy = WebProxy.GetDefaultProxy();

            object[] result = null;
            string vssFile = vssItem.Spec;
            string vssVersion = vssItem.VersionNumber.ToString();
            string comment = vssComment
                + Environment.NewLine
                + Environment.NewLine
                + vssAction + ":" + vssFile + ":" + vssVersion;

            try
            {
                result = ticket.Update(tracInfo.TicketNo, comment);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message + e.StackTrace);
                return;
            }
        }

        public void Init(VSSApp app)
        {
            // by saving the VSSApp pointer you can drive the database during events
            this.vssApp = app;

            // Wire up the COM connection point manually
            IConnectionPointContainer cpc = (IConnectionPointContainer)app;
            Guid guid = typeof(IVSSEvents).GUID;
            cpc.FindConnectionPoint(ref guid, out vssEvents);
            vssEvents.Advise(this, out cookie);
        }

        /// <summary>
        /// イベントが発生する度に呼ばれるイベントハンドラです。
        /// </summary>
        /// <param name="item">VSSのイベント情報。</param>
        /// <param name="vssAction">アクション。</param>
        /// <param name="vssComment">コメント。</param>
        public void AfterEvent(VSSItem vssItem, string vssAction, string vssComment)
        {
            // 環境変数からTrac接続文字列を取得する。
            string tracConnection = System.Environment.GetEnvironmentVariable("TRAC");

            if (tracConnection == null)
            {
                return;
            }

            string[] tracVariables = tracConnection.Split(';');

            if (tracVariables == null
                || tracVariables.Length != 3)
            {
                MessageBox.Show("TRAC環境変数の形式が不正です。\n TRAC = (TracURL);(ID);(パスワード)\n例 http://10.68.0.161/trac;rsato;rsato\nの形式で設定してください。");
                return;
            }

            // 接続情報を作成。
            TracInfo tracInfo = new TracInfo();
            tracInfo.URL = "" + tracVariables[0];
            tracInfo.UserId = "" + tracVariables[1];
            tracInfo.Password = "" + tracVariables[2];

            // チケット番号をvssCommentから取る。
            string ticketNo = "";
            if(vssComment == null)
            {
                vssComment = "";
            }
            if( vssComment[0].Equals('#') && vssComment.IndexOf('\n') > -1)
            {
                ticketNo = vssComment.Substring(1, vssComment.IndexOf('\n')-1);
            }

            int parse = 0;
            if (int.TryParse(ticketNo, out parse))
            {
                tracInfo.TicketNo = parse;
                vssComment = vssComment.Substring(vssComment.IndexOf('\n') + 1);
                　
                // バージョン情報を取得。
                VSSVersion vssVersion = vssItem.VSSVersion;

                try
                {
                    RegisterTicket(vssAction, vssComment, tracInfo, vssItem);
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message + e.StackTrace);
                }
                
            }
        }


        #region AfterEvent

        public void AfterAdd(VSSItem vssItem, string localSpec, string comment) 
        { 
            AfterEvent(vssItem, "Add", comment);
        }

        public void AfterBranch(VSSItem vssItem, string comment) 
        {
            AfterEvent(vssItem, "Branch", comment);
        }

        public void AfterCheckin(VSSItem vssItem, string localSpec, string comment) 
        {
            AfterEvent(vssItem, "Checkin", comment);
        }

        public void AfterCheckout(VSSItem vssItem, string localSpec, string comment) 
        {
            // AfterEvent(vssItem, "Checkout", comment);
        }

        public void AfterEvent(int eventNum, VSSItem vssItem, string str, object var) 
        {
            // Use of this event is deprecated
        }

        public void AfterRename(VSSItem vssItem, string oldName) 
        {
            AfterEvent(vssItem, "Rename", "from="+oldName);
        }

        public void AfterUndoCheckout(VSSItem vssItem, string localSpec) 
        {
            // AfterEvent(vssItem, "UndoCheckout", "");
        }
        #endregion


        #region BeforeEvent

        public bool BeforeAdd(VSSItem vssItem, string localSpec, string comment) 
        {
            return true;
        }

        public bool BeforeBranch(VSSItem vssItem, string comment) 
        {
            return true;
        }

        public bool BeforeCheckin(VSSItem vssItem, string localSpec, string comment) 
        {
            return true;
        }

        public bool BeforeCheckout(VSSItem vssItem, string localSpec, string comment) 
        {
            return true;
        }

        public bool BeforeEvent(int eventNum, VSSItem vssItem, string str, object var) 
        {
            return true;
        }

        public bool BeforeRename(VSSItem vssItem, string oldName) 
        {
            return true;
        }

        public bool BeforeUndoCheckout(VSSItem vssItem, string localSpec) 
        {
            return true;
        }

        public bool BeginCommand(int unused) 
        {
            // use of this event is deprecated
            return true;
        }

        public void EndCommand(int unused) 
        {
            // Use of this event is deprecated
        }

        #endregion
    }
}
