/// <summary>
        /// The Get Estimate API is used to get a rough estimate for a project based on the parameters listed below. 
        /// It is useful for client applications that are capable of counting words and that want to give the end customer a rough estimate of how much a project will cost. 
        /// Please note, the estimate will not be the same as the actual price if the onDemand word counting algorithm produces a different result than the client application.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="unitCount"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        public Estimate GetEstimate(Service service, Int32 unitCount, EstimateOptions options)
        {
            Estimate result = null;

            if (service == null)
            {
                throw new ArgumentNullException("service", "Must specify a Service to generate an estimate");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options", "Must specify estimate options to get an estimate");
            }

            options.Initialize(this, service);

            String targetLanguagesCSV = String.Join(",", options.TargetLanguages.Select(p => p.LanguageCode).ToArray());

            Uri uri = new Uri(this.EndPoint.AbsoluteUri + String.Format("api/estimate?service_id={0}&unit_count={1}&currency={2}&source_lang={3}&target_lang={4}",
                service.ServiceID, unitCount, options.Currency, options.SourceLanguage, targetLanguagesCSV));

            HttpWebRequest request = CreateRequestGET(uri);

            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        XDocument document = XDocument.Load(reader);

                        result = new Estimate(document.Element("Estimate"), this);
                    }
                }
                else
                {
                    this.HandleError(response);
                }
            }

            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="client"></param>
        /// <param name="service"></param>
        internal void Initialize(IContentAPI client, Service service)
        {
            if (client == null)
            {
                throw new ArgumentNullException("client", "Client cannot be null");
            }

            if (service == null)
            {
                throw new ArgumentNullException("service", "Service cannot be null");
            }

            if (this.Currency == null)
            {
                this.Currency = client.DefaultCurrency;
            }

            this.ServiceID = service.ServiceID;

            if (this.TargetLanguages == null)
            {
                this.TargetLanguages = service.TargetLanguages.Select(p => new TargetLanguage(p));
            }

            if (!service.SourceLanguages.Contains(this.SourceLanguage.LanguageCode))
            {
                throw new ArgumentOutOfRangeException("sourceLanguage", this.SourceLanguage.LanguageCode, "Source language must be in the Service's list of source languages");
            }

            if (this.TargetLanguages == null)
            {
                throw new ArgumentNullException("targetLanguages", "Target languages cannot be null");
            }

            if (this.TargetLanguages.Count() == 0)
            {
                throw new ArgumentOutOfRangeException("targetLanguages", "Must include at least one target language");
            }

            foreach (TargetLanguage targetLanguage in this.TargetLanguages)
            {
                if (!service.TargetLanguages.Contains(targetLanguage.LanguageCode))
                {
                    throw new ArgumentOutOfRangeException("targetLanguages", targetLanguage.LanguageCode, "Target lanauges must be in the Service's list of target languages");
                }
            }
        }
        /// <summary>
        /// This interface is used to generate a quote from Files that were uploading using the Add File API. A quote can contain multiple projects.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="files"></param>
        /// <param name="options"></param>
        /// <param name="referenceFiles">If null, will not use reference files</param>
        /// <returns></returns>
        public Quote GenerateQuote(Service service, IEnumerable<File> files, QuoteOptions options, IEnumerable<File> referenceFiles = null)
        {
            // Check the service
            if (service == null)
            {
                throw new ArgumentNullException("service", "Must specify a Service to generate a quote");
            }

            if (!service.AcceptsFiles)
            {
                throw new ArgumentException("This service does not accept files.  Please use GenerateQuote with Products", "service");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options", "Must specify options to generate a quote");
            }

            options.Initialize(this, service);

            // Now Generate the Quote based on the uplaoded files
            Quote result = null;

            Uri uri = new Uri(this.EndPoint.AbsoluteUri + "api/quote/generate");

            HttpWebRequest request = this.CreateRequestPOST(uri, new GenerateQuote(files, options, referenceFiles));

            using (HttpWebResponse response = request.GetResponseWithoutException() as HttpWebResponse)
            {
                if (response.StatusCode == HttpStatusCode.Created)
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        XDocument document = XDocument.Load(reader);

                        result = new Quote(document.Element("Quote"), this);
                    }
                }
                else
                {
                    this.HandleError(response);
                }
            }

            return result;
        }
        /// <summary>
        /// This interface is used to generate a quote from file URLs that will be loaded. A quote can contain multiple projects.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="fileURLs"></param>
        /// <param name="fileNames"></param>
        /// <param name="options"></param>
        /// <param name="referenceFiles">If null, will not use reference files</param>
        /// <returns></returns>
        public Quote GenerateQuote(Service service, Uri[] fileURLs, String[] fileNames, QuoteOptions options, IEnumerable<File> referenceFiles = null)
        {
            // Check the service
            if (service == null)
            {
                throw new ArgumentNullException("service", "Must specify a Service to generate a quote");
            }

            if (fileURLs == null || fileNames == null)
            {
                throw new ArgumentNullException("files", "Must specify the fileURLs and fileNames");
            }

            if (!service.AcceptsFiles)
            {
                throw new ArgumentException("This service does not accept files.  Please use GenerateQuote with Products", "service");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options", "Must specify options to generate a quote");
            }

            options.Initialize(this, service);

            // Check that the file extensions are valid
            foreach (String fileName in fileNames)
            {
                if (!service.AcceptsExtension(Path.GetExtension(fileName)))
                {
                    throw new ArgumentOutOfRangeException("fileNames", fileName, "Service does not accept files with this extension");
                }
            }

            // Upload the files
            List<File> addedFiles = new List<File>();

            for (int i = 0; i < fileNames.Length && i < fileURLs.Length; i++)
            {
                addedFiles.Add(this.AddFile(options.SourceLanguage.LanguageCode, fileNames[i], fileURLs[i]));
            }

            return this.GenerateQuote(service, addedFiles, options, referenceFiles);
        }
        /// <summary>
        /// This interface is used to generate a quote from file paths that will be loaded. A quote can contain multiple projects.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="filePaths"></param>
        /// <param name="options"></param>
        /// <param name="referenceFilePaths">If null, will not use reference files</param>
        /// <returns></returns>
        public Quote GenerateQuote(Service service, string[] filePaths, QuoteOptions options, string[] referenceFilePaths = null)
        {
            // Check the service
            if (service == null)
            {
                throw new ArgumentNullException("service", "Must specify a Service to generate a quote");
            }

            if (!service.AcceptsFiles)
            {
                throw new ArgumentException("This service does not accept files.  Please use GenerateQuote with Products", "service");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options", "Must specify options to generate a quote");
            }

            options.Initialize(this, service);

            // Check that the file extensions are valid
            foreach (String filePath in filePaths)
            {
                if (!service.AcceptsExtension(Path.GetExtension(filePath)))
                {
                    throw new ArgumentOutOfRangeException("filePaths", filePath, "Service does not accept files with this extension");
                }
            }

            // Upload the files
            List<File> addedFiles = new List<File>();

            foreach (String filePath in filePaths)
            {
                addedFiles.Add(this.AddFile(options.SourceLanguage.LanguageCode, filePath));
            }

            // Upload the reference
            List<File> referenceFiles = null;

            if (referenceFilePaths != null)
            {
                referenceFiles = new List<File>();
                foreach (String referenceFilePath in referenceFilePaths)
                {
                    referenceFiles.Add(this.AddFile(options.SourceLanguage.LanguageCode, referenceFilePath));
                }
            }

            return this.GenerateQuote(service, addedFiles, options, referenceFiles);
        }
        /// <summary>
        /// Adds a new project to onDemand. Should be used in conjunction with Generate Quote to make a purchase. 
        /// </summary>
        /// <param name="projectName"></param>
        /// <param name="service"></param>
        /// <param name="products"></param>
        /// <param name="options"></param>
        /// <param name="referenceFiles"></param>
        /// <returns></returns>
        public Project AddProject(String projectName, Service service, IEnumerable<Product> products, ProjectOptions options, IEnumerable<File> referenceFiles = null)
        {
            // Check the service
            if (service == null)
            {
                throw new ArgumentNullException("service", "Must specify a Service to add a project");
            }

            // Check the service
            if (service == null)
            {
                throw new ArgumentNullException("service", "Must specify a Service to add a project");
            }

            if (!service.AcceptsProducts)
            {
                throw new ArgumentException("This service does not accept projdcuts.  Please use AddProject with files", "service");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options", "Must specify project options to add a project");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options", "Must specify project options to add a project");
            }

            options.Initialize(this, service);

            Project result = null;

            Uri uri = new Uri(this.EndPoint.AbsoluteUri + "api/projects/add");

            HttpWebRequest request = this.CreateRequestPOST(uri, new AddProject(projectName, products, options, referenceFiles));

            using (HttpWebResponse response = request.GetResponseWithoutException() as HttpWebResponse)
            {
                if (response.StatusCode == HttpStatusCode.Created)
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        XDocument document = XDocument.Load(reader);

                        result = new Project(document.Element("Project"), this);
                    }
                }
                else
                {
                    this.HandleError(response);
                }
            }

            return result;
        }
        /// <summary>
        /// Provides detailed information about a single service
        /// </summary>
        /// <param name="serviceID"></param>
        /// <returns></returns>
        public Service GetService(int serviceID)
        {
            Service result = null;

            Uri uri = new Uri(this.EndPoint.AbsoluteUri + "api/services/" + serviceID);

            HttpWebRequest request = this.CreateRequestGET(uri);

            using (HttpWebResponse response = request.GetResponseWithoutException() as HttpWebResponse)
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        XDocument document = XDocument.Load(reader);

                        result = new Service(document.Element("Service"), this);
                    }
                }
                else
                {
                    this.HandleError(response);
                }
            }

            return result;
        }