/*
 * 	    document.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$
 * 
 */
#include "document.h"
#include <fcntl.h>
#include <math.h>
#include "page.h"
#include "errlog.h"
#include "request.h"

/*
 * Constructeur - Destructeur
 * Init - Uninit
 */
Document::Document()
{
    _raster = NULL;
}

Document::~Document()
{
    if (_raster)
         cupsRasterClose(_raster);
}



/*
 * Ouverture du fichier contenant la requête
 * Open the file which contains the job
 */
bool Document::load(const Request& request)
{
    _currentPage = 1;
    _lastPage = false;
    _raster = cupsRasterOpen(STDIN_FILENO, CUPS_RASTER_READ);
    if (!_raster) {
        ERRORMSG(_("Cannot open job"));
        return false;
    }
    return true;
}



/*
 * Extraction d'une nouvelle page de la requête
 * Exact a new job page
 */
Page* Document::getNextRawPage(const Request& request)
{
    cups_page_header_t header;
    unsigned long pageWidth, pageWidthInB, pageHeight, clippingX=0, clippingY=0;
    unsigned long documentWidth, documentHeight, lineSize, planeSize, index=0;
    unsigned long bytesToCopy, marginWidthInB=0, marginHeight=0;
    unsigned char *line, *planes[4];
    unsigned char colors;
    Page *page;

    // Read the header
    if (_lastPage)
        return NULL;
    if (!_raster) {
        ERRORMSG(_("The raster hasn't been loaded"));
        return NULL;
    }
    if (!cupsRasterReadHeader(_raster, &header) || !header.cupsBytesPerLine ||
        !header.PageSize[1]) {
        DEBUGMSG(_("No more pages"));
        _lastPage = true;
        return NULL;
    }

    // Make some calculations and store important data
    page = new Page;
    page->setXResolution(header.HWResolution[0]);
    page->setYResolution(header.HWResolution[1]);
    colors = header.cupsColorSpace == CUPS_CSPACE_K ? 1 : 4;
    documentWidth = (header.cupsWidth + 7) & ~7;
    documentHeight = header.cupsHeight;
    lineSize = header.cupsBytesPerLine / colors;
    pageWidth = ((unsigned long)ceil(page->convertToXResolution(request.
        printer()->pageWidth())) + 7) & ~7;
    pageHeight = ceil(page->convertToYResolution(request.printer()->
        pageHeight()));
    marginWidthInB =(ceil(page->convertToXResolution(header.Margins[0]))+7)/8; 
    marginHeight = ceil(page->convertToYResolution(header.Margins[1]));
    pageWidthInB = (pageWidth + 7) / 8;
    planeSize = pageWidthInB * pageHeight;
    page->setWidth(pageWidth);
    page->setHeight(pageHeight);
    page->setColorsNr(colors);
    page->setPageNr(_currentPage);
    page->setCompression(header.cupsCompression);
    page->setCopiesNr(header.NumCopies);

    // Calculate clippings and margins
    if (lineSize > pageWidthInB - 2 * marginWidthInB) {
        clippingX = (lineSize - (pageWidthInB - 2 * marginWidthInB)) / 2;
        bytesToCopy = pageWidthInB - 2 * marginWidthInB;
    } else {
        clippingX = 0;
        marginWidthInB = (pageWidthInB - lineSize) / 2;
        bytesToCopy = lineSize;
    }

    if (documentHeight > pageHeight - 2 * marginHeight) {
        clippingY = (documentHeight - (pageHeight - 2 * marginHeight)) / 2;
        index = pageWidthInB * marginHeight;
    } else {
        clippingY = 0;
        index = pageWidthInB * ((pageHeight - documentHeight)/2);
    }
    documentHeight -= clippingY;
    pageHeight -= 2*marginHeight;
    clippingY *= colors;
    line = new unsigned char[header.cupsBytesPerLine];
    DEBUGMSG(_("Document width=%lu height=%lu"), documentWidth, documentHeight);
    DEBUGMSG(_("Page width=%lu (%lu) height=%lu"), pageWidth, pageWidthInB, pageHeight);
    DEBUGMSG(_("Margin width in bytes=%lu height=%lu"), marginWidthInB, marginHeight);
    DEBUGMSG(_("Clipping X=%lu Y=%lu"), clippingX, clippingY);
    DEBUGMSG(_("Line size=%lu, Plane size=%lu, bytes to copy=%lu"), lineSize, planeSize, bytesToCopy);

    // Prepare planes and clip vertically the document if needed
    for (unsigned char i=0; i < colors; i++) {
        planes[i] = new unsigned char[planeSize];
        memset(planes[i], 0, planeSize);
    }
    while (clippingY) {
        if (cupsRasterReadPixels(_raster, line, lineSize) < 1) {
            ERRORMSG(_("Cannot read pixel line"));
            for (unsigned int i=0; i < colors; i++)
                delete[] planes[i];
            delete[] line;
            delete page;
            return NULL;
        }
        clippingY--;
    }

    // Load the bitmap
    while (pageHeight && documentHeight) {
        for (unsigned int i=0; i < colors; i++) {
            if (cupsRasterReadPixels(_raster, line, lineSize) < 1) {
                ERRORMSG(_("Cannot read pixel line"));
                for (unsigned int j=0; j < colors; j++)
                    delete[] planes[j];
                delete[] line;
                delete page;
                return NULL;
            }
            memcpy(planes[i] + index + marginWidthInB, line + clippingX, 
                bytesToCopy);
        }
        index += pageWidthInB;
        pageHeight--;
        documentHeight--;
    }

    // Finish to clip vertically the document
    documentHeight *= colors;
    while (documentHeight) {
        if (cupsRasterReadPixels(_raster, line, lineSize) < 1) {
            ERRORMSG(_("Cannot read pixel line"));
            for (unsigned int j=0; j < colors; j++)
                delete[] planes[j];
            delete[] line;
            delete page;
            return NULL;
        }
        documentHeight--;
    }
    _currentPage++;

    for (unsigned int i=0; i < colors; i++)
        page->setPlaneBuffer(i, planes[i]);

    DEBUGMSG(_("Page %lu (%u×%u on %lu×%lu) has been successfully loaded into "
        "memory"), page->pageNr(), header.cupsWidth, header.cupsHeight, 
        page->width(), page->height());

    delete[] line;
    return page;
}



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

