/// <summary>
        /// Converts to the template type, valid types are double, int, string, bool
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="valueBaseType"></param>
        /// <returns></returns>
        public static TType ConvertTo <TType>(this ValueBaseType valueBaseType)
        {
            var decimalType = valueBaseType as DecimalValueType;

            if (decimalType != null && decimalType.DecimalValueSpecified)
            {
                return((TType)Convert.ChangeType(decimalType.DecimalValue, typeof(TType)));
            }
            var stringType = valueBaseType as StringValueType;

            if (stringType != null)
            {
                return((TType)Convert.ChangeType(stringType.StringValue, typeof(TType)));
            }
            var integerType = valueBaseType as IntegerValueType;

            if (integerType != null)
            {
                return((TType)Convert.ChangeType(integerType.IntegerValue, typeof(TType)));
            }
            var booleanType = valueBaseType as BooleanValueType;

            if (booleanType != null && booleanType.BooleanValueSpecified)
            {
                return((TType)Convert.ChangeType(booleanType.BooleanValue, typeof(TType)));
            }
            return(default(TType));
        }
        /// <summary>
        /// Converts to an IfcValue type, IfcText, IfcInteger, IfcBoolean or IfcReal
        /// </summary>
        /// <param name="valueBaseType"></param>
        /// <returns></returns>
        public static IfcValue ConvertToIfcValue(this ValueBaseType valueBaseType)
        {
            var decimalType = valueBaseType as DecimalValueType;

            if (decimalType != null && decimalType.DecimalValueSpecified)
            {
                return(new IfcReal((double)Convert.ChangeType(decimalType.DecimalValue, typeof(double))));
            }
            var stringType = valueBaseType as StringValueType;

            if (stringType != null)
            {
                return(new IfcText((string)Convert.ChangeType(stringType.StringValue, typeof(string))));
            }
            var integerType = valueBaseType as IntegerValueType;

            if (integerType != null)
            {
                return(new IfcInteger((int)Convert.ChangeType(integerType.IntegerValue, typeof(int))));
            }
            var booleanType = valueBaseType as BooleanValueType;

            if (booleanType != null && booleanType.BooleanValueSpecified)
            {
                return(new IfcBoolean((bool)Convert.ChangeType(booleanType.BooleanValue, typeof(bool))));
            }
            return(default(IfcText));
        }
        /// <summary>
        /// Returns true if the value base type has a value, empty strings return false
        /// </summary>
        /// <param name="valueBaseType"></param>
        /// <returns></returns>
        public static bool HasValue(this ValueBaseType valueBaseType)
        {
            if (valueBaseType == null)
            {
                return(false);
            }
            var decimalType = valueBaseType as DecimalValueType;

            if (decimalType != null)
            {
                return(decimalType.DecimalValueSpecified);
            }
            var stringType = valueBaseType as StringValueType;

            if (stringType != null)
            {
                return(!string.IsNullOrEmpty(stringType.StringValue));
            }
            var integerType = valueBaseType as IntegerValueType;

            if (integerType != null)
            {
                return(true);                     //for some reason these are always OK
            }
            var booleanType = valueBaseType as BooleanValueType;

            if (booleanType != null)
            {
                return(booleanType.BooleanValueSpecified);
            }
            return(false);
        }
        private void AddQuantity(ValueBaseType valueBaseType, string cobiePropertyName,
                                 IfcUnitConverter actualUnits, IfcElementQuantity propertySetDefinition, NamedProperty namedProperty)
        {
            try
            {
                var cobieValue = valueBaseType.ConvertTo <double>(); //quantities are always doubles
                if (actualUnits.IsUndefined)
                {
                    throw new ArgumentException("Invalid unit type " + actualUnits.UserDefinedSiUnitName +
                                                " has been pass to CreatePropertySingleValue");
                }

                IfcPhysicalQuantity quantity;

                switch (actualUnits.UnitName)
                //they are all here for future proofing, time, mass and count though are not really used by COBie
                {
                case IfcUnitEnum.AREAUNIT:
                    quantity =
                        TargetRepository.Instances.New <IfcQuantityArea>(
                            q => q.AreaValue = new IfcAreaMeasure(cobieValue));
                    break;

                case IfcUnitEnum.LENGTHUNIT:
                    quantity =
                        TargetRepository.Instances.New <IfcQuantityLength>(
                            q => q.LengthValue = new IfcLengthMeasure(cobieValue));
                    break;

                case IfcUnitEnum.MASSUNIT:
                    quantity =
                        TargetRepository.Instances.New <IfcQuantityWeight>(
                            q => q.WeightValue = new IfcMassMeasure(cobieValue));
                    break;

                case IfcUnitEnum.TIMEUNIT:
                    quantity =
                        TargetRepository.Instances.New <IfcQuantityTime>(
                            q => q.TimeValue = new IfcTimeMeasure(cobieValue));
                    break;

                case IfcUnitEnum.VOLUMEUNIT:
                    quantity =
                        TargetRepository.Instances.New <IfcQuantityVolume>(
                            q => q.VolumeValue = new IfcVolumeMeasure(cobieValue));
                    break;

                case IfcUnitEnum.USERDEFINED:     //we will treat this as Item for now
                    quantity =
                        TargetRepository.Instances.New <IfcQuantityCount>(
                            q => q.CountValue = new IfcCountMeasure(cobieValue));
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
                quantity.Description = "Converted from COBie " + cobiePropertyName;
                quantity.Name        = namedProperty.PropertyName;
                propertySetDefinition.Quantities.Add(quantity);
            }
            catch (Exception e)
            {
                throw new Exception("Failed to convert a COBie Value to and Ifc Quantity. " + e.Message);
            }
        }
        /// <summary>
        /// Creates the property and if required the property set, populates them with the correct values and adds them to the IfcObject
        /// If the value is null or empty no property is created
        /// </summary>
        /// <param name="ifcObject">Object to associate the property with</param>
        /// <param name="valueBaseType">COBie value to populate the property with</param>
        /// <param name="cobiePropertyName">Name of the COBie property being mapped</param>
        /// <param name="defaultUnits">Units to use if the COBie property does not specify</param>
        internal bool TryCreatePropertySingleValue(IfcObject ifcObject, ValueBaseType valueBaseType, string cobiePropertyName, IfcUnitConverter?defaultUnits)
        {
            if (!valueBaseType.HasValue())
            {
                return(false);                           //nothing to do
            }
            try
            {
                NamedProperty namedProperty;
                if (CobieToIfcPropertyMap.TryGetValue(cobiePropertyName, out namedProperty))
                {
                    var actualUnits = new IfcUnitConverter(valueBaseType.UnitName);
                    if (actualUnits.IsUndefined && defaultUnits.HasValue)
                    {
                        actualUnits = defaultUnits.Value;
                    }
                    List <IfcPropertySetDefinition> propertySetDefinitionList;
                    if (!_objectsToPropertySets.TryGetValue(ifcObject, out propertySetDefinitionList))
                    {
                        propertySetDefinitionList = new List <IfcPropertySetDefinition>();
                        _objectsToPropertySets.Add(ifcObject, propertySetDefinitionList);
                    }
                    var propertySetDef = propertySetDefinitionList.Find(p => p.Name == namedProperty.PropertySetName);
                    //see what sets we have against this object
                    if (propertySetDef == null)
                    {
                        //simplistic way to decide if this should be a quantity, IFC 4 specifies the name starts with QTO, under 2x3 most vendors have gone for BaseQuantities
                        if (namedProperty.PropertySetName.StartsWith("qto_", true, CultureInfo.InvariantCulture) ||
                            namedProperty.PropertySetName.StartsWith("basequantities", true,
                                                                     CultureInfo.InvariantCulture))
                        {
                            var quantitySet = TargetRepository.Instances.New <IfcElementQuantity>();
                            propertySetDefinitionList.Add(quantitySet);
                            quantitySet.Name = namedProperty.PropertySetName;
                            var relDef = TargetRepository.Instances.New <IfcRelDefinesByProperties>();
                            relDef.RelatingPropertyDefinition = quantitySet;
                            relDef.RelatedObjects.Add(ifcObject);
                            AddQuantity(valueBaseType, cobiePropertyName, actualUnits, quantitySet,
                                        namedProperty);
                        }
                        else //it is a normal property set
                        {
                            var propertySet = TargetRepository.Instances.New <IfcPropertySet>();
                            propertySetDefinitionList.Add(propertySet);
                            propertySet.Name = namedProperty.PropertySetName;
                            var relDef = TargetRepository.Instances.New <IfcRelDefinesByProperties>();
                            relDef.RelatingPropertyDefinition = propertySet;
                            relDef.RelatedObjects.Add(ifcObject);
                            AddProperty(ifcObject, valueBaseType.ConvertToIfcValue(), cobiePropertyName,
                                        propertySet,
                                        namedProperty);
                        }
                    }
                    else //need to use an existing PropertySet definition
                    {
                        //simplistic way to decide if this should be a quantity, IFC 4 specifies the name starts with QTO, under 2x3 most vendors have gone for BaseQuantities
                        if (namedProperty.PropertySetName.StartsWith("qto_", true, CultureInfo.InvariantCulture) ||
                            namedProperty.PropertySetName.StartsWith("basequantities", true,
                                                                     CultureInfo.InvariantCulture))
                        {
                            AddQuantity(valueBaseType, cobiePropertyName, actualUnits,
                                        (IfcElementQuantity)propertySetDef,
                                        namedProperty);
                        }
                        else //it is a normal property set

                        {
                            AddProperty(ifcObject, valueBaseType.ConvertToIfcValue(), cobiePropertyName, (IfcPropertySet)propertySetDef,
                                        namedProperty);
                        }
                    }
                    return(true);
                }
                throw new ArgumentException("Incorrect property map", "cobiePropertyName");
            }
            catch (Exception e)
            {
                Debug.WriteLine("Incorrect property map, " + e.Message);
                Debug.Assert(false);
                return(false);
            }
        }