//______________________________________________________________________________
//   |       |
//   | 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.3 (15112), 2011/05/27 16:00:14
//                        $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 java.util.regex.Pattern;
import java.util.regex.Matcher;

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

import de.knipp.rritoolkit.ResponseParseException;

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

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


/**
 *  this class encapsulates result messages contained in the DENIC responses
 *
 *  @version  $Revision: 14936 $
 *  @author   Klaus Malorny
 */

public class ResultMessage
{
  /** the level of the message */
  
  private Level level;
  
  
  /** the error code */
  
  private ErrorCode code;
  
  
  /** the textual message */
  
  private String message;
  
  
  /** the arguments */
  
  private List<String> arguments;
  
  
  /** the pattern for the value part */
  
  private static Pattern messagePattern = Pattern.compile ("(\\d{11})\\s+(.*)");
  
  
  
  /**
   *  constructor for key-value pairs
   *
   *  @param key                      the key
   *  @param levelFromKey             if {@code true}, the level is derived
   *                                  from the key, otherwise it is derived
   *                                  from the message code itself
   *  @param value                    the code and message
   *  @throws ResponseParseException  if the key and value do not represent a
   *                                  message
   */
  
  public ResultMessage (String key, boolean levelFromKey, String value)
    throws ResponseParseException
  {
    Matcher m = messagePattern.matcher (value);
    
    if (!m.matches ())
      throw new ResponseParseException ("message malformed", key, value);
    
    code = new ErrorCode (m.group (1));
    message = m.group (2);
    arguments = Collections.emptyList ();  // no arguments on key-value

    if (levelFromKey)
    {
      level = Level.fromToken (key);

      if (level == null)
        throw new ResponseParseException ("invalid error level", key, value);

    } else
    {
      level = Level.fromErrorClass (code.getErrorClass ());
      
      if (level == null)
        throw new ResponseParseException ("invalid error class \"" + 
          code.getErrorClass () + "\"", key, value);
    }    
  }
  
  
  
  
  /**
   *  constructor for XML
   *
   *  @param el                       the message element
   *  @throws DOMException            on DOM exceptions
   *  @throws ResponseParseException  if the key and value do not represent a
   *                                  message
   */
  
  public ResultMessage (Element el)
    throws DOMException, ResponseParseException
  {
    Attr levelAttr = el.getAttributeNode ("level");
    
    if (levelAttr == null) 
      throw new ResponseParseException ("\"level\" attribute missing", el);
    
    level = Level.fromToken (levelAttr.getValue ());
    
    if (level == null)
      throw new ResponseParseException ("invalid error level", levelAttr);
    
    String temp = el.getAttribute ("code");
    
    if (temp == null)
      throw new ResponseParseException ("\"code\" attribute missing", el);

    if (temp.length () != 11)
      throw new ResponseParseException ("invalid length of error code", el);

    code = new ErrorCode (temp);
    
    Element text = getFirstElement (el, NS_TXN, "text");
    
    if (text == null)
      throw new ResponseParseException ("\"text\" element missing", el);
    
    message = text.getTextContent ();
    arguments = new ArrayList<String> ();
    
    for (Element arg: getElements (el, NS_TXN, "argument"))
      arguments.add (arg.getTextContent ());
  }

  
  
  /**
   *  return a string representation for debugging purposes
   *
   *  @return                 the string
   */
  
  @Override
  public String toString ()
  {
    StringBuilder sb = new StringBuilder ();
    sb.append ("ResultMessage [");
    sb.append (level.getDenicToken ());
    sb.append (" ");
    sb.append (code);
    sb.append (" \"");
    sb.append (message);
    sb.append ('\"');
    
    if (!arguments.isEmpty ())
    {
      sb.append (", arguments = (");
      boolean first = true;
      
      for (String arg: arguments)
      {
        if (first)
          first = false;
         else
          sb.append (", ");
        
        sb.append ('\"');
        sb.append (arg);
        sb.append ('\"');
      }
      
      sb.append (')');
    }
    
    sb.append (']');
    
    return sb.toString ();
  }
  
  
  
  //--- access methods ---------------------------------------------------------
  
  /**
   *  return the error level
   *
   *  @return                   the level
   */
  
  public Level getLevel ()
  {
    return level;
  }
  
  
  
  /**
   *  return the message code
   *
   *  @return                   the message code
   */
  
  public ErrorCode getCode ()
  {
    return code;
  }
  
  

  
  
  /**
   *  return the message text
   *
   *  @return                   the message text
   */
  
  public String getMessage ()
  {
    return message;
  }
  
  
  /**
   *  return the arguments
   *
   *  @return                   the arguments as a list
   */
  
  public List<String> getArguments ()
  {
    return arguments;
  }
  
  
  
  
  /**
   *  enumeration for the different error levels of a message
   *
   *  @version  $Revision: 14936 $
   *  @author   Klaus Malorny
   */
  
  public static enum Level
  {
    /** informational result */

    Info ("info",
      ErrorCode.ECLASS_INFO_FAILED,
      ErrorCode.ECLASS_INFO_SUCCESS),


    /** warning result */

    Warning ("warning",
      ErrorCode.ECLASS_WARNING_TEMPORARY,
      ErrorCode.ECLASS_WARNING_PERMANENT),


    /** error result */

    Error ("error",
      ErrorCode.ECLASS_ERROR_TEMPORARY,
      ErrorCode.ECLASS_ERROR_PERMANENT),


    /** fatal error result */

    FatalError ("fatalerror", 
      ErrorCode.ECLASS_FATAL_ERROR_TEMPORARY, 
      ErrorCode.ECLASS_FATAL_ERROR_PERMANENT);


    
    /** the DENIC token */
    
    private String denicToken;
    
    
    /** the class codes */
    
    private String[] classCodes;
    
    
    /** map from the token to the level */
    
    private static final Map<String, Level> tokenMap;
    
    
    /** map from the error classes to the level */
    
    private static final Map<String, Level> classMap;
    
    
    static
    {
      tokenMap = new HashMap<String, Level> ();
      classMap = new HashMap<String, Level> ();
      
      for (Level level: values ()) 
      {
        tokenMap.put (level.denicToken.toLowerCase (), level);
        
        for (String code: level.classCodes)
          classMap.put (code, level);
      }
    }
    
    /**
     *  constructor
     *
     *  @param token               the token that is used in DENIC messages
     *                             for representing the level
     *  @param codes               the class codes ({@code ECLASS_}
     *                             constants defined in {@code ErrorCode})
     */
    
    private Level (String token, String... codes)
    {
      denicToken = token;
      classCodes = codes;
    }
 
    
    /**
     *  return the DENIC token
     *
     *  @return                     the token
     */
    
    public String getDenicToken ()
    {
      return denicToken;
    }
    
    
    
    /**
     *  return the level for the given token
     *
     *  @param denicToken           the token
     *  @return                     the level or {@code null} if the
     *                              token is not found
     */
    
    public static Level fromToken (String denicToken)
    {
      return tokenMap.get (denicToken.toLowerCase ());
    }
    
    
    /**
     *  return the level for the given error class
     *
     *  @param errorClass          the error class
     *  @return                    the level or {@code null} if the class
     *                             is not found
     */
    
    public static Level fromErrorClass (String errorClass)
    {
      return classMap.get (errorClass);
    }
    
  }
  
}

