// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// 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 (at your option) 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
//

package net.sourceforge.jtds.util;

import java.sql.*;
import java.io.PrintWriter;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Class providing static methods to log diagnostics.
 * <p>
 * There are three ways to enable logging:
 * <ol>
 * <li>Pass a valid PrintWriter to DriverManager.setLogWriter().
 * <li>Pass a valid PrintWriter to DataSource.setLogWriter().
 * <li>For backwards compatibility call Logger.setActive();
 * </ol>
 *
 * @author
 *    Mike Hutchinson, Holger Rehn
 */
public class Logger {
    /** PrintWriter stream set by DataSource. */
    private static PrintWriter log;

    /**
     * Set the logging PrintWriter stream.
     *
     * @param out the PrintWriter stream
     */
    public static void setLogWriter(PrintWriter out) {
        log = out;
    }

    /**
     * Get the logging PrintWriter Stream.
     *
     * @return the logging stream as a <code>PrintWriter</code>
     */
    public static PrintWriter getLogWriter() {
        return log;
    }

    /**
     * Retrieve the active status of the logger.
     *
     * @return <code>boolean</code> true if logging enabled
     */
    public static boolean isActive() {
        return(log != null || DriverManager.getLogWriter() != null);
    }

   /**
    * <p> Print a diagnostic message to the output stream provided by the
    * {@link DataSource} or the {@link DriverManager}. </p>
    *
    * @param message
    *    the diagnostic message to print
    */
   public static void println( String message )
   {
      if( log != null )
      {
         log.println( message );
      }
      else
      {
         // get writer, DriverManager isn't thread-safe (see bug #694)
         PrintWriter pw = DriverManager.getLogWriter();

         if( pw != null )
         {
            pw.println( message );
            pw.flush();
         }
      }
   }

    private static final char hex[] =
    {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

    /**
     * Print a dump of the current input or output network packet.
     *
     * @param streamId the owner of this packet
     * @param in       true if this is an input packet
     * @param pkt      the packet data
     */
    public static void logPacket(int streamId, boolean in, byte[] pkt) {
        int len = ((pkt[2] & 0xFF) << 8)| (pkt[3] & 0xFF);

        StringBuffer line = new StringBuffer(80);

        line.append("----- Stream #");
        line.append(streamId);
        line.append(in ? " read" : " send");
        line.append((pkt[1] != 0) ? " last " : " ");

        switch (pkt[0]) {
            case 1:
                line.append("Request packet ");
                break;
            case 2:
                line.append("Login packet ");
                break;
            case 3:
                line.append("RPC packet ");
                break;
            case 4:
                line.append("Reply packet ");
                break;
            case 6:
                line.append("Cancel packet ");
                break;
            case 14:
                line.append("XA control packet ");
                break;
            case 15:
                line.append("TDS5 Request packet ");
                break;
            case 16:
                line.append("MS Login packet ");
                break;
            case 17:
                line.append("NTLM Authentication packet ");
                break;
            case 18:
                line.append("MS Prelogin packet ");
                break;
            default:
                line.append("Invalid packet ");
                break;
        }

        println(line.toString());
        println("");
        line.setLength(0);

        for (int i = 0; i < len; i += 16) {
            if (i < 1000) {
                line.append(' ');
            }

            if (i < 100) {
                line.append(' ');
            }

            if (i < 10) {
                line.append(' ');
            }

            line.append(i);
            line.append(':').append(' ');

            int j = 0;

            for (; j < 16 && i + j < len; j++) {
                int val = pkt[i+j] & 0xFF;

                line.append(hex[val >> 4]);
                line.append(hex[val & 0x0F]);
                line.append(' ');
            }

            for (; j < 16 ; j++) {
                line.append("   ");
            }

            line.append('|');

            for (j = 0; j < 16 && i + j < len; j++) {
                int val = pkt[i + j] & 0xFF;

                if (val > 31 && val < 127) {
                    line.append((char) val);
                } else {
                    line.append(' ');
                }
            }

            line.append('|');
            println(line.toString());
            line.setLength(0);
        }

        println("");
    }

   /**
    * Print an Exception stack trace to the log.
    *
    * @param e
    * the exception to log
    */
   public static void logException( Exception e )
   {
      if( log != null )
      {
         e.printStackTrace( log );
      }
      else
      {
         // get writer, DriverManager isn't thread-safe (see bug #694)
         PrintWriter pw = DriverManager.getLogWriter();

         if( pw != null )
         {
            e.printStackTrace( pw );
            pw.flush();
         }
      }
   }

    //
    // Backward compatibility method
    //

    /**
     * Turn the logging on or off.
     *
     * @deprecated Use the JDBC standard mechanisms to enable logging.
     * @param value  true to turn on logging
     */
    public static void setActive(boolean value) {
        if (value && log == null) {
            try {
                log = new PrintWriter(new FileOutputStream("log.out"), true);
            } catch (IOException e) {
                log = null; // Sorry no logging!
            }
        }
    }
}