using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Internal.DatabaseServices;
using AcAp = Autodesk.AutoCAD.ApplicationServices;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using AcGe = Autodesk.AutoCAD.Geometry;
using AcEd = Autodesk.AutoCAD.EditorInput;
using AcId = Autodesk.AutoCAD.Internal.DatabaseServices;
 
[assembly: CommandClass(typeof(Rivilis.BPTTest))]
 
#pragma warning disable 618
 
namespace Rivilis
{
 
  public class BPTTest
  {
    [CommandMethod("InsBlockWithBPT")]
    public static void InsBlockWithBPT()
    {
      AcAp.Document doc = AcAp.Application.DocumentManager.MdiActiveDocument;
      AcEd.Editor ed = doc.Editor;
      AcDb.Database db = doc.Database;
      AcEd.PromptResult rs = ed.GetString("\nEnter block name: ");
      if (rs.Status != PromptStatus.OK) return;
      AcDb.ObjectId idBTR = AcDb.ObjectId.Null, idBref;
      using (AcDb.BlockTable bt = db.BlockTableId.Open(OpenMode.ForRead) as AcDb.BlockTable) {
        if (bt != null) idBTR = bt[rs.StringResult];
      }
      if (idBTR.IsNull) {
        ed.WriteMessage("\nDatabase has not block \"{0}\"", rs.StringResult);
        return;
      }
      AcEd.PromptPointResult rsp = ed.GetPoint("\nEnter position: ");
      if (rsp.Status != PromptStatus.OK) return;
 
      using (AcDb.Transaction tr = doc.TransactionManager.StartTransaction()) {
        AcDb.BlockTableRecord btr = 
          tr.GetObject(idBTR, OpenMode.ForRead) as AcDb.BlockTableRecord;
        AcDb.BlockTableRecord btrSpace = 
          tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as AcDb.BlockTableRecord;
        if (btr != null && btrSpace != null) {
          AcDb.BlockReference brf =
            new AcDb.BlockReference(rsp.Value.TransformBy(ed.CurrentUserCoordinateSystem), idBTR);
          idBref = btrSpace.AppendEntity(brf);
          tr.AddNewlyCreatedDBObject(brf, true);
          if (brf.IsDynamicBlock) {
            AcDb.BlockTableRecord btrDyn =
              tr.GetObject(brf.DynamicBlockTableRecord, OpenMode.ForRead) as AcDb.BlockTableRecord;
            if (btrDyn != null && !btrDyn.ExtensionDictionary.IsNull) {
              AcDb.DBDictionary extDic = 
                tr.GetObject(btrDyn.ExtensionDictionary, OpenMode.ForRead) as AcDb.DBDictionary;
              if (extDic != null) {
                AcId.EvalGraph graph =
                  tr.GetObject(extDic.GetAt("ACAD_ENHANCEDBLOCK"), OpenMode.ForRead) as AcId.EvalGraph;
                int[] nodeIds = graph.GetAllNodes();
                foreach (uint nodeId in nodeIds) {
                  DBObject node = graph.GetNode(nodeId, OpenMode.ForRead, tr);
                  if (!(node is BlockPropertiesTable)) continue;
                  AcDb.BlockPropertiesTable bpt = node as AcDb.BlockPropertiesTable;
                  int currentRow = SelectRowNumber(ref bpt);
                  AcDb.BlockPropertiesTableRow row = bpt.Rows[currentRow];
                  List<string> nameProps = new List<string>();
                  for (int i = 0; i < bpt.Columns.Count; i++) {
                    nameProps.Add(bpt.Columns[i].Parameter.Name);
                  }
                  AcDb.DynamicBlockReferencePropertyCollection dynPropsCol =
                    brf.DynamicBlockReferencePropertyCollection;
                  foreach (AcDb.DynamicBlockReferenceProperty dynProp in dynPropsCol) {
                    int i = nameProps.FindIndex(delegate(string s) { return s == dynProp.PropertyName; });
                    if (i >= 0 && i < nameProps.Count) {
                      try { 
                        dynProp.Value = row[i].AsArray()[0].Value;
                      } catch {
                        ed.WriteMessage("\nCan not set to {0} value={1}", 
                          dynProp.PropertyName, row[i].AsArray()[0].Value);
                      }
                    }
                  }
                }
              }
            }
          } else {
            ed.WriteMessage("\nBlock \"{0}\" is not dynamic", rs.StringResult);
          }
        }
        tr.Commit();
      }
    }
 
    public static int SelectRowNumber(ref BlockPropertiesTable bpt)
    {
      AcEd.Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
      int columns = bpt.Columns.Count;
      int rows = bpt.Rows.Count;
      int currentRow = 0, currentColumn = 0;
      ed.WriteMessage("\n");
      for (currentColumn = 0; currentColumn < columns; currentColumn++) {
        ed.WriteMessage("{0}; ", bpt.Columns[currentColumn].Parameter.Name);
      }
      foreach (BlockPropertiesTableRow row in bpt.Rows) {
        ed.WriteMessage("\n[{0}]:\t", currentRow);
        for (currentColumn = 0; currentColumn < columns; currentColumn++) {
          TypedValue[] columnValue = row[currentColumn].AsArray();
          foreach (TypedValue tpVal in columnValue) {
            ed.WriteMessage("{0}; ", tpVal.Value);
          }
          ed.WriteMessage("|");
        }
        currentRow++;
      }
 
      AcEd.PromptIntegerResult res;
      string.Format("0-{0}", rows - 1);
 
      while ((res = ed.GetInteger(string.Format("\nSelect row number (0-{0}): ", rows - 1))).Status == PromptStatus.OK) {
        if (res.Value >= 0 && res.Value <= rows) return res.Value;
      }
      return -1;
    }
  }
}