/*
 * [AllPostersSOAP.java]
 *
 * Summary: Request information from AllPosters.com about posters using the SOAP interface.
 *
 * Copyright: (c) 2010-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
 *
 * Licence: This software may be copied and used freely for any purpose but military.
 *          http://mindprod.com/contact/nonmil.html
 *
 * Requires: JDK 1.8+
 *
 * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
 *
 * Version History:
 *  1.0 2010-02-07 initial version
 */
package com.mindprod.poster;

import com.mindprod.entities.DeEntifyStrings;
import com.mindprod.fastcat.FastCat;
import com.mindprod.http.Get;
import com.mindprod.http.Post;

import java.net.MalformedURLException;
import java.net.URL;

import static java.lang.System.*;

/**
 * Request information from AllPosters.com about posters using the SOAP interface.
 * <p/>
 * This code uses Canadian Mind Products standard classes you can download at
 * http://mindprod.com/products1.html#HTTP
 * http://mindprod.com/products1.html#FASTCAT
 * http://mindprod.com/products1.html#ENTITIES
 * Analysis of the response is done with JAXB in AllPostersComProvider.
 *
 * @author Roedy Green, Canadian Mind Products
 * @version 1.0 2010-02-07 initial version
 * @see AllPostersComProvider
 * @since 2010-02-07
 */
