/*
 *      fhist - file history and comparison tools
 *      Copyright (C) 2000, 2002, 2008 Peter Miller
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *
 *      This program 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 General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program. If not, see
 *      <http://www.gnu.org/licenses/>.
 */

#include <ac/ctype.h>
#include <ac/stdio.h> /* for sprintf */

#include <error.h>
#include <input/hexify.h>
#include <input/private.h>
#include <trace.h>


typedef struct input_hexify_ty input_hexify_ty;
struct input_hexify_ty
{
        input_ty        inherited;
        input_ty        *deeper;
        int             delete_on_close;
        int             buf_pos;
        char            buffer[16];
};


static void
destruct(input_ty *p)
{
        input_hexify_ty *this;

        trace(("input_hexify::destruct()\n{\n"));
        this = (input_hexify_ty *)p;
        input_pushback_transfer(this->deeper, p);
        if (this->delete_on_close)
                input_delete(this->deeper);
        this->deeper = 0; /* paranoia */
        trace(("}\n"));
}


static int
get(input_ty *p)
{
        input_hexify_ty *this;
        int             c;

        trace(("input_hexify::get()\n{\n"));
        this = (input_hexify_ty *)p;
        if (this->buffer[this->buf_pos] == 0)
        {
                char *ctrl = "";
                char *meta = "";
                int c2;
                c = input_getc(this->deeper);
                if (c < 0)
                {
                        trace(("return EOF;\n"));
                        return -1;
                }
                c2 = c;
                if (c2 & 0x80)
                {
                        meta = "M-";
                        c2 &= 0x7F;
                }
                if (!isprint(c2))
                {
                        ctrl = "^";
                        c2 ^= 0x40;
                }
                sprintf(this->buffer, "0x%02X  %s%s%c\n", c, meta, ctrl, c2);
                this->buf_pos = 0;
        }
        c = this->buffer[this->buf_pos++];
        trace(("return %d;\n", c));
        trace(("}\n"));
        return c;
}


static long
itell(input_ty *fp)
{
        input_hexify_ty *this;

        this = (input_hexify_ty *)fp;
        trace(("input_hexify::tell => %ld\n", this->buf_pos));
        return input_ftell(this->deeper);
}


static const char *
name(input_ty *p)
{
        input_hexify_ty *this;

        trace(("input_hexify::name\n"));
        this = (input_hexify_ty *)p;
        return input_name(this->deeper);
}


static long
length(input_ty *p)
{
        trace(("input_hexify::length => -1\n"));
        return -1;
}


static input_vtbl_ty vtbl =
{
        sizeof(input_hexify_ty),
        destruct,
        input_generic_read,
        get,
        itell,
        name,
        length,
};


input_ty *
input_hexify(input_ty *deeper, int delete_on_close)
{
        input_ty        *result;
        input_hexify_ty *this;

        trace(("input_hexify(fp = %08lX)\n{\n", (long)deeper));
        result = input_new(&vtbl);
        this = (input_hexify_ty *)result;
        this->deeper = deeper;
        this->delete_on_close = delete_on_close;
        this->buf_pos = 0;
        this->buffer[0] = 0;
        trace(("return %08lX\n", (long)result));
        trace(("}\n"));
        return result;
}
