/*
 *  kis_pattern.cpp - part of Krayon
 *
 *  Copyright (c) 2000 Matthias Elter <elter@kde.org>
 *                2001 John Califf
 *                2004 Boudewijn Rempt <boud@valdyas.org>
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
#include "kis_pattern.h"

#include <sys/types.h>
#include <netinet/in.h>

#include <limits.h>
#include <stdlib.h>

#include <tqpoint.h>
#include <tqsize.h>
#include <tqimage.h>
#include <tqvaluevector.h>
#include <tqmap.h>
#include <tqfile.h>

#include <kdebug.h>
#include <tdelocale.h>

#include "kis_color.h"
#include "kis_layer.h"
#include "kis_paint_device.h"

namespace {
    struct GimpPatternHeader {
        TQ_UINT32 header_size;  /*  header_size = sizeof (PatternHeader) + brush name  */
        TQ_UINT32 version;      /*  pattern file version #  */
        TQ_UINT32 width;        /*  width of pattern */
        TQ_UINT32 height;       /*  height of pattern  */
        TQ_UINT32 bytes;        /*  depth of pattern in bytes : 1, 2, 3 or 4*/
        TQ_UINT32 magic_number; /*  GIMP brush magic number  */
    };

    // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT'
    TQ_UINT32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0));
}

KisPattern::KisPattern(const TQString& file) : super(file), m_hasFile(true)
{
}

KisPattern::KisPattern(KisPaintDevice* image, int x, int y, int w, int h)
    : super(""), m_hasFile(false)
{
    // Forcefully convert to RGBA8
    // XXX profile and exposure?
    setImage(image->convertToTQImage(0, x, y, w, h));
    setName(image->name());
}

KisPattern::~KisPattern()
{
}

bool KisPattern::load()
{
    if (!m_hasFile)
        return true;

    TQFile file(filename());
    file.open(IO_ReadOnly);
    TQByteArray data = file.readAll();
    if (!data.isEmpty()) {
        TQ_INT32 startPos = m_data.size();

        m_data.resize(m_data.size() + data.count());
        memcpy(&m_data[startPos], data.data(), data.count());
    }
    file.close();
    return init();
}

bool KisPattern::save()
{
    TQFile file(filename());
    file.open(IO_WriteOnly | IO_Truncate);

    TQTextStream stream(&file);
    // Header: header_size (24+name length),version,width,height,colourdepth of brush,magic,name
    // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA
    // magic = "GPAT", as a single uint32, the docs are wrong here!
    // name is UTF-8 (\0-terminated! The docs say nothing about this!)
    // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!)
    // We only save RGBA at the moment
    // Version is 1 for now...

    GimpPatternHeader ph;
    TQCString utf8Name = name().utf8();
    char const* name = utf8Name.data();
    int nameLength = tqstrlen(name);

    ph.header_size = htonl(sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0
    ph.version = htonl(1);
    ph.width = htonl(width());
    ph.height = htonl(height());
    ph.bytes = htonl(4);
    ph.magic_number = htonl(GimpPatternMagic);

    TQByteArray bytes;
    bytes.setRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
    int wrote = file.writeBlock(bytes);
    bytes.resetRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));

    if (wrote == -1)
        return false;

    wrote = file.writeBlock(name, nameLength + 1); // Trailing 0 apparantly!
    if (wrote == -1)
        return false;

    int k = 0;
    bytes.resize(width() * height() * 4);
    for (TQ_INT32 y = 0; y < height(); y++) {
        for (TQ_INT32 x = 0; x < width(); x++) {
            // RGBA only
            TQRgb pixel = m_img.pixel(x,y);
            bytes[k++] = static_cast<char>(tqRed(pixel));
            bytes[k++] = static_cast<char>(tqGreen(pixel));
            bytes[k++] = static_cast<char>(tqBlue(pixel));
            bytes[k++] = static_cast<char>(tqAlpha(pixel));
        }
    }

    wrote = file.writeBlock(bytes);
    if (wrote == -1)
        return false;

    file.close();

    return true;
}

TQImage KisPattern::img()
{
    return m_img;
}

