import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.ibm.xml.enc.AlgorithmFactoryExtn;
import com.ibm.xml.enc.EncryptionContext;

import java.security.Key;

import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import org.apache.xpath.XPathAPI;


public class ReferenceListOnlyToken implements Token{
    private Element encryptionTemplate = null;
    private String encryptionTemplateFile = "ReferenceListOnlyTemplate.xml";
    private WSSMessage wssMessage = null;
    private String KeyPass = "";
    private String wsuKeyID = "";
    private String keyName = "";
    private Element keyInfoElement = null;
    private AlgorithmFactoryExtn af = null;
    private String wsuEncryptedElementID = "";
    private Key key = null;
    private KeyStore store = null;

    public ReferenceListOnlyToken (
                String keyStoreFileName,
                String keyStorePassword,
                String keyName,
                String wsuKeyID,
                WSSMessage wssMessage
                ){

        this.wssMessage = wssMessage;
        this.wsuKeyID = wsuKeyID;
        this.keyName = keyName;

        DOMParser dp = new DOMParser();

        try{

            dp.parse ( encryptionTemplateFile );
            encryptionTemplate = dp.getDocument().getDocumentElement();

            Element keyNameElement = (Element)encryptionTemplate.getOwnerDocument().
                                      getElementsByTagName("KeyName").item(0);

            keyNameElement.appendChild( encryptionTemplate.getOwnerDocument().
                                        createTextNode(keyName)
                                      );

            store = KeyStore.getInstance("JKS");
            File f = new File ( keyStoreFileName );
            InputStream is = null;
            if (f.exists()){
                is = new FileInputStream ( f );
            }

            char[] storePass;
            storePass = new String( keyStorePassword ).toCharArray();
            store.load(is, storePass);

            // Instantiate an algo factory.
            af = new AlgorithmFactoryExtn();

        }catch ( Exception e ){
            System.out.println ( "Exception in ReferenceListTokon's Constructor......." );
            e.printStackTrace();
        }
    }// End of constructor


    // Return the value of the wsu:Id attribute of the token.
    public String getWSUId()
    {
        return wsuKeyID;
    }


    // Return a string identifier for the type of token.
    public String getType()
    {
        return "ReferenceListOnlyToken";
    }


    // Return the parent WSSMessage object that wraps this token.
    public WSSMessage getParentWSSMessage()
    {
        return wssMessage;
    }


    // Return the WSS-compliant XML structure of this token.
    public String getXMLString()
    {
        return "\n<ReferenceList>\n"+
                       "<DataReference URI=\"#"+ wsuEncryptedElementID +"\"/>\n"+
               "</ReferenceList>\n";
    }


    // Return the W3C DOM object representation of the token object.
    public org.w3c.dom.Element getDOMObject()
    {
        return null;
    }


    // The Token instance will receive an array of bytes (secretBytes) that
    // it will treat as the secret to be used to
    // produce signatures and decrypt encrypted data.
    public void setSecret(byte[] secretBytes)
    {
        try
        {
            char keyPass[];
            String secret = new String ( secretBytes );
            keyPass = new String ( secret ).toCharArray();
            key = store.getKey ( keyName, keyPass );

        }catch ( Exception e ){
            System.out.println ( 
                           "Error in Getting the Key from keyBytes in RefListToken...." );
           }
    }


    // The sign, signWithXPath, and decrypt methods are included here
    // because the Token knows the secret and the secret is needed to
    // produce signatures and for decryption.
    // Therefore, the WSSMessage, Signature, and EncryptedData classes will
    // use these methods internally.
    // The Token should never tell the secret to any other class.

    public String sign(
                String wsuElementID,
                String digestAlgo,
                String signatureAlgo,
                String canonicalizationAlgo
                ){
        return null;
    }


    public String signWithXPath(
                String XPathExpression,
                String digestAlgo,
                String signatureAlgo,
                String canonicalizationAlgo
                ){
        return null;
    }


    public void decrypt(String encryptedData)
    {
    }


    public Element encrypt(
                String wsuEncryptedElementID,
                String encryptionAlgo,
                Element elementWantToEncrypt
                ){

        /**** Step 1 *********/
            this.wsuEncryptedElementID = wsuEncryptedElementID;

        /**** Step 2 *********/
            Element encAlgo = (Element)encryptionTemplate.getElementsByTagName(
               "EncryptionMethod").item(0);
            encAlgo.setAttribute ( "Algorithm", encryptionAlgo );

        /**** Step 3 *********/
            Element encryptedData = (Element)encryptionTemplate.getOwnerDocument().
                                getElementsByTagName("EncryptedData").item(0);
            encryptedData.setAttribute ( "wsu:Id", wsuEncryptedElementID );

        /**** Step 4 *********/
            EncryptionContext ec = new EncryptionContext();
            ec.setAlgorithmFactory (af);
            ec.setKey (key);
            ec.setData (elementWantToEncrypt);
            ec.setEncryptedType ( (Element)encryptionTemplate.cloneNode(true),
                     null, null, null);

            try{
                ec.encrypt();
                ec.replace();
            }catch(Exception e){
                System.out.println ( "Error in ecryptionContext" );
                e.printStackTrace();
            }

        return getElementByWSUId (
                    elementWantToEncrypt.getOwnerDocument(),
                    wsuEncryptedElementID );
    }// End of encrypt Element


    private Element getElementByWSUId (
                Document document,
                String wsuEncryptedElementID
                ){

        Element root = document.getDocumentElement();

        String xpathString = "//*[@Id=\"" + wsuEncryptedElementID + "\"]";
        Element plainTextElement = null;

        try{
            plainTextElement = (Element)XPathAPI.selectSingleNode(root, xpathString);
        }catch ( Exception e ){
            System.out.println ( "Exception in getting the plainTextElement." );
            e.printStackTrace();
        }

        return plainTextElement;
    }//encryptedDataElementFinder

}// End of class ReferenceListToken