/******************************************************************************* 
 *  Copyright 2007 Amazon Technologies, Inc.  
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  
 *  You may not use this file except in compliance with the License. 
 *  You may obtain a copy of the License at: http://aws.amazon.com/apache2.0
 *  This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
 *  CONDITIONS OF ANY KIND, either express or implied. See the License for the 
 *  specific language governing permissions and limitations under the License.
 * ***************************************************************************** 
 *    __  _    _  ___ 
 *   (  )( \/\/ )/ __)
 *   /__\ \    / \__ \
 *  (_)(_) \/\/  (___/
 
 *  Amazon A2S Java Library
 *  API Version: 2007-10-29
 *  Generated: Thu Jan 10 05:27:32 PST 2008 
 
 */



package com.amazonaws.a2s;

import com.amazonaws.a2s.*;
import com.amazonaws.a2s.model.*;

import java.io.BufferedReader;
import java.util.Map;
import java.util.Iterator;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.JAXBException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.HttpMethodRetryHandler;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NoHttpResponseException;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.InputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.xml.transform.stream.StreamSource;




/**
 
 * Amazon Associates Web Service (A2S) is the best way to make money on the Internet.
 * Amazon has spent ten years and hundreds of millions of dollars developing a
 * world-class web service that millions of customers use every day. As a developer,
 * you can build A2S applications that leverage this robust, scalable, and reliable
 * technology. You get access to much of the data that is used by Amazon,
 * including the items for sale, customer reviews, seller reviews, as well as most
 * of the functionality that you see on www.amazon.com, such as finding items,
 * finding similar items, displaying customer reviews, and product promotions.
 * In short, A2S operations open the doors to Amazon's databases so that you can
 * take advantage of Amazon's sophisticated E-commerce data and functionality.
 * Build your own web store to sell Amazon items or your own items.
 <br><br>
 * Best of all, A2S is free. By signing up to become a A2S developer, you join the
 * tens of thousands of developers who are already realizing financial gains by
 * creating A2S-driven applications and web stores. In 2006, A2S developers sold well
 * over $600 million worth of items. Would you like a percentage of that revenue?
 <br><br>
 * E-commerce is the practice of conducting business over the Internet. This guide
 * explains in detail how you can use A2S operations to create storefronts in which
 * you enable Internet customers to search for your items, see pictures of them, find
 * related items, get customer reviews, and purchase items.
 <br><br>
 * With e-commerce, the barrier of distance between the shopper and the store goes away:
 * the local video store must compete with stores across the country. E-commerce levels
 * the playing field: the web site of an individual seller can appear as sophisticated
 * and intoxicating as that of a major retailer. A2S is your opportunity to enter the
 * world market where patronage is not limited by the size of your storefront, foot traffic
 * or locality. Welcome to the world of A2S E-commerce.
 
 
 *
 * AmazonA2SClient is the implementation of AmazonA2S based on the
 * Apache <a href="http://jakarta.apache.org/commons/httpclient/">HttpClient</a>.
 *
 */
public  class AmazonA2SClient implements AmazonA2S {
    
    private final Log log = LogFactory.getLog(AmazonA2SClient.class);
    
    private String awsAccessKeyId = null;
    private String associateTag = null;
    private AmazonA2SConfig config = null;
    private HttpClient httpClient = null;
    private static JAXBContext  jaxbContext;
    private static ThreadLocal<Unmarshaller> unmarshaller;
    
