private const double ARROW_FULL_LENGTH       = 2; // Hack to move position based on a known length

        public static void GenerateBlock(Database database, LevelBlockDetails x, LevelBlockDetails y)
        {
            if (!x.IsValid || !y.IsValid)
            {
                return;
            }

            if (x.Level.Equals(y.Level))
            {
                HousingExtensionApplication.Current.Logger.Entry(Resources.Message_Levels_Are_Equal);
                return;
            }

            Point2d startPoint;
            Point2d endPoint;
            double  startLevel;
            double  endLevel;

            //Always point downhill
            if (x.Level > y.Level)
            {
                startPoint = x.Point2d;
                startLevel = x.Level;
                endPoint   = y.Point2d;
                endLevel   = y.Level;
            }
            else
            {
                startPoint = y.Point2d;
                startLevel = y.Level;
                endPoint   = x.Point2d;
                endLevel   = x.Level;
            }

            var vector = endPoint.GetAsVector() - startPoint.GetAsVector();

            var gradient = 1 / ((startLevel - endLevel) / vector.Length);
            var midPoint = startPoint + vector * 0.5;

            // Hack to move position based on a known length
            var shiftVector = vector.GetNormal() * ARROW_FULL_LENGTH;
            var matrix      = Matrix2d.Displacement(shiftVector);

            midPoint.TransformBy(matrix);

            var rotation = vector.Angle;

            NewGradientBlockAtPoint(database, new Point3d(midPoint.X, midPoint.Y, 0), gradient, rotation);

            HousingExtensionApplication.Current.Logger.Entry(string.Format(Resources.Command_Output_GradientLineLength, Math.Round(vector.Length, 3)));
        }
        public static LevelBlockDetails GetPromptedBlockDetails(string prompt, Editor ed, Transaction trans)
        {
            var objectId = ed.PromptForEntity(prompt, typeof(BlockReference), Resources.Command_Prompt_RejectBlockReference, true);

            if (!objectId.HasValue)
            {
                return(LevelBlockDetails.CreateEmpty());
            }

            var block   = GetBlockReference(objectId.Value, trans);
            var details = new LevelBlockDetails(block);

            if (details.IsValid)
            {
                return(details);
            }

            HousingExtensionApplication.Current.Logger.Entry(Resources.Message_Invalid_Level_Block_Selected, Severity.Warning);
            return(LevelBlockDetails.CreateEmpty());
        }
        public static LevelBlockDetails UpdateExistingLevelBlock(BlockReference block, double level)
        {
            //Update level value, but not adjust any other properties.
            var trans = block.Database.TransactionManager.TopTransaction;

            foreach (ObjectId attObjId in block.AttributeCollection)
            {
                var attDbObj = trans.GetObject(attObjId, OpenMode.ForRead);
                if (attDbObj is AttributeReference attRef)
                {
                    if (string.Equals(attRef.Tag, LEVEL_ATTRIBUTE_NAME, StringComparison.CurrentCultureIgnoreCase))
                    {
                        attRef.UpgradeOpen();

                        attRef.TextString = $"{level:0.000}";
                        return(new LevelBlockDetails(block));
                    }
                }
            }

            HousingExtensionApplication.Current.Logger.Entry(Resources.Message_Invalid_Level_Block_Selected, Severity.Warning);
            return(LevelBlockDetails.CreateEmpty());
        }
        public static LevelBlockDetails NewLevelBlockAtPoint(Database database, LevelBlockArgs arg)
        {
            var trans = database.TransactionManager.TopTransaction;
            var bt    = (BlockTable)trans.GetObject(database.BlockTableId, OpenMode.ForRead);

            foreach (var btrId in bt)
            {
                var btr = (BlockTableRecord)trans.GetObject(btrId, OpenMode.ForRead);
                if (string.Equals(btr.Name, LEVEL_BLOCK_NAME, StringComparison.CurrentCultureIgnoreCase))
                {
                    var blockId          = btr.ObjectId;
                    var modelSpaceRecord = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    var blockRef = new BlockReference(arg.Point, blockId)
                    {
                        Layer = ObjectModel.Constants.FOR_REVIEW_LEVEL_LAYER
                    };

                    if (arg.Rotation.HasValue)
                    {
                        blockRef.Rotation = arg.Rotation.Value;
                    }

                    modelSpaceRecord.AppendEntity(blockRef);
                    trans.AddNewlyCreatedDBObject(blockRef, true);

                    if (btr.HasAttributeDefinitions)
                    {
                        foreach (var objId in btr)
                        {
                            var dbObj = trans.GetObject(objId, OpenMode.ForRead);
                            if (dbObj is AttributeDefinition acAtt)
                            {
                                if (acAtt.Constant)
                                {
                                    continue;
                                }

                                using (var acAttRef = new AttributeReference())
                                {
                                    if (string.Equals(acAtt.Tag, LEVEL_ATTRIBUTE_NAME, StringComparison.CurrentCultureIgnoreCase))
                                    {
                                        acAttRef.SetAttributeFromBlock(acAtt, blockRef.BlockTransform);
                                        acAttRef.Position = acAtt.Position.TransformBy(blockRef.BlockTransform);

                                        acAttRef.TextString = $"{arg.Level:0.000}";
                                        blockRef.AttributeCollection.AppendAttribute(acAttRef);
                                        trans.AddNewlyCreatedDBObject(acAttRef, true);
                                    }
                                }
                            }
                        }
                    }

                    if (arg.Rotate.HasValue && blockRef.IsDynamicBlock)
                    {
                        var dynamicProps = blockRef.DynamicBlockReferencePropertyCollection;
                        foreach (DynamicBlockReferenceProperty dynamicProp in dynamicProps)
                        {
                            if (string.Equals(dynamicProp.PropertyName, ROTATE_ATTRIBUTE_NAME, StringComparison.CurrentCultureIgnoreCase))
                            {
                                dynamicProp.Value = arg.Rotate.Value;
                            }
                        }
                    }


                    database.TransactionManager.QueueForGraphicsFlush();

                    return(new LevelBlockDetails(blockRef));
                }
            }

            return(LevelBlockDetails.CreateEmpty());
        }