/*
 * Copyright (c) 2004-2017, by Alex Leigh.
 * All rights reserved.
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE
 * The copyright notice above does not evidence any
 * actual or intended publication of such source code.
 */

package lc.mecha.util;

import lc.mecha.log.MechaLogger;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

/**
 * Utility class for producing hex dump listing.
 *
 * @author Alex Leigh
 */
public class HexDump {
    private static final char[] HEX_DIGITS = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    };

    public static void hexDump(String s, Charset charset) throws UnsupportedEncodingException {
        byte[] buf = s.getBytes(charset);
        hexDump(buf, 0, buf.length);
    }

    public static void hexDump(byte[] buf, int off, int len) {
        System.out.println(formatHexString(buf, off, len));
    }

    /**
     * Log a multi-line hex dump of the provided buffer to the provided logger.
     */
    public static void logHexDump(MechaLogger logger, byte[] buf, int off, int len) {
        String s = formatHexString(buf, off, len);

        String msg[] = s.split("\n");
        for (String aMsg : msg) {
            logger.info("hex: {}", aMsg);
        }
    }

    /**
     * Format the provided buffer into a multi-line hex-dump string, and return it to the caller.
     */
    public static String formatHexString(final byte[] buf, final int off, final int len) {
        StringBuffer output = new StringBuffer();
        int i, j, jmax;
        int c;

        for (i = 0; i < len; i += 0x10) {
            String line = formatHex(i + off, 8) + ": ";
            jmax = len - i;
            jmax = jmax > 16 ? 16 : jmax;

            for (j = 0; j < jmax; j++) {
                c = (buf[i + j] + 0x100) % 0x100;

                line += formatHex(c, 2);

                if ((j % 2) == 1) {
                    line += " ";
                }
            }

            while (j < 16) {
                line += "  ";

                if ((j % 2) == 1) {
                    line += " ";
                }
                j++;
            }

            line += " ";

            for (j = 0; j < jmax; j++) {
                c = (buf[i + j] + 0x100) % 0x100;

                c = (c < 32) || (c >= 127) ? '.' : c;

                line += (char) c;
            }

            output.append(line);
            output.append('\n');
        }
        return output.toString();
    }

    private static String formatHex(final int i, final int sz) {
        String str = Integer.toHexString(i);

        while (str.length() < sz) {
            str = "0" + str;
        }

        return str;
    }

    /**
     * Returns a string of 8 hexadecimal digits (most significant digit first) corresponding to the integer <i>n</i>,
     * which is treated as unsigned.
     */
    public static String intToString(final int n) {
        int n1 = n;
        final char[] buf = new char[8];

        for (int i = 7; i >= 0; i--) {
            buf[i] = HEX_DIGITS[n1 & 0x0F];

            n1 >>>= 4;
        }

        return new String(buf);
    }
}
