/// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException("getShippingOptionRequest");
            }

            if (getShippingOptionRequest.Items == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "No shipment items"
                           }
                }
            }
            ;

            if (getShippingOptionRequest.ShippingAddress == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "Shipping address is not set"
                           }
                }
            }
            ;

            if (getShippingOptionRequest.ShippingAddress.Country == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "Shipping country is not set"
                           }
                }
            }
            ;

            if (string.IsNullOrEmpty(getShippingOptionRequest.ZipPostalCodeFrom))
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "Origin postal code is not set"
                           }
                }
            }
            ;

            //get available services
            string errors;
            var    availableServices = CanadaPostHelper.GetServices(getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode,
                                                                    _canadaPostSettings.ApiKey, _canadaPostSettings.UseSandbox, out errors);

            if (availableServices == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               errors
                           }
                }
            }
            ;

            //create object for the get rates requests
            var    result = new GetShippingOptionResponse();
            object destinationCountry;

            switch (getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode.ToLowerInvariant())
            {
            case "us":
                destinationCountry = new mailingscenarioDestinationUnitedstates
                {
                    zipcode = getShippingOptionRequest.ShippingAddress.ZipPostalCode
                };

                break;

            case "ca":
                destinationCountry = new mailingscenarioDestinationDomestic
                {
                    postalcode = getShippingOptionRequest.ShippingAddress.ZipPostalCode
                };
                break;

            default:
                destinationCountry = new mailingscenarioDestinationInternational
                {
                    countrycode = getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode
                };
                break;
            }

            var mailingScenario = new mailingscenario
            {
                customernumber   = _canadaPostSettings.CustomerNumber,
                originpostalcode = getShippingOptionRequest.ZipPostalCodeFrom,
                destination      = new mailingscenarioDestination
                {
                    Item = destinationCountry
                }
            };

            //get original parcel characteristics
            decimal originalLength;
            decimal originalWidth;
            decimal originalHeight;
            decimal originalWeight;

            GetWeight(getShippingOptionRequest, out originalWeight);
            GetDimensions(getShippingOptionRequest, out originalLength, out originalWidth, out originalHeight);

            //get rate for all available services
            var errorSummary = new StringBuilder();

            foreach (var service in availableServices.service)
            {
                var currentService = CanadaPostHelper.GetServiceDetails(_canadaPostSettings.ApiKey, service.link.href, service.link.mediatype, out errors);
                if (currentService != null)
                {
                    #region parcels count calculation

                    var totalParcels = 1;

                    //parcels count by weight
                    var maxWeight = currentService.restrictions != null &&
                                    currentService.restrictions.weightrestriction != null &&
                                    currentService.restrictions.weightrestriction.maxSpecified
                        ? currentService.restrictions.weightrestriction.max : int.MaxValue;
                    if (originalWeight * 1000 > maxWeight)
                    {
                        var parcelsOnWeight = Convert.ToInt32(Math.Ceiling(originalWeight * 1000 / maxWeight));
                        if (parcelsOnWeight > totalParcels)
                        {
                            totalParcels = parcelsOnWeight;
                        }
                    }

                    //parcels count by length
                    var maxLength = currentService.restrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions.length != null &&
                                    currentService.restrictions.dimensionalrestrictions.length.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.length.max : int.MaxValue;
                    if (originalLength > maxLength)
                    {
                        var parcelsOnLength = Convert.ToInt32(Math.Ceiling(originalLength / maxLength));
                        if (parcelsOnLength > totalParcels)
                        {
                            totalParcels = parcelsOnLength;
                        }
                    }

                    //parcels count by width
                    var maxWidth = currentService.restrictions != null &&
                                   currentService.restrictions.dimensionalrestrictions != null &&
                                   currentService.restrictions.dimensionalrestrictions.width != null &&
                                   currentService.restrictions.dimensionalrestrictions.width.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.width.max : int.MaxValue;
                    if (originalWidth > maxWidth)
                    {
                        var parcelsOnWidth = Convert.ToInt32(Math.Ceiling(originalWidth / maxWidth));
                        if (parcelsOnWidth > totalParcels)
                        {
                            totalParcels = parcelsOnWidth;
                        }
                    }

                    //parcels count by height
                    var maxHeight = currentService.restrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions.height != null &&
                                    currentService.restrictions.dimensionalrestrictions.height.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.height.max : int.MaxValue;
                    if (originalHeight > maxHeight)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(originalHeight / maxHeight));
                        if (parcelsOnHeight > totalParcels)
                        {
                            totalParcels = parcelsOnHeight;
                        }
                    }

                    //parcel count by girth
                    var lengthPlusGirthMax = currentService.restrictions != null &&
                                             currentService.restrictions.dimensionalrestrictions != null &&
                                             currentService.restrictions.dimensionalrestrictions.lengthplusgirthmaxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.lengthplusgirthmax : int.MaxValue;
                    var lengthPlusGirth = 2 * (originalWidth + originalHeight) + originalLength;
                    if (lengthPlusGirth > lengthPlusGirthMax)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(lengthPlusGirth / lengthPlusGirthMax));
                        if (parcelsOnHeight > totalParcels)
                        {
                            totalParcels = parcelsOnHeight;
                        }
                    }

                    //parcel count by sum of length, width and height
                    var lengthWidthHeightMax = currentService.restrictions != null &&
                                               currentService.restrictions.dimensionalrestrictions != null &&
                                               currentService.restrictions.dimensionalrestrictions.lengthheightwidthsummaxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.lengthheightwidthsummax : int.MaxValue;
                    var lengthWidthHeight = originalLength + originalWidth + originalHeight;
                    if (lengthWidthHeight > lengthWidthHeightMax)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(lengthWidthHeight / lengthWidthHeightMax));
                        if (parcelsOnHeight > totalParcels)
                        {
                            totalParcels = parcelsOnHeight;
                        }
                    }

                    #endregion

                    //set parcel characteristics
                    mailingScenario.services = new[] { currentService.servicecode };
                    mailingScenario.parcelcharacteristics = new mailingscenarioParcelcharacteristics
                    {
                        weight     = Math.Round(originalWeight / totalParcels, 3),
                        dimensions = new mailingscenarioParcelcharacteristicsDimensions
                        {
                            length = Math.Round(originalLength / totalParcels, 1),
                            width  = Math.Round(originalWidth / totalParcels, 1),
                            height = Math.Round(originalHeight / totalParcels, 1)
                        }
                    };

                    //get rate
                    var priceQuotes = CanadaPostHelper.GetShippingRates(mailingScenario, _canadaPostSettings.ApiKey, _canadaPostSettings.UseSandbox, out errors);
                    if (priceQuotes != null)
                    {
                        foreach (var option in priceQuotes.pricequote)
                        {
                            result.ShippingOptions.Add(new ShippingOption
                            {
                                Name        = option.servicename,
                                Rate        = PriceToPrimaryStoreCurrency(option.pricedetails.due * totalParcels),
                                Description = string.Format("Delivery {0}into {1} parcels",
                                                            option.servicestandard != null && !string.IsNullOrEmpty(option.servicestandard.expectedtransittime)
                                    ? string.Format("in {0} days ", option.servicestandard.expectedtransittime) : string.Empty, totalParcels),
                            });
                        }
                    }
                    else
                    {
                        errorSummary.AppendLine(errors);
                    }
                }
                else
                {
                    errorSummary.AppendLine(errors);
                }
            }

            //write errors
            var errorString = errorSummary.ToString();
            if (!string.IsNullOrEmpty(errorString))
            {
                _logger.Error(errorString);
            }
            if (!result.ShippingOptions.Any())
            {
                result.AddError(errorString);
            }

            return(result);
        }
        /// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
                throw new ArgumentNullException("getShippingOptionRequest");

            if (getShippingOptionRequest.Items == null)
                return new GetShippingOptionResponse { Errors = new List<string> { "No shipment items" } };

            if (getShippingOptionRequest.ShippingAddress == null)
                return new GetShippingOptionResponse { Errors = new List<string> { "Shipping address is not set" } };

            if (getShippingOptionRequest.ShippingAddress.Country == null)
                return new GetShippingOptionResponse { Errors = new List<string> { "Shipping country is not set" } };

            if (string.IsNullOrEmpty(getShippingOptionRequest.ZipPostalCodeFrom))
                return new GetShippingOptionResponse { Errors = new List<string> { "Origin postal code is not set" } };

            //get available services
            string errors;
            var availableServices = CanadaPostHelper.GetServices(getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode,
                _canadaPostSettings.ApiKey, _canadaPostSettings.UseSandbox, out errors);
            if (availableServices == null)
                return new GetShippingOptionResponse { Errors = new List<string> { errors } };

            //create object for the get rates requests
            var result = new GetShippingOptionResponse();
            object destinationCountry;
            switch (getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode.ToLowerInvariant())
            {
                case "us":
                    destinationCountry = new mailingscenarioDestinationUnitedstates
                    {
                        zipcode = getShippingOptionRequest.ShippingAddress.ZipPostalCode
                    };
                    break;
                case "ca":
                    destinationCountry = new mailingscenarioDestinationDomestic
                    {
                        postalcode = getShippingOptionRequest.ShippingAddress.ZipPostalCode
                    };
                    break;
                default:
                    destinationCountry = new mailingscenarioDestinationInternational
                    {
                        countrycode = getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode
                    };
                    break;
            }

            var mailingScenario = new mailingscenario
            {
                customernumber = _canadaPostSettings.CustomerNumber,
                originpostalcode = getShippingOptionRequest.ZipPostalCodeFrom,
                destination = new mailingscenarioDestination
                {
                    Item = destinationCountry
                }
            };

            //get original parcel characteristics
            decimal originalLength;
            decimal originalWidth;
            decimal originalHeight;
            decimal originalWeight;
            GetWeight(getShippingOptionRequest, out originalWeight);
            GetDimensions(getShippingOptionRequest, out originalLength, out originalWidth, out originalHeight);

            //get rate for all available services
            var errorSummary = new StringBuilder();
            foreach (var service in availableServices.service)
            {
                var currentService = CanadaPostHelper.GetServiceDetails(_canadaPostSettings.ApiKey, service.link.href, service.link.mediatype, out errors);
                if (currentService != null)
                {
                    #region parcels count calculation

                    var totalParcels = 1;

                    //parcels count by weight
                    var maxWeight = currentService.restrictions != null
                        && currentService.restrictions.weightrestriction != null
                        && currentService.restrictions.weightrestriction.maxSpecified
                        ? currentService.restrictions.weightrestriction.max : int.MaxValue;
                    if (originalWeight * 1000 > maxWeight)
                    {
                        var parcelsOnWeight = Convert.ToInt32(Math.Ceiling(originalWeight * 1000 / maxWeight));
                        if (parcelsOnWeight > totalParcels)
                            totalParcels = parcelsOnWeight;
                    }

                    //parcels count by length
                    var maxLength = currentService.restrictions != null
                        && currentService.restrictions.dimensionalrestrictions != null
                        && currentService.restrictions.dimensionalrestrictions.length != null
                        && currentService.restrictions.dimensionalrestrictions.length.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.length.max : int.MaxValue;
                    if (originalLength > maxLength)
                    {
                        var parcelsOnLength = Convert.ToInt32(Math.Ceiling(originalLength / maxLength));
                        if (parcelsOnLength > totalParcels)
                            totalParcels = parcelsOnLength;
                    }

                    //parcels count by width
                    var maxWidth = currentService.restrictions != null
                        && currentService.restrictions.dimensionalrestrictions != null
                        && currentService.restrictions.dimensionalrestrictions.width != null
                        && currentService.restrictions.dimensionalrestrictions.width.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.width.max : int.MaxValue;
                    if (originalWidth > maxWidth)
                    {
                        var parcelsOnWidth = Convert.ToInt32(Math.Ceiling(originalWidth / maxWidth));
                        if (parcelsOnWidth > totalParcels)
                            totalParcels = parcelsOnWidth;
                    }

                    //parcels count by height
                    var maxHeight = currentService.restrictions != null
                        && currentService.restrictions.dimensionalrestrictions != null
                        && currentService.restrictions.dimensionalrestrictions.height != null
                        && currentService.restrictions.dimensionalrestrictions.height.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.height.max : int.MaxValue;
                    if (originalHeight > maxHeight)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(originalHeight / maxHeight));
                        if (parcelsOnHeight > totalParcels)
                            totalParcels = parcelsOnHeight;
                    }

                    //parcel count by girth
                    var lengthPlusGirthMax = currentService.restrictions != null
                        && currentService.restrictions.dimensionalrestrictions != null
                        && currentService.restrictions.dimensionalrestrictions.lengthplusgirthmaxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.lengthplusgirthmax : int.MaxValue;
                    var lengthPlusGirth = 2 * (originalWidth + originalHeight) + originalLength;
                    if (lengthPlusGirth > lengthPlusGirthMax)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(lengthPlusGirth / lengthPlusGirthMax));
                        if (parcelsOnHeight > totalParcels)
                            totalParcels = parcelsOnHeight;
                    }

                    //parcel count by sum of length, width and height
                    var lengthWidthHeightMax = currentService.restrictions != null
                        && currentService.restrictions.dimensionalrestrictions != null
                        && currentService.restrictions.dimensionalrestrictions.lengthheightwidthsummaxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.lengthheightwidthsummax : int.MaxValue;
                    var lengthWidthHeight = originalLength + originalWidth + originalHeight;
                    if (lengthWidthHeight > lengthWidthHeightMax)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(lengthWidthHeight / lengthWidthHeightMax));
                        if (parcelsOnHeight > totalParcels)
                            totalParcels = parcelsOnHeight;
                    }

                    #endregion

                    //set parcel characteristics
                    mailingScenario.services = new[] { currentService.servicecode };
                    mailingScenario.parcelcharacteristics = new mailingscenarioParcelcharacteristics
                    {
                        weight = Math.Round(originalWeight / totalParcels, 3),
                        dimensions = new mailingscenarioParcelcharacteristicsDimensions
                        {
                            length = Math.Round(originalLength / totalParcels, 1),
                            width = Math.Round(originalWidth / totalParcels, 1),
                            height = Math.Round(originalHeight / totalParcels, 1)
                        }
                    };

                    //get rate
                    var priceQuotes = CanadaPostHelper.GetShippingRates(mailingScenario, _canadaPostSettings.ApiKey, _canadaPostSettings.UseSandbox, out errors);
                    if (priceQuotes != null)
                        foreach (var option in priceQuotes.pricequote)
                        {
                            result.ShippingOptions.Add(new ShippingOption
                            {
                                Name = option.servicename,
                                Rate = PriceToPrimaryStoreCurrency(option.pricedetails.due * totalParcels),
                                Description = string.Format("Delivery {0}into {1} parcels", 
                                    option.servicestandard != null && !string.IsNullOrEmpty(option.servicestandard.expectedtransittime) 
                                    ? string.Format("in {0} days ", option.servicestandard.expectedtransittime) : string.Empty, totalParcels),
                            });
                        }
                    else
                        errorSummary.AppendLine(errors);
                }
                else
                    errorSummary.AppendLine(errors);
            }

            //write errors
            var errorString = errorSummary.ToString();
            if (!string.IsNullOrEmpty(errorString))
                _logger.Error(errorString);
            if (!result.ShippingOptions.Any())
                result.AddError(errorString);

            return result;

        }