/*
 * qrencode - QR Code encoder
 *
 * Binary sequence class.
 * Copyright (C) 2006-2017 Kentaro Fukuchi <kentaro@fukuchi.org>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bitstream.h"

#define DEFAULT_BUFSIZE (128)

BitStream *BitStream_new(void)
{
	BitStream *bstream;

	bstream = (BitStream *)malloc(sizeof(BitStream));
	if(bstream == NULL) return NULL;

	bstream->length = 0;
	bstream->data = (unsigned char *)malloc(DEFAULT_BUFSIZE);
	if(bstream->data == NULL) {
		free(bstream);
		return NULL;
	}
	bstream->datasize = DEFAULT_BUFSIZE;

	return bstream;
}

#ifdef WITH_TESTS
BitStream *BitStream_newWithBits(size_t size, unsigned char *bits)
{
	BitStream *bstream;

	if(size == 0) return BitStream_new();

	bstream = (BitStream *)malloc(sizeof(BitStream));
	if(bstream == NULL) return NULL;

	bstream->data = (unsigned char *)malloc(size);
	if(bstream->data == NULL) {
		free(bstream);
		return NULL;
	}

	bstream->length = size;
	bstream->datasize = size;
	memcpy(bstream->data, bits, size);

	return bstream;
}
#endif

static int BitStream_expand(BitStream *bstream)
{
	unsigned char *data;

	data = (unsigned char *)realloc(bstream->data, bstream->datasize * 2);
	if(data == NULL) {
		return -1;
	}

	bstream->data = data;
	bstream->datasize *= 2;

	return 0;
}

static void BitStream_writeNum(unsigned char *dest, size_t bits, unsigned int num)
{
	unsigned int mask;
	size_t i;
	unsigned char *p;

	p = dest;
	mask = 1U << (bits - 1);
	for(i = 0; i < bits; i++) {
		if(num & mask) {
			*p = 1;
		} else {
			*p = 0;
		}
		p++;
		mask = mask >> 1;
	}
}

static void BitStream_writeBytes(unsigned char *dest, size_t size, unsigned char *data)
{
	unsigned char mask;
	size_t i, j;
	unsigned char *p;

	p = dest;
	for(i = 0; i < size; i++) {
		mask = 0x80;
		for(j = 0; j < 8; j++) {
			if(data[i] & mask) {
				*p = 1;
			} else {
				*p = 0;
			}
			p++;
			mask = mask >> 1;
		}
	}
}

int BitStream_append(BitStream *bstream, BitStream *arg)
{
	int ret;

	if(arg == NULL) {
		return -1;
	}
	if(arg->length == 0) {
		return 0;
	}

	while(bstream->length + arg->length > bstream->datasize) {
		ret = BitStream_expand(bstream);
		if(ret < 0) return ret;
	}

	memcpy(bstream->data + bstream->length, arg->data, arg->length);
	bstream->length += arg->length;

	return 0;
}

int BitStream_appendNum(BitStream *bstream, size_t bits, unsigned int num)
{
	int ret;

	if(bits == 0) return 0;

	while(bstream->datasize - bstream->length < bits) {
		ret = BitStream_expand(bstream);
		if(ret < 0) return ret;
	}
	BitStream_writeNum(bstream->data + bstream->length, bits, num);
	bstream->length += bits;

	return 0;
}

int BitStream_appendBytes(BitStream *bstream, size_t size, unsigned char *data)
{
	int ret;

	if(size == 0) return 0;

	while(bstream->datasize - bstream->length < size * 8) {
		ret = BitStream_expand(bstream);
		if(ret < 0) return ret;
	}
	BitStream_writeBytes(bstream->data + bstream->length, size, data);
	bstream->length += size * 8;

	return 0;
}

unsigned char *BitStream_toByte(BitStream *bstream)
{
	size_t i, j, size, bytes, oddbits;
	unsigned char *data, v;
	unsigned char *p;

	size = BitStream_size(bstream);
	if(size == 0) {
		return NULL;
	}
	data = (unsigned char *)malloc((size + 7) / 8);
	if(data == NULL) {
		return NULL;
	}

	bytes = size  / 8;

	p = bstream->data;
	for(i = 0; i < bytes; i++) {
		v = 0;
		for(j = 0; j < 8; j++) {
			v = (unsigned char)(v << 1);
			v |= *p;
			p++;
		}
		data[i] = v;
	}
	oddbits = size & 7;
	if(oddbits > 0) {
		v = 0;
		for(j = 0; j < oddbits; j++) {
			v = (unsigned char)(v << 1);
			v |= *p;
			p++;
		}
		data[bytes] = (unsigned char)(v << (8 - oddbits));
	}

	return data;
}

void BitStream_free(BitStream *bstream)
{
	if(bstream != NULL) {
		free(bstream->data);
		free(bstream);
	}
}