using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
 
// This line is not mandatory, but improves loading performances
[assembly: CommandClass(typeof(CloneToLayout.Utils))]
 
namespace CloneToLayout
{
 
  public class Utils
  {
    [CommandMethod("CloneToLayout")]
    public void CloneToLayout()
    {
      Document doc = Application.DocumentManager.MdiActiveDocument;
      if (doc == null) return;
      Editor ed = doc.Editor;
      Database db = doc.Database;
      PromptSelectionResult rs = ed.GetSelection();
      if (rs.Status != PromptStatus.OK) return;
      ObjectIdCollection ids = new ObjectIdCollection(rs.Value.GetObjectIds());
      testClone(ref ids);
    }
 
    public static void testClone(ref ObjectIdCollection ids)
    {
      Document doc = Application.DocumentManager.MdiActiveDocument;
      Editor ed = doc.Editor;
      Database db = doc.Database;
      Extents3d ext = new Extents3d();
      using (Transaction tr = doc.TransactionManager.StartTransaction())
      {
        foreach (ObjectId id in ids)
        {
          Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
          if (ent != null) {
            ext.AddExtents(ent.GeometricExtents);
          }
        }
 
        // Центральная точка габаритов выбранных примитивов.
        Point3d modelCenter = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5;
        double modelHeight = ext.MaxPoint.Y - ext.MinPoint.Y;
        double modelWidth  = ext.MaxPoint.X - ext.MinPoint.X;
 
        LayoutManager layman = LayoutManager.Current;
 
        DBDictionary layoutDic = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
 
        double mScale = 1;
 
        // Проходимся по всем листам (модель пропускаем)
        foreach (DBDictionaryEntry entry in layoutDic)
        {
          Layout layout = tr.GetObject(entry.Value, OpenMode.ForRead) as Layout;
 
          if (!layout.ModelType) // Это не модель
          {
            Extents3d extLayout = new Extents3d();
            BlockTableRecord btrLayout = tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead) as BlockTableRecord;
            // Находим габариты примитивов в листе - будем считать их рамкой листа.
            foreach (ObjectId id in btrLayout)
            {
              Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
              if (ent != null && !(ent is Viewport))
              {
                extLayout.AddExtents(ent.GeometricExtents);
              }
            }
            // Центральная точка габаритов выбранных примитивов в листе
            Point3d layoutCenter = extLayout.MinPoint + (extLayout.MaxPoint - extLayout.MinPoint) * 0.5;
            double layoutHeight = extLayout.MaxPoint.Y - extLayout.MinPoint.Y;
            double layoutWidth = extLayout.MaxPoint.X - extLayout.MinPoint.X;
 
            // Находим масштабный коэффициент.
            mScale = layoutWidth / modelWidth;
            // Если масштабированная высота модели превышает высоту листа,
            // то в качестве масштаба используем коэффициент на основе высоты
            if (mScale * modelHeight > layoutHeight)
              mScale = layoutHeight / modelHeight;
 
            // Матрица масштабирования относительно центра в модели 
            Matrix3d matScale = Matrix3d.Scaling(mScale, modelCenter);
            // Масштабируем старый центр в модели по этой матрице
            Point3d  modelCenterScaling = modelCenter.TransformBy(matScale);
            // Находим матрицу переноса точки масштабированного центра в модели в точку
            // центра листа
            Matrix3d matDisp = Matrix3d.Displacement(layoutCenter - modelCenterScaling);
            // Комбинированная матрица
            Matrix3d matCompose = matScale.PreMultiplyBy(matDisp);
 
            IdMapping idmap = new IdMapping();
 
            btrLayout.UpgradeOpen();
            db.DeepCloneObjects(ids, layout.BlockTableRecordId, idmap, false);
            btrLayout.DowngradeOpen();
            foreach (IdPair idpair in idmap)
            {
              if (idpair.IsPrimary)
              {
                Entity ent = tr.GetObject(idpair.Value, OpenMode.ForWrite) as Entity;
                if (ent != null)
                {
                  // Трансформируем по комбинированной матрице
                  ent.TransformBy(matCompose);
                }
              }
            }
          }
 
        }
        tr.Commit();
      }
    }
  }
}