/*
 * 	    page.cpp                  (C) 2006-2008, Aurélien Croc (AP²C)
 *
 *  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; version 2 of the License.
 * 
 *  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, write to the
 *  Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 *  $Id: page.cpp 301 2012-02-29 14:11:09Z tillkamppeter $
 * 
 */
#include "page.h"
#include <unistd.h>
#include <string.h>
#include "band.h"
#include "errlog.h"

/*
 * This magic formula reverse the bit of a byte. ie. the bit 1 becomes the 
 * bit 8, the bit 2 becomes the bit 7 etc.
 */
#define REVERSE_BITS(N) ((N * 0x0202020202ULL & 0x010884422010ULL) % 1023)

/*
 * Constructeur - Destructeur
 * Init - Uninit 
 */
Page::Page()
{
    _empty = true;
    _xResolution = 0;
    _yResolution = 0;
    _planes[0] = NULL;
    _planes[1] = NULL;
    _planes[2] = NULL;
    _planes[3] = NULL;
    _firstBand = NULL;
    _lastBand = NULL;
    _bandsNr = 0;
    _bih = NULL;
}

Page::~Page()
{
    flushPlanes();
    if (_firstBand)
        delete _firstBand;
    if (_bih)
        delete[] _bih;
}



/*
 * Enregistrement d'une nouvelle bande
 * Register a new band
 */
void Page::registerBand(Band *band)
{
    if (_lastBand)
        _lastBand->registerSibling(band);
    else
        _firstBand = band;
    _lastBand = band;
    band->registerParent(this);
    _bandsNr++;
}



/*
 * Rotation des couches
 * Rotate bitmaps planes
 */
void Page::rotate()
{
    unsigned long size, midSize;
    unsigned char tmp;

    size  = _width * _height / 8;
    midSize = size / 2;

    for (unsigned int i=0; i < _colors; i++) {
        for (unsigned long j=0; j < midSize; j++) {
            tmp = _planes[i][j];
            _planes[i][j] = REVERSE_BITS(_planes[i][size - j - 1]);
            _planes[i][size - j - 1] = REVERSE_BITS(tmp);
        }
    }
}



/*
 * Libération de la mémoire utilisée par les couches
 * Flush the planes
 */
void Page::flushPlanes()
{
    for (unsigned int i=0; i < 4; i++) {
        if (_planes[i]) {
            delete[] _planes[i];
            _planes[i] = NULL;
        }
    }
    _empty = false;
}



/*
 * Mise sur disque / Rechargement
 * Swapping / restoring
 */
bool Page::swapToDisk(int fd)
{
    unsigned long i;
    Band* band;

    if (_planes[0] || _planes[1] || _planes[2] || _planes[3]) {
        ERRORMSG(_("Cannot swap page instance which still contains bitmap "
            "representation"));
        return false;
    }
    write(fd, &_xResolution, sizeof(_xResolution));
    write(fd, &_yResolution, sizeof(_yResolution));
    write(fd, &_width, sizeof(_width));
    write(fd, &_height, sizeof(_height));
    write(fd, &_colors, sizeof(_colors));
    write(fd, &_pageNr, sizeof(_pageNr));
    write(fd, &_copiesNr, sizeof(_copiesNr));
    write(fd, &_compression, sizeof(_compression));
    write(fd, &_empty, sizeof(_empty));
    write(fd, &_bandsNr, sizeof(_bandsNr));
    /* Carefully check if there is BIH data and compression type is 0x15,
       before saving BIH data to file. */
    if (( 0x15 == _compression ) && ( _bandsNr > 0 ) && ( NULL != _bih ))
        write(fd, _bih, 20);
    for (i=0, band = _firstBand; i < _bandsNr; i++) {
        if (!band->swapToDisk(fd))
            return false;
        band = band->sibling();
    }

    return true;
}

Page* Page::restoreIntoMemory(int fd)
{
    unsigned long nr;
    Page* page;

    page = new Page();
    read(fd, &page->_xResolution, sizeof(page->_xResolution));
    read(fd, &page->_yResolution, sizeof(page->_yResolution));
    read(fd, &page->_width, sizeof(page->_width));
    read(fd, &page->_height, sizeof(page->_height));
    read(fd, &page->_colors, sizeof(page->_colors));
    read(fd, &page->_pageNr, sizeof(page->_pageNr));
    read(fd, &page->_copiesNr, sizeof(page->_copiesNr));
    read(fd, &page->_compression, sizeof(page->_compression));
    read(fd, &page->_empty, sizeof(page->_empty));
    read(fd, &nr, sizeof(nr));
    /* Check if compression type is 0x15 and that there is at least one
       image band before reading BIH data. */
    if (( 0x15 == page->_compression ) && ( nr > 0 )) {
        unsigned char bih[20];
        read(fd, bih, 20);
        page->setBIH(bih);
    }
    for (unsigned int i=0; i < nr; i++) {
        Band *band = Band::restoreIntoMemory(fd);
        if (!band) {
            delete page;
            return NULL;
        }
        page->registerBand(band);
    }

    return page;
}

void Page::setBIH(const unsigned char *bih_data) {
    if (NULL == _bih)
        _bih = new unsigned char[20];
    memcpy(_bih, bih_data, 20);
}

/* vim: set expandtab tabstop=4 shiftwidth=4 smarttab tw=80 cin enc=utf8: */

