using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using System.Linq;


using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI.Selection;


namespace QuickRevisionCloud
{
    [TransactionAttribute(TransactionMode.Manual)]
    public class RevisionCloudSetting : IExternalCommand
    {
        
        public Result Execute(ExternalCommandData commandData,
          ref string messages, ElementSet elements)
        {
            UIApplication app = commandData.Application;
            Document doc = app.ActiveUIDocument.Document;


            FormRevisionSetting form = new FormRevisionSetting();
            form.ShowDialog();


            return Result.Succeeded;
        }
    }

    [TransactionAttribute(TransactionMode.Manual)]
    public class CreateRevisionCloud : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData,
          ref string messages, ElementSet elements)
        {
            UIApplication app = commandData.Application;
            Document doc = app.ActiveUIDocument.Document;
            Autodesk.Revit.DB.View view = app.ActiveUIDocument.ActiveGraphicalView;

            //get this element's bounding box in the current view.
            if (!(view is ViewPlan))
            {
                messages = "This command can only run when active view is plan view";
                return Result.Failed;
            }

            bool repeat = true;
            while (repeat)
            {
                Selection sel = app.ActiveUIDocument.Selection;
                Element elem = null;
                try
                {
                    Reference ref1 = sel.PickObject(ObjectType.Element, "pick an element");
                    elem = doc.GetElement(ref1);

                }
                catch (Exception)
                {                 
                    return Result.Succeeded;
                }

                IList<Curve> cloudBoundary = new List<Curve>();
                double ext = 2;  //extent the cloud revision boundary, based on the element's bounding box.
                XYZ tagPosition;

                Transaction trans = new Transaction(doc);
                trans.Start("Revision");


                if (elem is Room)
                {
                    //Get the bounding of the room.
                    Room r = elem as Room;
                    SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
                    options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.CoreCenter;

                    IList<IList<Autodesk.Revit.DB.BoundarySegment>> boundary = r.GetBoundarySegments(options);
                    IList<Autodesk.Revit.DB.BoundarySegment> loop = boundary[0]; //suppose only one loop.
                    CurveLoop initialCurveLoop = new CurveLoop();
                    foreach (Autodesk.Revit.DB.BoundarySegment segment in loop)
                    {
                        initialCurveLoop.Append(segment.Curve);
                    }

                    CurveLoop finalCurveLoop = initialCurveLoop;
                    if (initialCurveLoop.IsCounterclockwise(view.ViewDirection))
                    {
                        finalCurveLoop = new CurveLoop();
                        for (int i = loop.Count - 1; i >= 0; i-- )
                        {
                            Curve curve = initialCurveLoop.ElementAt(i);
                            finalCurveLoop.Append(curve.CreateReversed());
                        }
                    }

                    // Offset by 1 ft
                    finalCurveLoop = CurveLoop.CreateViaOffset(
                      finalCurveLoop, -1, view.ViewDirection);

                    cloudBoundary = finalCurveLoop.ToList<Curve>();

                    tagPosition = (r.Location as LocationPoint).Point;

                }
                else
                {

                    BoundingBoxXYZ bounding = elem.get_BoundingBox(doc.ActiveView);

                    
                    XYZ ptMax = bounding.Max + new XYZ(ext, ext, 0);
                    XYZ ptMin = bounding.Min - new XYZ(ext, ext, 0);


                    double z = 0; //project to the XOY plane
                    XYZ pt1 = new XYZ(ptMax.X, ptMax.Y, z);
                    XYZ pt2 = new XYZ(ptMax.X, ptMin.Y, z);
                    XYZ pt3 = new XYZ(ptMin.X, ptMin.Y, z);
                    XYZ pt4 = new XYZ(ptMin.X, ptMax.Y, z);

                    tagPosition = (pt1 + pt3) * 0.5;

                    Line line1 = Line.CreateBound(pt1, pt2);
                    Line line2 = Line.CreateBound(pt2, pt3);
                    Line line3 = Line.CreateBound(pt3, pt4);
                    Line line4 = Line.CreateBound(pt4, pt1);

                    //Attention, the cloud boundary segement should be clockwise, otherwise, Revit will crash.
                   
                    cloudBoundary.Add(line1);
                    cloudBoundary.Add(line2);
                    cloudBoundary.Add(line3);
                    cloudBoundary.Add(line4);
                }

                //create a revision.        
            
                Revision rev = Revision.Create(doc);


                rev.NumberType = FormRevisionSetting.numbericType;
                rev.RevisionDate = DateTime.Now.ToLongDateString();
                rev.Description = "This " + elem.Category.Name.ToString() + FormRevisionSetting.errorDescription;
                rev.IssuedBy = FormRevisionSetting.issuedBy;
                rev.RevisionDate = DateTime.Now.ToLongDateString();               
                ElementId id = rev.Id;
                trans.Commit();

                //insert in the current view.

                Transaction trans2 = null;
                try
                {

                    trans2 = new Transaction(doc);
                    trans2.Start("create cloud");

                    RevisionCloud revisionCloud = RevisionCloud.Create(doc, doc.ActiveView, id, cloudBoundary);
                    //check if there is a tag family loaded in current project.

                    FilteredElementCollector collector = new FilteredElementCollector(doc);
                    collector.OfClass(typeof(FamilySymbol)).OfCategory(BuiltInCategory.OST_RevisionCloudTags);
                    if (collector.ToElementIds().Count > 0)
                    {
                        IndependentTag tag = doc.Create.NewTag(doc.ActiveView, revisionCloud, false, TagMode.TM_ADDBY_CATEGORY, TagOrientation.Horizontal, tagPosition);
                    }

                    trans2.Commit();


                }
                catch (Exception ex)
                {
                    TaskDialog.Show("error", ex.Message);
                    trans2.RollBack();
                }
            }

      

            return Result.Succeeded;
        }
    }

    public class OTWindowHandle : System.Windows.Forms.IWin32Window
    {
        IntPtr _hwnd;

        public OTWindowHandle(IntPtr h)
        {

            _hwnd = h;
        }

        public IntPtr Handle
        {
            get
            {
                return _hwnd;
            }
        }
    }
}
