/*
 * Created by SharpDevelop.
 * User: conoves
 * Date: 12/2/2013
 * Time: 10:46 AM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB.Structure;

namespace ReinforcementNumbering
{
  [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
  [Autodesk.Revit.DB.Macros.AddInId("0344A5CF-DB1B-442E-828C-FF2D049AF4E2")]
  public partial class ThisApplication
  {
    private void Module_Startup(object sender, EventArgs e)
    {

    }

    private void Module_Shutdown(object sender, EventArgs e)
    {

    }

    class FabricAreaSelectionFilter : ISelectionFilter
    {
      public bool AllowElement(Element element)
      {
        return (element is FabricArea);
      }

      public bool AllowReference(Reference refer, XYZ point)
      {
        return false;
      }
    }

    class PartSelectionFilter : ISelectionFilter
    {
      public bool AllowElement(Element element)
      {
        return (element is Part);
      }

      public bool AllowReference(Reference refer, XYZ point)
      {
        return false;
      }
    }

    static public void CopyFabricAreaToNewPart2( UIDocument uiDoc )
    {
      Document doc = uiDoc.Document;

      Reference fAreaRef = uiDoc.Selection.PickObject(ObjectType.Element, new FabricAreaSelectionFilter(), "Pick a fabric area");

      FabricArea fArea = doc.GetElement(fAreaRef) as FabricArea;

      Reference partRef = uiDoc.Selection.PickObject(ObjectType.Element, new PartSelectionFilter(), "Pick a part");

      Part part = doc.GetElement(partRef) as Part;

      CopyFabricAreaToNewPart(fArea, part);
    }

    public void CopyFabricAreaToNewPart()
    {
      CopyFabricAreaToNewPart2( this.ActiveUIDocument );
    }

    static double GetElevationOfPart(Part part)
    {
      Document doc = part.Document;

      ElementId sourceElementId = part.GetSourceElementIds().First().HostElementId;

      Element hostParent = doc.GetElement(sourceElementId);

      if (hostParent is Part)
      {
        hostParent = doc.GetElement((hostParent as Part).GetSourceElementIds().First().HostElementId);
      }

      Level refLevel = doc.GetElement(hostParent.LevelId) as Level;

      double elevation = refLevel.Elevation;

      Parameter offset = hostParent.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM);

      elevation += offset.AsDouble();

      //double elevation = (hostParent.Location as LocationPoint).Point.Z;

      return elevation;
    }

    static String GetLevelNumberOfPart(Part part)
    {
      Document doc = part.Document;

      ElementId sourceElementId = part.GetSourceElementIds().First().HostElementId;

      Element hostParent = doc.GetElement(sourceElementId);

      if (hostParent is Part)
      {
        hostParent = doc.GetElement((hostParent as Part).GetSourceElementIds().First().HostElementId);
      }

      Level refLevel = doc.GetElement(hostParent.LevelId) as Level;

      String name = refLevel.Name;

      if (name.StartsWith("Level "))
      {
        return name.Substring(6);
      }
      return "ERROR";
    }

    static void CopyFabricAreaToNewPart(FabricArea fArea, Part part)
    {
      Document doc = part.Document;


      Element fSheet = doc.GetElement(fArea.GetFabricSheetElementIds().First());
      ElementId fSheetTypeId = fSheet.GetTypeId();
      Part currentHost = doc.GetElement(fArea.HostId) as Part;

      double currentElevation = GetElevationOfPart(currentHost);
      double newElevation = GetElevationOfPart(part);
      double deltaZ = newElevation - currentElevation;

      IList<CurveLoop> curveLoops = fArea.CopyCurveLoopsInSketch();


      // Current partition param
      String currentLevelNumber = GetLevelNumberOfPart(currentHost);
      String newLevelNumber = GetLevelNumberOfPart(part);

      int numberIncrement = Int32.Parse(newLevelNumber) - Int32.Parse(currentLevelNumber);

      Parameter partitionParam = fArea.get_Parameter(BuiltInParameter.NUMBER_PARTITION_PARAM);
      String partitonString = partitionParam.AsString();

      String newPartitionString = partitonString.Replace(currentLevelNumber, newLevelNumber);

      //List<CurveLoop> newCurveLoops = new List<CurveLoop>();
      //foreach (CurveLoop curveLoop in curveLoops)
      //{
      //	newCurveLoops.Add(CurveLoop.CreateViaOffset(curveLoop, deltaZ, XYZ.BasisZ));
      //}

      XYZ newDirectionOrigin = fArea.DirectionOrigin + deltaZ * XYZ.BasisZ;

      using (Transaction t = new Transaction(doc, "Create new FabricArea"))
      {
        t.Start();
        FabricArea newFabricArea = FabricArea.Create(doc, part, curveLoops, fArea.Direction, newDirectionOrigin, fArea.GetTypeId(), fSheetTypeId);

        doc.Regenerate();

        // Make it a new partition

        Parameter newPartitionParam = newFabricArea.get_Parameter(BuiltInParameter.NUMBER_PARTITION_PARAM);
        newPartitionParam.Set(newPartitionString);

        doc.Regenerate();

        NumberingSchema schema =
          NumberingSchema.GetNumberingSchema(doc, NumberingSchemaTypes.StructuralNumberingSchemas.ReinforcementFabric);

        int startingNumber = schema.GetNumbers(partitonString).First().Low;

        int newStartingNumber = startingNumber + 100 * numberIncrement;
        t.Commit();

        t.Start();

        schema.ShiftNumbers(newPartitionString, newStartingNumber);

        t.Commit();
      }
    }

    #region Revit Macros generated code
    private void InternalStartup()
    {
      this.Startup += new System.EventHandler(Module_Startup);
      this.Shutdown += new System.EventHandler(Module_Shutdown);
    }
    #endregion
  }

  [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
  class Command : IExternalCommand
  {
    public Result Execute(
      ExternalCommandData commandData, 
      ref string message, 
      ElementSet elements)
    {
      ThisApplication.CopyFabricAreaToNewPart2(commandData.Application.ActiveUIDocument);
      return Result.Succeeded;
    }
  }
}