using System;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using TracPluginTemplateMaker.CodeGenerator;

namespace TracPluginTemplateMaker
{
    /// <summary>
    /// プラグインテンプレート作成
    /// </summary>
    public class PluginTemplateMaker
    {
        public enum GenerateInterface {
            IAdminPanelProvider,
            IAuthenticator,
            IEnvironmentSetupParticipant,
            INavigationContributor,
            IRequestHandler,
            IRequestFilter,
            ITemplateProvider,
            ITemplateStreamFilter,
            ITicketManipulator,
            ITicketChangeListener,
            ITicketActionController,
            ITicketReportRenderer,
            IPermissionRequestor,
            IPermissionGroupProvider,
            IPermissionStore,
            IPermissionPolicy,
            ITimelineEventProvider,
            IContentConverter,
            IHTMLPreviewRenderer,
            IHTMLPreviewAnnotator,
            IWikiChangeListener,
            IWikiPageManipulator,
            IWikiMacroProvider,
            IWikiSyntaxProvider,
        }

        string title = string.Empty;
        string name = string.Empty;

        string outputDirectory = string.Empty;
        string rootDirectory = string.Empty;
        string pluginDirectory = string.Empty;

        const string templateDir = ".\\template\\";

        PluginInfo pluginInfo;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="title"></param>
        /// <param name="outputDirectory"></param>
        public PluginTemplateMaker( string title, string outputDirectory )
        {
            this.title = title;
            name = this.title + "Plugin";

            this.outputDirectory = outputDirectory;
            rootDirectory = this.outputDirectory + "\\" + name;
            pluginDirectory = rootDirectory + "\\" + title;
        }

        /// <summary>
        /// プラグインテンプレートの作成
        /// </summary>
        public void Make( PluginInfo pluginInfo, IEnumerable< GenerateInterface > plugins )
        {
            if ( !plugins.Any() ) {
                throw new ArgumentException( "生成するプラグインが指定されていません" );
            }

            this.pluginInfo = pluginInfo;

            CreateRootDirectory();
            CreateSetupFile();
            CreatePluginDirectory();
            CretaeInitFile();
            CreatePluginFile( plugins );
        }

        /// <summary>
        /// ロートディレクトリの作成
        /// </summary>
        private void CreateRootDirectory()
        {
            Directory.CreateDirectory( rootDirectory );
        }

        /// <summary>
        /// setup.py の作成
        /// </summary>
        private void CreateSetupFile()
        {
            using ( StreamWriter setup = new StreamWriter( rootDirectory + "\\setup.py" ) ) {
                using ( FileStream setupTemplate = File.Open( templateDir + "setup.py.template", FileMode.Open ) ){
                    // テンプレートコードの読み込み
                    byte[] template = new byte[setupTemplate.Length];
                    setupTemplate.Read( template, 0, template.Length );

                    // 文字列に変換 → フォーマット
                    string format = Encoding.UTF8.GetString( template, 0, template.Length );
                    string code = string.Format( format, name, title,
                        pluginInfo.Version, pluginInfo.Author, pluginInfo.Email,
                        pluginInfo.Description, pluginInfo.License, pluginInfo.Url );

                    // 実際のソースに書き込み
                    setup.Write( code );
                }
            }
        }

        /// <summary>
        /// プラグインディレクトリの作成
        /// </summary>
        private void CreatePluginDirectory()
        {
            Directory.CreateDirectory( pluginDirectory );
        }

        /// <summary>
        /// __init__.py の作成
        /// </summary>
        private void CretaeInitFile()
        {
            using ( StreamWriter init = new StreamWriter( pluginDirectory + "\\__init__.py" ) ) {
                using ( FileStream initTemplate = File.Open( templateDir + "__init__.py.template", FileMode.Open ) ) {
                    // テンプレートコードの読み込み
                    byte[] template = new byte[initTemplate.Length];
                    initTemplate.Read( template, 0, template.Length );

                    // 文字列に変換 → フォーマット
                    string format = Encoding.UTF8.GetString( template, 0, template.Length );

                    // 実際のソースに書き込み
                    init.Write( string.Format( format, title ) );
                }
            }
        }

        /// <summary>
        /// プラグインファイルの作成
        /// </summary>
        private void CreatePluginFile( IEnumerable<GenerateInterface> plugins )
        {
            CodeGeneratorEnvironment.TemplateDirectory = templateDir;

            // コード生成
            PluginFrame frame = new PluginFrame( GetGenerator( plugins ) );
            string code = frame.GetImportCode();
            code += frame.GetDefinationCode();
            code += frame.GetImplementCode();

            // プラグイン名でフォーマットする
            code = string.Format( code, name, title );

            using ( StreamWriter plugin = new StreamWriter( pluginDirectory + "\\" + title + ".py" ) ) {
                // 実際のソースに書き込み
                plugin.Write( code );
            }
        }

        /// <summary>
        /// ジェネレータを取得する
        /// </summary>
        /// <param name="plugins"></param>
        /// <returns></returns>
        private IEnumerable<ICodeGenerator> GetGenerator( IEnumerable<GenerateInterface> plugins )
        {
            foreach ( GenerateInterface plugin in plugins ) {
                yield return CreateCodeGenerator( plugin );
            }
        }

        /// <summary>
        /// 作成するプラグインのジェネレータを生成する
        /// </summary>
        /// <param name="plugin"></param>
        /// <returns></returns>
        private ICodeGenerator CreateCodeGenerator( GenerateInterface plugin ) 
        {
            switch ( plugin ) {
            case GenerateInterface.IAdminPanelProvider:
                return new AdminPanelProvider();
            case GenerateInterface.IAuthenticator:
                return new Authenticator();
            case GenerateInterface.INavigationContributor:
                return new NavigationContributor();
            case GenerateInterface.IRequestHandler:
                return new RequestHandler();
            case GenerateInterface.IRequestFilter:
                return new RequestFilter();
            case GenerateInterface.ITemplateProvider:
                return new TemplateProvider();
            case GenerateInterface.ITemplateStreamFilter:
                return new TemplateStreamFilter();
            case GenerateInterface.ITicketManipulator:
                return new TicketManipulator();
            case GenerateInterface.ITicketActionController:
                return new TicketActionController();
            case GenerateInterface.ITicketChangeListener:
                return new TicketChangeListener();
            case GenerateInterface.ITicketReportRenderer:
                return new TicketReportRenderer();
            case GenerateInterface.IEnvironmentSetupParticipant:
                return new EnvironmentSetupParticipant();
            case GenerateInterface.IPermissionRequestor:
                return new PermissionRequestor();
            case GenerateInterface.IPermissionGroupProvider:
                return new PermissionGroupProvider();
            case GenerateInterface.IPermissionStore:
                return new PermissionStore();
            case GenerateInterface.IPermissionPolicy:
                return new PermissionPolicy();
            case GenerateInterface.ITimelineEventProvider:
                return new TimelineEventProvider();
            case GenerateInterface.IContentConverter:
                return new ContentConverter();
            case GenerateInterface.IHTMLPreviewRenderer:
                return new HTMLPreviewRenderer();
            case GenerateInterface.IHTMLPreviewAnnotator:
                return new HTMLPreviewAnnotator();
            case GenerateInterface.IWikiChangeListener:
                return new WikiChangeListener();
            case GenerateInterface.IWikiPageManipulator:
                return new WikiPageManipulator();
            case GenerateInterface.IWikiMacroProvider:
                return new WikiMacroProvider();
            case GenerateInterface.IWikiSyntaxProvider:
                return new WikiSyntaxProvider();
            }

            throw new ArgumentException();
        }
    }
}
