using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using System.Xml.Linq;
using System.Xml.Serialization;
using Autodesk.Revit.DB;

namespace Revit.Addin.ScheduleImages
{
   class Utils
   {
      public static BitmapSource convertFromBitmap(System.Drawing.Bitmap bitmap)
      {
         return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
             bitmap.GetHbitmap(),
             IntPtr.Zero,
             Int32Rect.Empty,
             BitmapSizeOptions.FromEmptyOptions());
      }

      private static bool ShareParameterExists(Document revitDoc, BuiltInCategory biCat, String paramName)
      {
         BindingMap bindingMap = revitDoc.ParameterBindings;
         DefinitionBindingMapIterator iter = bindingMap.ForwardIterator();
         iter.Reset();

         while (iter.MoveNext())
         {
            Definition tempDefinition = iter.Key;

            // find the definition of which the name is the appointed one
            if (String.Compare(tempDefinition.Name, paramName) != 0)
            {
               continue;
            }

            // get the category which is bound
            ElementBinding binding = bindingMap.get_Item(tempDefinition) as ElementBinding;
            CategorySet bindCategories = binding.Categories;
            foreach (Category category in bindCategories)
            {
               if (category.Name
                   == revitDoc.Settings.Categories.get_Item(biCat).Name)
               {
                  // the definition with appointed name was bound to Walls, return true
                  return true;
               }
            }
         }
         // 
         // return false if shared parameter doesn't exist
         return false;
      }

      public static bool CreateSharedParam(Document revitDoc, BuiltInCategory biCat, string paramName, bool isInstance)
      {
         try
         {
            // check whether shared parameter exists
            if (ShareParameterExists(revitDoc, biCat, paramName))
            {
               return false;
            }

            // create shared parameter file
            String paramFile = Path.Combine(AppConfig.AddonDirectory, "PS_Shared_Params.txt");
            if (File.Exists(paramFile))
            {
               File.Delete(paramFile);
            }
            FileStream fs = File.Create(paramFile);
            fs.Close();

            // prepare shared parameter file
            Autodesk.Revit.ApplicationServices.Application revitApp = revitDoc.Application;
            revitApp.SharedParametersFilename = paramFile;

            // open shared parameter file
            DefinitionFile parafile = revitApp.OpenSharedParameterFile();

            // create a group
            DefinitionGroup apiGroup = parafile.Groups.Create("PS_Shared_ParamGroups");

            // create a visible "External Wall ID" of text type.
            ExternalDefinitonCreationOptions options = new ExternalDefinitonCreationOptions(paramName, ParameterType.Image);

            Definition wallSharedParamDef = apiGroup.Definitions.Create(options);
            // get Walls category
            Category wallCat = revitDoc.Settings.Categories.get_Item(biCat);
            CategorySet categories = revitDoc.Application.Create.NewCategorySet();
            categories.Insert(wallCat);

            // insert the new parameter
            Autodesk.Revit.DB.Binding paramBinding = null;
            if (isInstance)
               paramBinding = revitApp.Create.NewInstanceBinding(categories);
            else
               paramBinding = revitApp.Create.NewTypeBinding(categories);
            revitDoc.ParameterBindings.Insert(wallSharedParamDef, paramBinding);
            return true;
         }
         catch (Exception ex)
         {
            throw new Exception("Failed to create shared parameter: " + ex.Message);
         }
      }

      public static void ReloadImages(Document activeDoc)
      {
         FilteredElementCollector coll = new FilteredElementCollector(activeDoc);
         IList<Element> imageSyms = coll.OfClass(typeof(ImageType)).ToElements();

         Transaction tran = new Transaction(activeDoc);
         tran.Start("Reload Images");
         try
         {
            int failFiles = 0;
            foreach (ImageType imgSym in imageSyms)
            {
               if (!File.Exists(imgSym.Path))
               {
                  failFiles++;
                  continue;
               }
               imgSym.Reload();
            }
            tran.Commit();
            string msg = string.Format("Succeed to reload {0} images.", imageSyms.Count);
            if(0 != failFiles)
               msg = string.Format("Succeed to reload {0} images; {1} image files not found.", imageSyms.Count - failFiles, failFiles);
            MessageBox.Show(msg, "Reload", MessageBoxButtons.OK, MessageBoxIcon.Information);
         }
         catch (System.Exception ex)
         {
            MessageBox.Show("Sorry, exception occurs -->" + ex.Message);
         }
      }
   }

   [Serializable]
   //[XmlRoot("Settings")]
//   public class Config
//   {
//      /// <summary>
//      /// The config file.
//      /// </summary>
//      private static string ConfigFile = Path.Combine(AppConfig.AddonDirectory, "ScheuleImages.config");