    /** Initialize JAXBContext and  Unmarshaller **/
    static {
        try {
            jaxbContext = JAXBContext.newInstance("com.amazonaws.a2s.model", AmazonA2S.class.getClassLoader());
        catch (JAXBException ex) {
            throw new ExceptionInInitializerError(ex);
        }
        unmarshaller = new ThreadLocal<Unmarshaller>() {
            @Override   
            protected synchronized Unmarshaller initialValue() {
                try {
                    return jaxbContext.createUnmarshaller();
                catch(JAXBException e) {
                    throw new ExceptionInInitializerError(e);
                }
            }
        };
    }
 
    
    /**
     * Constructs AmazonA2SClient with AWS Access Key ID and Accociate Tag
     *
     @param awsAccessKeyId
     *          AWS Access Key ID
     @param associateTag
     *          Associate Tag
     */
    public  AmazonA2SClient(String awsAccessKeyId,String associateTag) {
        this (awsAccessKeyId, associateTag, AmazonA2SLocale.US);
    }
    
    /**
     * Constructs AmazonA2SClient with AWS Access Key ID,  Associate Tag
     * and specific service locale
     *
     @param awsAccessKeyId
     *          AWS Access Key ID
     @param associateTag
     *          Associate Tag
     @param locale
     *          Locale you wish to invoke the service call at. 
     
     *  Supported Locales are: US, UK, DE, FR, CA, JP. Note, not all operations
     *  supported by all locales. Please refer to Amazon A2S documentation for
     *  more information
     *                                                          
     */
    public  AmazonA2SClient(String awsAccessKeyId,String associateTag, AmazonA2SLocale locale) {
        this.awsAccessKeyId = awsAccessKeyId;
        this.associateTag = associateTag;
        this.config = locale.getConfig();
        this.httpClient = configureHttpClient();
    }

    
    // Public API ------------------------------------------------------------//


        
    /**
     * Help  Request
     
     <br><br>
     * The Help operation provides information about A2S operations and
     * response groups. For operations, Help lists required and optional
     * request parameters, as well as default and optional response groups the
     * operation can use. For response groups, Help lists the operations that can
     * use the response group as well as the response tags returned by the
     * response group in the XML response.
     <br><br>
     * The Help operation is not often used in customer applications. It can, however, be
     * used to help the developer in the following ways:
     <br><br>
     <ul>
     <li>Provide contextual help in an interactive development environment (IDE) for developerst</li>
     <li>Automate documentation creation as part of a developer's toolkit. </li>
     </ul>
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>Help</li>
     </ul>
     *   
     @param request
     *          Help Request
     @return
     *          Help Response from the service
     *
     @throws AmazonA2SException
     */
    public HelpResponse help(HelpRequest... requestthrows AmazonA2SException {
        Help action = new Help();
        return invoke(HelpResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Item Search  Request
     
     <br><br>
     * The ItemSearch operation returns items that satisfy the search
     * criteria, including one or more search indices
     <br><br>
     * ItemSearch returns up to ten search results at a time. When condition
     * equals "All," ItemSearch returns up to three offers per condition (if they exist),
     * for example, three new, three used, three refurbished, and three collectible items.
     * Or, for example, if there are no collectible or refurbished offers, ItemSearch
     * returns three new and three used offers.
     <br><br>
     * Because there are thousands of items in each search index, ItemSearch requires
     * that you specify the value for at least one parameter in addition to a search index.
     * The additional parameter value must reference items within the specified search index.
     * For example, you might specify a browse node (BrowseNode is an ItemSearch parameter),
     * Harry Potter Books, within the Books product category. You would not get results,
     * for example, if you specified the search index to be Automotive and the browse node
     * to be Harry Potter Books. In this case, the parameter value is not associated with
     * the search index value.
     <br><br>
     * The ItemPage parameter enables you to return a specified page of results. The maximum
     * ItemPage number that can be returned is 400. An error is returned if you try to access
     * higher numbered pages. If you do not include ItemPage in your request, the first page
     * will be returned by default. There can be up to ten items per page.
     <br><br>
     * ItemSearch is the operation that is used most often in requests. In general,
     * when trying to find an item for sale, you use this operation.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>Small</li>
     <li>Accessories</li>
     <li>BrowseNodes</li>
     <li>EditorialReview</li>
     <li>Images</li>
     <li>ItemAttributes</li>
     <li>ItemIds</li>
     <li>Large</li>
     <li>ListmaniaLists</li>
     <li>Medium</li>
     <li>MerchantItemAttributes</li>
     <li>OfferFull</li>
     <li>Offers</li>
     <li>OfferSummary</li>
     <li>Reviews</li>
     <li>SalesRank</li>
     <li>SearchBins</li>
     <li>Similarities</li>
     <li>Subjects</li>
     <li>Tracks</li>
     <li>TagsSummary</li>
     <li>Tags</li>
     <li>VariationImages</li>
     <li>VariationMinimum</li>
     <li>Variations</li>
     <li>VariationSummary</li>
     </ul>
     *   
     @param request
     *          ItemSearch Request
     @return
     *          ItemSearch Response from the service
     *
     @throws AmazonA2SException
     */
    public ItemSearchResponse itemSearch(ItemSearchRequest... requestthrows AmazonA2SException {
        ItemSearch action = new ItemSearch();
        return invoke(ItemSearchResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Item Lookup  Request
     
     <br><br>
     * Given an Item identifier, the ItemLookup operation returns some or all
     * of the item attributes, depending on the response group specified in the request.
     * By default, ItemLookup returns an item's ASIN, DetailPageURL, Manufacturer,
     * ProductGroup, and Title of the item.
     <br><br>
     * ItemLookup supports many response groups, so you can retrieve many different
     * kinds of product information, called item attributes, including product reviews,
     * variations, similar products, pricing, availability, images of products, accessories,
     * and other information.
     <br><br>
     * To look up more than one item at a time, separate the item identifiers by commas.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>Small</li>
     <li>Accessories</li>
     <li>BrowseNodes</li>
     <li>EditorialReview</li>
     <li>Images</li>
     <li>ItemAttributes</li>
     <li>ItemIds</li>
     <li>Large</li>
     <li>ListmaniaLists</li>
     <li>Medium</li>
     <li>MerchantItemAttributes</li>
     <li>OfferFull</li>
     <li>Offers</li>
     <li>OfferSummary</li>
     <li>Reviews</li>
     <li>SalesRank</li>
     <li>Similarities</li>
     <li>Subjects</li>
     <li>Tracks</li>
     <li>TagsSummary</li>
     <li>Tags</li>
     <li>VariationImages</li>
     <li>VariationMinimum</li>
     <li>Variations</li>
     <li>VariationSummary</li>
     </ul>
     *   
     @param request
     *          ItemLookup Request
     @return
     *          ItemLookup Response from the service
     *
     @throws AmazonA2SException
     */
    public ItemLookupResponse itemLookup(ItemLookupRequest... requestthrows AmazonA2SException {
        ItemLookup action = new ItemLookup();
        return invoke(ItemLookupResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Browse Node Lookup  Request
     
     <br><br>
     * Given a browse node ID, BrowseNodeLookup returns the specified browse node's name, children, and ancestors.
     * The names and browse node IDs of the children and ancestor browse nodes are also returned.
     * BrowseNodeLookup enables you to traverse the browse node hierarchy to find a browse node.
     * As you traverse down the hierarchy, you refine your search and limit the number of items returned.
     * For example, you might traverse the following hierarchy: DVD Used DVDs Kids and Family,
     * to select out of all the DVDs offered by Amazon only those that are appropriate for family viewing.
     * Returning the items associated with Kids and Family produces a much more targeted result than a search
     * based at the level of Used DVDs.
     <br><br>
     * Alternatively, by traversing up the browse node tree, you can determine the root category of an item.
     * You might do that, for example, to return the top seller of the root product category using the
     * TopSeller response group in an ItemSearch request.
     <br><br>
     * You can use BrowseNodeLookup iteratively to navigate through the browse node hierarchy to
     * reach the node that most appropriately suits your search. Then you can use the browse node ID in
     * an ItemSearch request. This response would be far more targeted than, for example,
     * searching through all of the browse nodes in a search index.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>BrowseNodeInfo</li>
     </ul>
     *   
     @param request
     *          BrowseNodeLookup Request
     @return
     *          BrowseNodeLookup Response from the service
     *
     @throws AmazonA2SException
     */
    public BrowseNodeLookupResponse browseNodeLookup(BrowseNodeLookupRequest... requestthrows AmazonA2SException {
        BrowseNodeLookup action = new BrowseNodeLookup();
        return invoke(BrowseNodeLookupResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * List Search  Request
     
     <br><br>
     * Given a customer name or Email address, the ListSearch operation
     * returns the associated list ID(s) but not the list items. To find those,
     * use the list ID returned by ListSearch with  ListLookup  .
     <br><br>
     * Specifying a full name or just a first or last name in the request typically
     * returns multiple lists belonging to different people. Using Email as the
     * identifier produces more filtered results.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>ListInfo</li>
     <li>ListMinimum</li>
     </ul>
     *   
     @param request
     *          ListSearch Request
     @return
     *          ListSearch Response from the service
     *
     @throws AmazonA2SException
     */
    public ListSearchResponse listSearch(ListSearchRequest... requestthrows AmazonA2SException {
        ListSearch action = new ListSearch();
        return invoke(ListSearchResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * List Lookup  Request
     
     <br><br>
     * The ListLookup operation returns, by default, summary information about a
     * list that you specify in the request. The summary information includes the:
     <br><br>
     <ul>
     <li>Creation date of the list</li>
     <li>Name of the list's creator</li>
     </ul>
     <br><br>
     * The operation returns up to ten sets of summary information per page.
     <br><br>
     * Lists are specified by list type and list ID, which can be found using ListSearch.
     <br><br>
     * You cannot lookup more than one list at a time in a single request.
     * You can, however, make a batch request to look for more than one list simultaneously.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>ListInfo</li>
     <li>Accessories</li>
     <li>BrowseNodes</li>
     <li>EditorialReview</li>
     <li>Images</li>
     <li>ItemAttributes</li>
     <li>ItemIds</li>
     <li>Large</li>
     <li>ListFull</li>
     <li>ListItems</li>
     <li>ListmaniaLists</li>
     <li>Medium</li>
     <li>Offers</li>
     <li>OfferSummary</li>
     <li>Reviews</li>
     <li>SalesRank</li>
     <li>Similarities</li>
     <li>Small</li>
     <li>Subjects</li>
     <li>Tracks</li>
     <li>VariationMinimum</li>
     <li>Variations</li>
     <li>VariationSummary</li>
     </ul>
     *   
     @param request
     *          ListLookup Request
     @return
     *          ListLookup Response from the service
     *
     @throws AmazonA2SException
     */
    public ListLookupResponse listLookup(ListLookupRequest... requestthrows AmazonA2SException {
        ListLookup action = new ListLookup();
        return invoke(ListLookupResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Customer Content Search  Request
     
     <br><br>
     * For a given customer Email address or name, the CustomerContentSearch
     * operation returns matching customer IDs, names, nicknames, and residence
     * information (city, state, and country). In general, supplying an Email
     * address returns unique results whereas supplying a name more often
     * returns multiple results.
     <br><br>
     * Often you use CustomerContentSearch to find a customer ID that you can
     * use in the CustomerContentLookup operation, which returns more
     * extensive customer information.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>CustomerInfo</li>
     </ul>
     *   
     @param request
     *          CustomerContentSearch Request
     @return
     *          CustomerContentSearch Response from the service
     *
     @throws AmazonA2SException
     */
    public CustomerContentSearchResponse customerContentSearch(CustomerContentSearchRequest... requestthrows AmazonA2SException {
        CustomerContentSearch action = new CustomerContentSearch();
        return invoke(CustomerContentSearchResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Customer Content Lookup  Request
     
     <br><br>
     * For a given customer ID, the CustomerContentLookup operation
     * retrieves all of the information a customer has made public about
     * themselves on Amazon. Such information includes some or all of the following:
     <br><br>
     <ul>
     <li>AboutMe</li>
     <li>Birthday</li>
     <li>City, State and Country</li>
     <li>Customer Reviews</li>
     <li>Customer ID</li>
     <li>Name</li>
     <li>Nickname</li>
     <li>Wedding Registry</li>
     <li>WishList</li>
     </ul>
     <br><br>
     * To find a customer ID, use the CustomerContentSearch operation.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>CustomerInfo</li>
     <li>CustomerReviews</li>
     <li>CustomerLists</li>
     <li>CustomerFull</li>
     <li>TaggedGuides</li>
     <li>TaggedItems</li>
     <li>TaggedListmaniaLists</li>
     <li>TagsSummary</li>
     <li>Tags</li>
     </ul>
     *   
     @param request
     *          CustomerContentLookup Request
     @return
     *          CustomerContentLookup Response from the service
     *
     @throws AmazonA2SException
     */
    public CustomerContentLookupResponse customerContentLookup(CustomerContentLookupRequest... requestthrows AmazonA2SException {
        CustomerContentLookup action = new CustomerContentLookup();
        return invoke(CustomerContentLookupResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Similarity Lookup  Request
     
     <br><br>
     * The SimilarityLookup operation returns up to ten products per page that are
     * similar to one or more items specified in the request. This operation is
     * typically used to pique a customer's interest in buying something similar to what they've already ordered.
     <br><br>
     * If you specify more than one item, SimilarityLookup returns the intersection of similar
     * items each item would return separately. Alternatively, you can use the SimilarityType
     * parameter to return the union of items that are similar to any of the specified items.
     * A maximum of ten similar items are returned; the operation does not return additional
     * pages of similar items. if there are more than ten similar items, running the same
     * request can result in different answers because the ten that are included in the
     * response are picked randomly.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>Small</li>
     <li>Accessories</li>
     <li>BrowseNodes</li>
     <li>EditorialReview</li>
     <li>Images</li>
     <li>Large</li>
     <li>ItemAttributes</li>
     <li>ItemIds</li>
     <li>ListmaniaLists</li>
     <li>Medium</li>
     <li>Offers</li>
     <li>OfferSummary</li>
     <li>PromotionDetails</li>
     <li>PromotionSummary</li>
     <li>Reviews</li>
     <li>SalesRank</li>
     <li>Similarities</li>
     <li>Tracks</li>
     <li>VariationMinimum</li>
     <li>Variations</li>
     <li>VariationSummary</li>
     </ul>
     *   
     @param request
     *          SimilarityLookup Request
     @return
     *          SimilarityLookup Response from the service
     *
     @throws AmazonA2SException
     */
    public SimilarityLookupResponse similarityLookup(SimilarityLookupRequest... requestthrows AmazonA2SException {
        SimilarityLookup action = new SimilarityLookup();
        return invoke(SimilarityLookupResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Seller Lookup  Request
     
     <br><br>
     * The SellerLookup operation returns detailed information about sellers and,
     * in the US locale, merchants. To lookup a seller, you must use their seller ID.
     * The information returned includes the seller's name, location, average rating by
     * customers, and the first five customer feedback entries. SellerLookup will not,
     * however, return the seller's Email or business addresses.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>Seller</li>
     </ul>
     *   
     @param request
     *          SellerLookup Request
     @return
     *          SellerLookup Response from the service
     *
     @throws AmazonA2SException
     */
    public SellerLookupResponse sellerLookup(SellerLookupRequest... requestthrows AmazonA2SException {
        SellerLookup action = new SellerLookup();
        return invoke(SellerLookupResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Cart Get  Request
     
     <br><br>
     * The CartGet operation enables you to retrieve the IDs, quantities,
     * and prices of all of the items, including SavedForLater items in a
     * remote shopping cart.
     <br><br>
     * Because the contents of a cart can change for different reasons,
     * such as availability, you should not keep a copy of a cart locally.
     * Instead, use CartGet to retrieve the items in a remote shopping cart.
     <br><br>
     * To retrieve the items in a cart, you must specify the cart using the
     * CartId and HMAC values, which are returned in the CartCreate operation.
     * A value similar to HMAC, URLEncodedHMAC, is also returned. This value is the
     * URL encoded version of the HMAC. This encoding is necessary because some
     * characters, such as + and /, cannot be included in a URL. Rather than
     * encoding the HMAC yourself, use the URLEncodedHMAC value for the HMAC parameter.
     <br><br>
     * CartGet does not work after the customer has used the PurchaseURL to either
     * purchase the items or merge them with the items in their Amazon cart.
     <br><br>
     * If the associated CartCreate request specified an AssociateTag, all CartGet
     * requests must also include a value for AssociateTag otherwise the request will fail.
     <br><br>
     <b>Available Response Groups</b>:
     <ul>
     <li>Request</li>
     <li>Cart</li>
     <li>CartSimilarities</li>
     <li>CartTopSellers</li>
     <li>CartNewReleases</li>
     </ul>
     *   
     @param request
     *          CartGet Request
     @return
     *          CartGet Response from the service
     *
     @throws AmazonA2SException
     */
    public CartGetResponse cartGet(CartGetRequest... requestthrows AmazonA2SException {
        CartGet action = new CartGet();
        return invoke(CartGetResponse.class, action.withRequest(request).toMap());
    }

        
    /**
     * Cart Add  Request
     
     <br><br>
     * The CartAdd operation enables you to add items to an existing remote shopping cart.
     * CartAdd can only be used to place a new item in a shopping cart. It cannot be used to increase