public final class AllPostersSOAP
    {
    /**
     * true if want messages in and out logged on console
     */
    private static final boolean DEBUGGING = false;

    /**
     * URL of description of AllPostersSOAP.com schema, case-sensitive
     */
    private static final String PRODUCT_INFORMATION_SCHEMA = "http://Webservice.Allposters.com/APCF" +
                                                             ".AffiliateWebService/ProductInformationService";

    /**
     * SOAP service name to request information about a single poster, case-sensitive
     */
    private static final String SOAP_ACTION_FOR_POSTER = "http://Webservice.Allposters.com/APCF" +
                                                         ".AffiliateWebService/ProductInformationService"
                                                         + "/GetProductByProductNumberInformation";

    /**
     * SOAP service name to request information about a single poster, case-sensitive
     */
    private static final String SOAP_ACTION_FOR_POSTERS = "http://Webservice.Allposters.com/APCF" +
                                                          ".AffiliateWebService/ProductInformationService" +
                                                          "/GetProductInformation";

    /**
     * a schema for all SOAP files, case-sensitive
     */
    private static final String SOAP_SCHEMA = "http://schemas.xmlsoap.org/soap/envelope/";

    /**
     * where we submit our SOAP message, case-sensitive
     */
    private static final String SOAP_URL = "http://webservice.allposters.com/ProductInformationService.asmx";

    /**
     * a generic schema for all XML files, case-sensitive
     */
    private static final String XSD_SCHEMA = "http://www.w3.org/2001/XMLSchema";

    /**
     * a generic schema for all XML files, case-sensitive
     */
    private static final String XSI_SCHEMA = "http://www.w3.org/2001/XMLSchema-instance";

    /**
     * get information about one poster from AllPosters.com SOAP service.
     * Unfortunately this SOAP service only returns information about the smallest size poster.
     * I have not yet figured out how to get information about other sizes.
     *
     * @param webSiteID website affiliate id, affiliate account number.
     * @param p         poster number
     *
     * @return raw response from allposters.com. Will be XML with some fields enclosed in entities instead of the
     * usual less than/greater than.
     */
    static String getSOAPInfoForPoster( int webSiteID, int p )
        {
        final Post post = new Post();
        post.setConnectTimeout( 90 * 1000 /* 90 seconds */ );
        post.setRequestProperties( "SOAPAction", "\"" + SOAP_ACTION_FOR_POSTER + "\"" );
        post.setContentType( "text/xml" );
        final FastCat sb = new FastCat( 5 /* max chunks to concatenate */ );
        sb.append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                   + "<soap:Envelope\n"
                   + " xmlns:xsi=\""
                   + XSI_SCHEMA
                   + "\"\n"
                   + " xmlns:xsd=\""
                   + XSD_SCHEMA
                   + "\"\n"
                   + " xmlns:soap=\""
                   + SOAP_SCHEMA
                   + "\">\n"
                   + "<soap:Body>\n"
                   + "<GetProductByProductNumberInformation\n"
                   + " xmlns=\""
                   + PRODUCT_INFORMATION_SCHEMA
                   + "\">\n"
                   + "<APCSearchXml>\n"
                   // For some reason, inner tags are treated as one big string, effectively entified to &lt; and &gt;
                   // We use CDATA to treat these tags as one big string.
                   + "<![CDATA[<APC_Search_Query>\n"
                   + "<WebSiteID>" );
        sb.append( webSiteID );
        sb.append( "</WebSiteID>\n"
                   + "<SearchText>" );
        sb.append( p /* poster product number */ );
        sb.append( "</SearchText>\n"
                   + "<LanguageID>1</LanguageID>\n"
                   + "<CurrencyCode>USD</CurrencyCode>\n"
                   + "</APC_Search_Query>]]>\n" // end of xml fields treated as one giant string.
                   + "</APCSearchXml>\n"
                   + "</GetProductByProductNumberInformation>\n"
                   + "</soap:Body>\n"
                   + "</soap:Envelope>\n" );
        final String body = sb.toString();
        if ( DEBUGGING )
            {
            out.println( body );
            }
        post.setPostBody( body );
        try
            {
            final String result = post.send( new URL( SOAP_URL ), Post.UTF8 );
            final int responseCode = post.getResponseCode();
            final String responseMessage = post.getResponseMessage();
            if ( responseCode != 200 )
                {
                err.println( "SOAP response: " + responseCode + " " + responseMessage );
                // carry on. might be redirect or something benign.
                }
            // some XML fields in result are delimited with &lt; &gt; instead of < >
            return DeEntifyStrings.deEntifyXML( result );
            }
        catch ( MalformedURLException e )
            {
            err.println( "bad soap url " + SOAP_URL );
            System.exit( 1 );
            // can't get here, but compiler does not know that.
            return null;
            }
        }

    /**
     * get information about posters in category from AllPosters.com SOAP service.
     *
     * @param webSiteID      website affiliate id, affiliate account number.
     * @param category       category to narrow search to
     * @param searchKeywords poster number or what to search for
     * @param page           page# for a multipage response
     *
     * @return raw response from allposters.com. Will be XML with some fields enclosed in entities instead of the
     * usual less than/greater than.
     */
    static String getSOAPinfoForPosters( int webSiteID, int category, String searchKeywords, int page )
        {
        final Post post = new Post();
        post.setRequestProperties( "SOAPAction", "\"" + SOAP_ACTION_FOR_POSTERS + "\"" );
        post.setContentType( "text/xml" );
        final FastCat sb = new FastCat( 9 /* max chunks to concatenate */ );
        sb.append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                   + "<soap:Envelope\n"
                   + " xmlns:xsi=\""
                   + XSI_SCHEMA
                   + "\"\n"
                   + " xmlns:xsd=\""
                   + XSD_SCHEMA
                   + "\"\n"
                   + " xmlns:soap=\""
                   + SOAP_SCHEMA
                   + "\">\n"
                   + "<soap:Body>\n"
                   + "<GetProductInformation\n"
                   + " xmlns=\""
                   + PRODUCT_INFORMATION_SCHEMA
                   + "\">\n"
                   + "<APCSearchXml>\n"
                   // For some reason, inner tags are treated as one big string, effectively entified to &lt; and &gt;
                   // We use CDATA to treat these tags as one big string.
                   + "<![CDATA[<APC_Search_Query>\n"
                   + "<WebSiteID>" );
        sb.append( webSiteID );
        sb.append( "</WebSiteID>\n"
                   + "<SearchText>" );
        sb.append( searchKeywords /* poster product number */ );
        sb.append( "</SearchText>\n"
                   + "<CategoryID>" );
        sb.append( category != 0 ? category : "" );
        sb.append( "</CategoryID>\n"
                   + "<PageNumber>" );
        sb.append( page );
        sb.append( "</PageNumber>\n"
                   + "<LanguageID>1</LanguageID>\n"
                   + "<CurrencyCode>USD</CurrencyCode>\n"
                   + "<ProductsPerPage>15</ProductsPerPage>\n"
                   + "<SortOrder>CA</SortOrder>\n"
                   + "<MinWidth>1</MinWidth>\n"
                   + "<MaxWidth>99</MaxWidth>\n"
                   + "<MinHeight>1</MinHeight>\n"
                   + "<MaxHeight>99</MaxHeight>\n"
                   + "<MinPrice>0</MinPrice>\n"
                   + "<MaxPrice>999</MaxPrice>\n"
                   + "</APC_Search_Query>]]>\n" // end of xml fields treated as one giant string.
                   + "</APCSearchXml>\n"
                   + "</GetProductInformation>\n"
                   + "</soap:Body>\n"
                   + "</soap:Envelope>\n" );
        final String body = sb.toString();
        if ( DEBUGGING )
            {
            out.println( body );
            }
        post.setPostBody( body );
        try
            {
            // some XML fields in result are delimited with &lt; &gt; instead of < >
            return DeEntifyStrings.deEntifyXML( post.send( new URL( SOAP_URL ), Get.UTF8 ) );
            }
        catch ( MalformedURLException e )
            {
            err.println( "bad soap url " + SOAP_URL );
            System.exit( 1 );
            // can't get here, but compiler does not know that.
            return null;
            }
        }

    /**
     * Test harness
     *
     * @param args two integers, AllPostersSOAP account number and poster product number.
     */
    public static void main( String[] args )
        {
        /* You can sometimes access the human page for a poster with:
     * product#
     * http://www.allposters.com/gallery.asp?aid=1025105019&apnum=3599007&LinkTypeID=1&PosterTypeID=1&desttype=7  */
        final int account = Integer.parseInt( args[ 0 ] ); // e.g. 1025105019
        final int posterNumber = Integer.parseInt( args[ 1 ] );  // e.g. 3599007
        out.println();
        out.println( "Information on one poster given poster#" );
        out.println( getSOAPInfoForPoster( account, posterNumber ) );
        out.println();
        //  out.println( "Information on posters given search keywords" );
        //  out.println( getSOAPinfoForPosters( account, 101, "matrix", 1 ) );
        }
    }