//      //[XmlElement("AutoReloadImage")]
//      public static bool AutoReloadImage { get; set; }

//      /// <summary>
//      /// Indicates whether the open/close pairs will be redrawn on every accessing for current value.
//      /// </summary>
//      //[XmlElement("RealtimeUpdateImage")]
//      public static bool RealtimeUpdateImage { get; set; }

//      /// <summary>
//      /// Loads the config setting from serialized data. 
//      /// </summary>
//      /// <returns></returns>
//      public static Config Load()
//      {
//         if (!File.Exists(ConfigFile))
//            return new Config();
//         //
//         // Deserialize
//         try
//         {
//               Stream st = File.Open(ConfigFile, FileMode.Open);
//#if(ENABLE_XML_SERIALIZABLE)
//                XmlSerializer xs = new XmlSerializer(typeof(Config));
//                object ojb = xs.Deserialize(st);
//#else
//               BinaryFormatter bf = new BinaryFormatter();
//               object ojb = bf.Deserialize(st);
//#endif
//               st.Close();
//               //
//               // valid config?
//               Config config = ojb as Revit.Addin.ScheduleImages.Config;
//               if (null == config)
//                  Trace.WriteLine("The deserialized config file doesn't contain well-formed config data!");
//               else
//                  return config;
//         }
//         catch (System.Exception ex)
//         {
//            Trace.WriteLine("Failed to load config, ex: " + ex.ToString());
//         }
//         Trace.WriteLine("End to load config");
//         return new Config();
//      }

//      /// <summary>
//      /// Saves the config setting to serialize file.
//      /// </summary>
//      /// <returns></returns>
//      public bool Save()
//      {
//         try
//         {
//            if (File.Exists(ConfigFile))
//               File.Delete(ConfigFile);
//            //
//            // serialize the data
//            string dir = Path.GetDirectoryName(ConfigFile);
//            if (!Directory.Exists(dir))
//               Directory.CreateDirectory(dir);
//            Stream st = File.Open(ConfigFile, FileMode.Create);
//#if(ENABLE_XML_SERIALIZABLE)
//                XmlSerializer xs = new XmlSerializer(this.GetType());
//                xs.Serialize(st, this);
//#else
//            BinaryFormatter bf = new BinaryFormatter();
//            bf.Serialize(st, this);
//#endif
//            st.Close();
//            return true;
//         }
//         catch (System.Exception ex)
//         {
//            Trace.WriteLine("Failed to save config, ex: " + ex.ToString());
//            return false;
//         }
//      }

//      /// <summary>
//      /// Default ctor with default values.
//      /// </summary>
//      private Config()
//      {
//         AutoReloadImage = false;
//         RealtimeUpdateImage = false;
//      }
//   }
//    }

    public sealed class Config
    {
      public bool AutoReloadImage { get; set; }

      public bool RealtimeUpdateImage { get; set; }

      public bool IsValid { get; set; }

      private static string m_settingFile = Path.Combine(AppConfig.AddonDirectory, "MySetting.xml");

      public Config()
      {
         AutoReloadImage = false;
         RealtimeUpdateImage = false;
      }

      public static Config Load()
      {
         Config config = new Config();
         config.IsValid = File.Exists(m_settingFile);
         if (config.IsValid)
         {
            // open xml file and find the root setting node
            XDocument saved = XDocument.Load(m_settingFile);
            IEnumerable<XElement> rootNodes = saved.Elements();
            XElement settingNode = null;
            foreach (XElement subNode in rootNodes)
            {
               if (subNode.Name == "Settings")
               {
                  settingNode = subNode;
                  break;
               }
            }
            if (settingNode == null)
               return config;
            //
            // parse setting node and get the layer and elevation settings 
            IEnumerable<XElement> nodes = settingNode.Elements();
            foreach (XElement node in nodes)
            {
               if (node.Name == "AutoReloadImage")
               {
                  config.AutoReloadImage = bool.Parse(node.Value);
               }
               else if (node.Name == "RealtimeUpdateImage")
               {
                  config.RealtimeUpdateImage = bool.Parse(node.Value);
               }
            }
         }
         return config;
      }

      public bool Save()
      {
         XDocument config = new XDocument(
            new XComment("Image Schedule setting file, created on " + DateTime.Now.ToString()),
            new XElement("Settings",
               new XElement("AutoReloadImage", AutoReloadImage),
               new XElement("RealtimeUpdateImage", RealtimeUpdateImage))
               );
         try
         {
            config.Save(m_settingFile);
            return true;
         }
         catch (Exception ex)
         {
            Trace.WriteLine("Failed to save, ex: " + ex.ToString());
            return false;
         }
      }
    }
}