bool KisPattern::init()
{
    // load Gimp patterns
    GimpPatternHeader bh;
    TQ_INT32 k;
    TQValueVector<char> name;

    if (sizeof(GimpPatternHeader) > m_data.size()) {
        return false;
    }

    memcpy(&bh, &m_data[0], sizeof(GimpPatternHeader));
    bh.header_size = ntohl(bh.header_size);
    bh.version = ntohl(bh.version);
    bh.width = ntohl(bh.width);
    bh.height = ntohl(bh.height);
    bh.bytes = ntohl(bh.bytes);
    bh.magic_number = ntohl(bh.magic_number);

    if (bh.header_size > m_data.size() || bh.header_size == 0) {
        return false;
    }

    name.resize(bh.header_size - sizeof(GimpPatternHeader));
    memcpy(&name[0], &m_data[sizeof(GimpPatternHeader)], name.size());

    if (name[name.size() - 1]) {
        return false;
    }

    setName(i18n(&name[0]));

    if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) {
        return false;
    }

    k = bh.header_size;

    if (bh.bytes == 1) {
        // Grayscale
        TQ_INT32 val;

        for (TQ_UINT32 y = 0; y < bh.height; y++) {
            for (TQ_UINT32 x = 0; x < bh.width; x++, k++) {
                if (static_cast<TQ_UINT32>(k) > m_data.size()) {
                    kdDebug(DBG_AREA_FILE) << "failed in gray\n";
                    return false;
                }

                val = m_data[k];
                m_img.setPixel(x, y, tqRgb(val, val, val));
                m_img.setAlphaBuffer(false);
            }
        }
    } else if (bh.bytes == 2) {
        // Grayscale + A
        TQ_INT32 val;
        TQ_INT32 alpha;
        for (TQ_UINT32 y = 0; y < bh.height; y++) {
            for (TQ_UINT32 x = 0; x < bh.width; x++, k++) {
                if (static_cast<TQ_UINT32>(k + 2) > m_data.size()) {
                    kdDebug(DBG_AREA_FILE) << "failed in grayA\n";
                    return false;
                }

                val = m_data[k];
                alpha = m_data[k++];
                m_img.setPixel(x, y, tqRgba(val, val, val, alpha));
                m_img.setAlphaBuffer(true);
            }
        }
    } else if (bh.bytes == 3) {
        // RGB without alpha
        for (TQ_UINT32 y = 0; y < bh.height; y++) {
            for (TQ_UINT32 x = 0; x < bh.width; x++) {
                if (static_cast<TQ_UINT32>(k + 3) > m_data.size()) {
                    kdDebug(DBG_AREA_FILE) << "failed in RGB\n";
                    return false;
                }

                m_img.setPixel(x, y, tqRgb(m_data[k],
                              m_data[k + 1],
                              m_data[k + 2]));
                k += 3;
                m_img.setAlphaBuffer(false);
            }
        }
    } else if (bh.bytes == 4) {
        // Has alpha
        for (TQ_UINT32 y = 0; y < bh.height; y++) {
            for (TQ_UINT32 x = 0; x < bh.width; x++) {
                if (static_cast<TQ_UINT32>(k + 4) > m_data.size()) {
                    kdDebug(DBG_AREA_FILE) << "failed in RGBA\n";
                    return false;
                }

                m_img.setPixel(x, y, tqRgba(m_data[k],
                               m_data[k + 1],
                               m_data[k + 2],
                               m_data[k + 3]));
                k += 4;
                m_img.setAlphaBuffer(true);
            }
        }
    } else {
        return false;
    }

    if (m_img.isNull()) {
        return false;
    }

    setWidth(m_img.width());
    setHeight(m_img.height());

    setValid(true);

    return true;
}

KisPaintDeviceSP KisPattern::image(KisColorSpace * colorSpace) {
    // Check if there's already a pattern prepared for this colorspace
    TQMap<TQString, KisPaintDeviceSP>::const_iterator it = m_colorspaces.find(colorSpace->id().id());
    if (it != m_colorspaces.end())
        return (*it);

    // If not, create one
    KisPaintDeviceSP layer = new KisPaintDevice(colorSpace, "pattern");

    TQ_CHECK_PTR(layer);

    layer->convertFromTQImage(m_img,"");

    m_colorspaces[colorSpace->id().id()] = layer;
    return layer;
}

TQ_INT32 KisPattern::width() const
{
    return m_width;
}

void KisPattern::setWidth(TQ_INT32 w)
{
    m_width = w;
}

TQ_INT32 KisPattern::height() const
{
    return m_height;
}

void KisPattern::setHeight(TQ_INT32 h)
{
    m_height = h;
}

void KisPattern::setImage(const TQImage& img)
{
    m_hasFile = false;
    m_img = img;
    m_img.detach();

    setWidth(img.width());
    setHeight(img.height());

    setValid(true);
}

KisPattern* KisPattern::clone() const
{
    KisPattern* pattern = new KisPattern("");
    pattern->setImage(m_img);
    pattern->setName(name());
    return pattern;
}

#include "kis_pattern.moc"
