﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string cpath = ".";     //カレントディレクトリのパス格納
            if (args.Length == 1) { cpath = args[0]; }

            // 引数チェック
            // 該当ディレクトリが存在しない時は終了
            if (!Directory.Exists(@cpath))
            {
                Console.WriteLine("'" + cpath + "' のディレクトリは存在しません。");
                Environment.Exit(1);
            } 

            // メソッドの再帰呼び出しによる削除（※第一版）
            //Delete(cpath);

            // LINQによる列挙順での削除（※第二版）
            // 引数で与えられたディレクトリとそのサブディレクトリにあるディレクトリ一覧を取得
            // 階層の深い順に並べ替え（降順）
            IEnumerable<string> directries = Enumerable.Empty<string>();
            try
            {
                directries = SafeEnumerateFilesInAllDirectories(@cpath).OrderByDescending(pathname => CountKeyword(pathname, '\\'));
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine(ex.Message);
            }

            // 最も深い階層から削除処理を行う
            foreach (string dir in directries)
            {
                RemoveDirectory(dir);
            }

            //削除漏れ追加
            RemoveDirectory(cpath);
        }

        public static IEnumerable<string> SafeEnumerateFilesInAllDirectories(string path)
        {
            return SafeEnumerateFilesInAllDirectories(path, "*");
        }

        private static string errpath = "";
        public static IEnumerable<string> SafeEnumerateFilesInAllDirectories(string path, string searchPattern)
        {
            var dirs = Enumerable.Empty<string>();
            try
            {
                dirs = Directory.EnumerateDirectories(path, searchPattern);
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("'" + path + "' へのアクセスが拒否されました。");
                errpath = path;
            }
            try
            {
                dirs = Directory.EnumerateDirectories(path)
                    .Aggregate<string, IEnumerable<string>>(
                        dirs,
                        (a, v) => a.Union(SafeEnumerateFilesInAllDirectories(v, searchPattern))
                    );
            }
            catch (UnauthorizedAccessException)
            {
                // 重複で検出されるだけなのでエラーはスルー
                //if (errpath != path)
                //    Console.WriteLine("'" + path + "' へのアクセスが拒否されました。②");
            }

            return dirs;
        }


        /// <summary>
        /// C#で文字列内の特定文字の出現回数を数える方法
        /// </summary>
        /// <param name="str"></param>
        /// <param name="ch"></param>
        /// <returns></returns>
        private static int CountKeyword(string str, char ch)
        {
            return str.Length - str.Replace(ch.ToString(), "").Length;
        }

        /// <summary>
        /// 指定した空ディレクトリのみを削除
        /// </summary>
        /// <param name="dirpath"></param>
        private static void RemoveDirectory(string dirpath)
        {
            try
            {
                Directory.Delete(dirpath, false);
                Console.WriteLine("'" + dirpath + "'" + " を削除しました。");
            }
            catch (IOException)
            {
                Console.WriteLine("'" + dirpath + "' ディレクトリが空ではありません。");
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// 指定したディレクトリとその中身を全て削除する
        /// </summary>
        public static void Delete(string targetDirectoryPath)
        {
            if (!System.IO.Directory.Exists(targetDirectoryPath))
            {
                return;
            }

            string[] directoryPaths = new string[0];
            try
            {
                //※ディレクトリの中のディレクトリも再帰的に削除
                directoryPaths = Directory.GetDirectories(targetDirectoryPath);
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine(targetDirectoryPath);
                Console.WriteLine(ex.Message);
            }

            foreach (string directoryPath in directoryPaths)
            {
                Delete(directoryPath);
            }

            //空ディレクトリのみ削除
            try
            {
                Directory.Delete(targetDirectoryPath, false);
            }
            catch (IOException ex)
            {
                Console.WriteLine(targetDirectoryPath);
                Console.WriteLine(ex.Message);
            }
        }
    }
}
