//______________________________________________________________________________
//   |       |
//   | knipp |           Knipp Medien und Kommunikation GmbH
//    -------
//
//
//     Knipp RRI Toolkit
//
//
//     Copyright (C) 2005 by Knipp Medien und Kommunikation GmbH
//
//     This library is free software; you can redistribute it and/or
//     modify it under the terms of the GNU Lesser General Public License 
//     as published by the Free Software Foundation; either version 2.1 
//     of the License, or any later version.
// 
//     This library is distributed in the hope that it will be useful, 
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
//     GNU Lesser General Public License for more details.
//
//     You should have received a copy of the GNU Lesser General
//     Public License along with this library; if not, write to the 
//     Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
//     Boston, MA 02111-1307 USA 
//
//
//     initial author:    Klaus Malorny
//
//     version:           1.4 (15127), 2011/06/03 14:52:49
//                        $Revision: 14936 $ 
//                        $Date: 2010-11-05 15:57:40 +0100 (Fri, 05 Nov 2010) $
//                        $Author: Klaus.Malorny $
//

package de.knipp.rritoolkit.response;

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.DOMException;

import de.knipp.rritoolkit.KVList;
import de.knipp.rritoolkit.KVMultiList;
import de.knipp.rritoolkit.ResponseParseException;

import static de.knipp.rritoolkit.XmlHelper.getElements;
import static de.knipp.rritoolkit.XmlHelper.getFirstElement;
import static de.knipp.rritoolkit.XmlHelper.getFirstElementText;

import static de.knipp.rritoolkit.DenicConstants.NS_GLOBAL;
import static de.knipp.rritoolkit.DenicConstants.NS_TXN;




/**
 *  base class for all DENIC responses
 *
 *  @version  $Revision: 14936 $
 *  @author   Klaus Malorny
 */

public class Response
{
  /** the client transaction ID */
  
  private String clientTxnID;
  
  
  /** the server transaction ID */
  
  private String serverTxnID;
  

  /** the overall result */
  
  private Result result;
  
  
  /** additional result messages */
  
  private List<ResultMessage> resultMessages;
  
  
  /** the message keys */
  
  private static final String[] messageKeys =
    { "info",
      "warning",
      "error",
      "fatalerror"
    };

    
  
  //--- XML conversion ---------------------------------------------------------
  
  /**
   *  initialize the instance from the given XML DOM document.
   *
   *  @param doc                      the document
   *  @throws DOMException            on DOM exceptions
   *  @throws ResponseParseException  if the document does not contain the
   *                                  expected content
   */
  
  public void fromXml (Document doc)
    throws DOMException, ResponseParseException
  {
    Element response = getFirstElement (doc, NS_GLOBAL, "registry-response");
    
    if (response == null)
      throw new ResponseParseException ("not a registry response", 
        doc.getDocumentElement ());
    
    Element txn = getFirstElement (response, NS_TXN, "transaction");

    if (txn == null) 
      throw new ResponseParseException ("transaction not found", response);
    
    serverTxnID = getFirstElementText (txn, NS_TXN, "stid");
    clientTxnID = getFirstElementText (txn, NS_TXN, "ctid");
    
    String resultText = getFirstElementText (txn, NS_TXN, "result");
    result = resultText == null ? null : Result.fromToken (resultText);

    if (result == null)
      throw new ResponseParseException ("result not found or invalid", txn);
    
    List<Element> msgs = getElements (txn, NS_TXN, "message");
    
    if (msgs.isEmpty ())
    {
      resultMessages = Collections.emptyList ();
    
    } else
    {
      resultMessages = new ArrayList<ResultMessage> ();
      
      for (Element el: msgs)
        resultMessages.add (new ResultMessage (el));
    }
    
    Element data = getFirstElement (txn, NS_TXN, "data");
    dataFromXml (data);
  }
  
  
  /**
   *  subclasses that need to read additional data from the XML response should
   *  override this method. The default implementation is empty and need not
   *  to be called.
   *
   *  @param data                     the data element or {@code null}
   *                                  if the data contains no additional data
   *  @throws DOMException            on DOM exceptions
   *  @throws ResponseParseException  if the document does not contain the
   *                                  expected content
   */
  
  protected void dataFromXml (Element data)
    throws DOMException, ResponseParseException
  {
    // empty
  }
  
  
  
  //--- KV conversion ----------------------------------------------------------
  
  /**
   *  initialize the instance from the given key-value multi-list
   *
   *  @param list                     the multilist
   *  @throws ResponseParseException  if the list does not contain the
   *                                  expected content
   */
  
