//
// bitwriter  --  wrapper class for bit output
//
// Copyright (c) 2003 by Joergen Ibsen / Jibz
// All Rights Reserved
//
// http://www.ibsensoftware.com/
//

/// @file   bitwriter.hpp
/// @brief  Base class for bit output wrappers.
/// @author Jibz
/// @date   2003.08.02

#ifndef BITWRITER_HPP_INCLUDED
#define BITWRITER_HPP_INCLUDED

#include <string>
#include <cstddef>
#include <climits>

/// Base class for bit output wrappers.
///
/// The bitwriter class is a wrapper which provides bit output functionality
/// to anything supporting output of 8-bit chars (bytes). It attempts to be
/// as clean and transparent as possible, while maintaining a fairly decent
/// efficiency.
///
/// The base class contains functions for handling the output by collecting
/// bits in a temporary store (a byte), writing them out when the store is
/// full.
///
/// It is used by creating a sub-class and implementing the
/// write_next_byte() member function.
///
/// The destructor of the derived class should call finalize().
///
/// @ingroup bitio
///
class bitwriter {

public:
    /// Constructor.
    bitwriter() : num(0), current(0), mask(0x80) { ; }

    /// Destructor. Derived classes should implement a destructor
    /// which calls the finalize() member function.
    virtual ~bitwriter() { ; }

public:
    /// Check for error.
    /// @return @c true if an error occurred.
    virtual bool error() const { return false; }

    /// Align bitwriter on a byte boundary.
    void align_byte()
    {
	if (mask != 0x80)
	{
	    ++num; write_next_byte(current);
	    current = 0;
	    mask = 0x80;
	}
    }

public:
    /// @name Size Functions
    /// @{

    /// Return the number of bits written.
    /// @return the number of bits written by the bitwriter.
    size_t bits_done() const { return 8*num + 8 - log2_tab[mask]; }
    /// Return the number of bytes written.
    /// @return the number of bytes written by the bitwriter.
    size_t bytes_done() const { return num; }

    /// Return the current bit position within the bit stream.
    /// @return the current bit position.
    size_t tell_bit() const { return bits_done(); }

    /// Return the current byte position within the bit stream.
    /// @return the current byte position.
    size_t tell_byte() const { return bytes_done(); }

    /// @}

public:
    /// @name Output Functions
    /// @{

    /// Write one bit to the output.
    /// @param v - bit value to write (@c true for 1, @c false for 0).
    void put_bit(bool v)
    {
	if (v) current |= mask;
	if (!(mask >>= 1))
	{
	    ++num; write_next_byte(current);
	    current = 0;
	    mask = 0x80;
	}
    }

    /// Write one byte (8-bit unsigned value).
    /// @param v - byte value to write.
    void put_byte(unsigned char v)
    {
	++num;

	if (mask == 0x80) { write_next_byte(v); return; }

	unsigned char ml = log2_tab[mask];
	current |= v >> (8 - ml);
	write_next_byte(current);
	current = v << ml;
    }

    /// Write one word (16-bit unsigned value).
    /// @param v - word value to write.
    void put_word(unsigned short v)
    {
	put_byte((v >> 8) & 0x00ff);
	put_byte((v     ) & 0x00ff);
    }

    /// Write one dword (32-bit unsigned value).
    /// @param v - dword value to write.
    void put_dword(unsigned long v)
    {
	put_byte((unsigned char) ((v >> 24) & 0x00ff));
	put_byte((unsigned char) ((v >> 16) & 0x00ff));
	put_byte((unsigned char) ((v >> 8 ) & 0x00ff));
	put_byte((unsigned char) ((v      ) & 0x00ff));
    }

    /// Write one float.
    /// @param v - float value to write.
    /// @warning Experimental, not portable.
    /// @todo    Experimental, not portable.
    void put_float(float v) { write((char*)&v, sizeof(float)); }

    /// Write one double.
    /// @param v - double value to write.
    /// @warning Experimental, not portable.
    /// @todo    Experimental, not portable.
    void put_double(double v) { write((char*)&v, sizeof(double)); }

    /// Write a string.
    /// @param s - the string to write.
    void put_string(std::string &s)
    {
	size_t len = s.length();

	if ((len > 0x0000ffff) || (len == 0))
	{
	    put_word(0);
	    put_dword(len);
	} else {
	    put_word(len);
	}

	write(s.c_str(), len);
    }

    /// Copy bytes from an array to the bitwriter.
    /// @param buffer - pointer to where the bytes should be read.
    /// @param length - the number of bytes to copy.
    void write(const char *buffer, size_t length)
    {
	for ( ; length; --length) put_byte(*buffer++);
    }

    /// @}

public:
    /// @name Polymorphic Output Function Wrappers
    /// @{

    void put(bool v) { put_bit(v); }
    void put(char v) { put_byte(v); }
    void put(unsigned char v) { put_byte(v); }
    void put(short v) { put_word(v); }
    void put(unsigned short v) { put_word(v); }
#if UINT_MAX == 0xffff
    void put(int v) { put_word(v); }
    void put(unsigned int v) { put_word(v); }
#else
    void put(int v) { put_dword(v); }
    void put(unsigned int v) { put_dword(v); }
#endif
    void put(long v) { put_dword(v); }
    void put(unsigned long v) { put_dword(v); }

    void put(float v) { put_float(v); }
    void put(double v) { put_double(v); }
    void put(std::string &s) { put_string(s); }

    /// @}

protected:
    /// Perform cleanup. Derived classes should call this method
    /// from their destructor.
    void finalize() { align_byte(); }

    /// Defines the method of writing a single byte of output.
    /// Derived classes should implement this function.
    /// @param c - the 8 bits to write to the output.
    virtual void write_next_byte(char c) = 0;

    size_t num;  ///< Number of bytes written.

private:
    unsigned char current;  ///< The current output byte.
    unsigned char mask;     ///< Mask to access the current bit.

    /// Table for looking up the position of the MSB in a byte.
    static const unsigned char log2_tab[256];
};

#endif // BITWRITER_HPP_INCLUDED
