//
// Surface class for PTC 2.0 C++ API
// Copyright (c) 1998 Glenn Fiedler (ptc@gaffer.org)
// This source code is licensed under the GNU LGPL
//

// include files
#include <memory.h>
#include "Area.h"
#include "Copy.h"
#include "Clip.h"
#include "Clear.h"
#include "Error.h"
#include "Color.h"
#include "Format.h"
#include "Surface.h"




Surface::Surface(int width,int height,const Format &format)
{
    // setup data
    m_width  = width;
    m_height = height;
    m_format = format;
    m_locked = false;
    m_pixels = 0;

    // calculate size of pixels
    int size = width * height * format.bytes();

    // check size
    if (!size) throw Error("zero surface size");

    // allocate pixels
    m_pixels = new char8[size];

    // clear pixels    
    memset(m_pixels,0,size);
}


Surface::~Surface()
{
    // free surface
    delete[] m_pixels;
}




void Surface::copy(BaseSurface &surface)
{
    // load surface pixels to other surface
    surface.load(m_pixels,m_width,m_height,m_format,m_palette);
}


void Surface::copy(BaseSurface &surface,const Area &source,const Area &destination)
{
    // load surface pixels to other surface
    surface.load(m_pixels,m_width,m_height,source,destination,m_format,m_palette);
}




void* Surface::lock()
{
    // fail if surface is already locked
    if (m_locked) throw Error("surface is already locked");

    // we are locked...
    m_locked = true;

    // lock surface
    return m_pixels;
}


void Surface::unlock()
{
    // fail if surface is not locked
    if (!m_locked) throw Error("surface is not locked");

    // unlock surface
    m_locked = false;
}




void Surface::load(const void *pixels,int width,int height,const Format &format,const Palette &palette)
{
    // request format conversion
    m_copy.request(format,m_format);
    
    // set copy palettes
    m_copy.palette(palette,m_palette);
    
    // copy pixels to surface
    m_copy.copy(pixels,0,0,width,height,width*format.bytes(),m_pixels,0,0,m_width,m_height,pitch());
}


void Surface::load(const void *pixels,int width,int height,const Area &source,const Area &destination,const Format &format,const Palette &palette)
{
    // clip source and destination areas
    Area clipped_source,clipped_destination;
    Clip::clip(Area(0,0,width,height),source,clipped_source,
               Area(0,0,m_width,m_height),destination,clipped_destination);

    // request format conversion
    m_copy.request(format,m_format);
    
    // set copy palettes
    m_copy.palette(palette,m_palette);

    // copy pixels to surface
    m_copy.copy(pixels,clipped_source.left(),clipped_source.top(),clipped_source.width(),clipped_source.height(),width*format.bytes(),
                m_pixels,clipped_destination.left(),clipped_destination.top(),clipped_destination.width(),clipped_destination.height(),pitch());
}




void Surface::save(void *pixels,int width,int height,const Format &format,const Palette &palette)
{
    // request format conversion
    m_copy.request(m_format,format);
    
    // set copy palettes
    m_copy.palette(m_palette,palette);

    // copy surface pixels to 'pixels' buffer
    m_copy.copy(m_pixels,0,0,m_width,m_height,pitch(),pixels,0,0,width,height,width*format.bytes());
}


void Surface::save(void *pixels,int width,int height,const Area &source,const Area &destination,const Format &format,const Palette &palette)
{
    // clip source and destination areas
    Area clipped_source,clipped_destination;
    Clip::clip(Area(0,0,m_width,m_height),source,clipped_source,
               Area(0,0,width,height),destination,clipped_destination);

    // request format conversion
    m_copy.request(m_format,format);
    
    // set copy palettes
    m_copy.palette(m_palette,palette);

    // copy surface pixels to 'pixels' buffer
    m_copy.copy(m_pixels,clipped_source.left(),clipped_source.top(),clipped_source.width(),clipped_source.height(),pitch(),
                pixels,clipped_destination.left(),clipped_destination.top(),clipped_destination.width(),clipped_destination.height(),width*format.bytes());
}




void Surface::clear()
{
    // check surface format
    if (format().direct())
    {
        // direct color
        clear(Color(0,0,0,0));
    }
    else
    {
        // indexed color
        clear(Color(0));
    }
}


void Surface::clear(const Color &color)
{
    // request clear
    m_clear.request(m_format);

    // clear surface
    m_clear.clear(m_pixels,0,0,m_width,m_height,pitch(),color);
}


void Surface::clear(const Color &color,const Area &area)
{
    // clip area
    Area clipped_area = Clip::clip(Area(0,0,m_width,m_height),area);

    // request clear
    m_clear.request(m_format);

    // clear surface area
    m_clear.clear(m_pixels,clipped_area.left(),clipped_area.top(),clipped_area.width(),clipped_area.height(),pitch(),color);
}




void Surface::palette(const Palette &palette)
{
    // set palette
    m_palette.load(palette.data());
}


const Palette& Surface::palette() const
{
    // get palette
    return m_palette;
}




int Surface::width() const
{
    // get width
    return m_width;
}


int Surface::height() const
{
    // get height
    return m_height;
}


int Surface::pitch() const
{
    // get pitch
    return m_width * m_format.bytes();
}


const Format& Surface::format() const
{
    // get format
    return m_format;
}


bool Surface::option(const char option[])
{
    // 
    // Surface option string
    // ---------------------
    //
    // This function just passes the option string to the
    // copy object in order to provide control over copying
    // from this surface to other surfaces. See "Copy::option"
    // for more details on copy option strings.
    //

    // pass the option to the copy object
    return m_copy.option(option);
}