  public void fromList (KVMultiList list)
    throws ResponseParseException
  {
    KVList main = list.getMainSection ();
    serverTxnID = main.getFirst ("stid");
    clientTxnID = main.getFirst ("ctid");
    String temp = main.getFirst ("result");
    result = temp == null ? null : Result.fromToken (temp);
    
    if (result == null)
      throw new ResponseParseException ("result not found or invalid", 
        "result", temp);
  
    resultMessages = null;
    
    for (String key: messageKeys)
    {
      String[] values = main.getAll (key);
      
      if (values != null)
      { for (String value: values)
        {
          if (resultMessages == null)
            resultMessages = new ArrayList<ResultMessage> ();

          resultMessages.add (new ResultMessage (key, true, value));
        }
      }
    }
    
    if (resultMessages == null) resultMessages = Collections.emptyList ();
    
    dataFromList (list);
  }
  
  /**
   *  subclasses that need to read additional data from the key-value pair
   *  response should override this method. The default implementation is empty 
   *  and need not to be called.
   *
   *  @param list                     the list
   *  @throws ResponseParseException  if the document does not contain the
   *                                  expected content
   */
  
  protected void dataFromList (KVMultiList list)
    throws ResponseParseException
  {
    // empty
  }
  
  
  
  //--- other ------------------------------------------------------------------
  
  
  /**
   *  call the suitable method of the given visitor
   *
   *  @param visitor               the visitor
   */
  
  public void accept (ResponseVisitor visitor)
  {
    visitor.visit (this);
  }
  
  
  
  
  /**
   *  returns a string representation for debugging purposes
   *
   *  @return                      the string representation
   */
  
  @Override
  public String toString ()
  {
    StringBuilder sb = new StringBuilder ();
    sb.append (super.toString ());
    
    if (serverTxnID != null)
    {
      sb.append ("\n  server txn ID: ");
      sb.append (serverTxnID);
    }
    
    if (clientTxnID != null)
    {
      sb.append ("\n  client txn ID: ");
      sb.append (clientTxnID);
    }
    
    sb.append ("\n  result: ");
    sb.append (result);

    for (ResultMessage msg: resultMessages)
    {
      sb.append ("\n    ");
      sb.append (msg);
    }
    
    sb.append ("\n");
    return sb.toString ();
  }
  
  
  
  
  //--- access methods ---------------------------------------------------------
  
  /**
   *  returns the result
   *
   *  @return                     the result
   */
  
  public Result getResult ()
  {
    return result;
  }
  
  
  /**
   *  returns whether the result is {@code Success}
   *
   *  @return                    {@code true} if so
   */
  
  public boolean isSuccess ()
  {
    return result == Result.Success;
  }
  
  
  /**
   *  returns the server transaction ID
   *
   *  @return                 the ID or {@code null} if none was supplied
   */
  
  public String getServerTxnID ()
  {
    return serverTxnID;
  }

  
  /**
   *  returns the client transaction ID
   *
   *  @return                 the ID or {@code null} if none was supplied
   */
  
  public String getClientTxnID ()
  {
    return clientTxnID;
  }
  
  
  
  /**
   *  returns a list of list of the supplied result messages
   *
   *  @return                 the list
   */
  
  public List<ResultMessage> getResultMessages ()
  {
    return resultMessages;
  }
  
  
  
  
  
  /**
   *  enumeration for the different result types of a response
   *
   *  @version  $Revision: 14936 $
   *  @author   Klaus Malorny
   */
  
  public static enum Result
  {
    /** success */

    Success ("success"),


    /** failed */

    Failed ("failed"),


    /** exception */

    Exception ("exception");
    
    /** the DENIC token */
    
    private String denicToken;
    
    
    /** map from the token to the result */
    
    private static final Map<String, Result> tokenMap;
    
    
    static
    {
      tokenMap = new HashMap<String, Result> ();
      
      for (Result r: values ()) 
        tokenMap.put (r.denicToken.toLowerCase (), r);
    }
    
    /**
     *  constructor
     *
     *  @param token               the token that is used in DENIC messages
     *                             for representing the level
     */
    
    private Result (String token)
    {
      denicToken = token;
    }
 
    
    /**
     *  returns the DENIC token
     *
     *  @return                     the token
     */
    
    public String getDenicToken ()
    {
      return denicToken;
    }
    
    
    
    /**
     *  returns the result for the given token
     *
     *  @param denicToken           the token
     *  @return                     the result, or {@code null} if the
     *                              token is not found
     */
    
    public static Result fromToken (String denicToken)
    {
      return tokenMap.get (denicToken.toLowerCase ());
    }
  }
  
  
  
}

