//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
// Contact: mcseemagg@yahoo.com
//          baer@karto.baug.ethz.ch
//----------------------------------------------------------------------------
//
// class pixel_map
//
//----------------------------------------------------------------------------

#include <cstring>
#include <Carbon.h>
#include <QuickTimeComponents.h>
#include <ImageCompression.h>
#include "platform/mac/agg_mac_pmap.h"
#include "agg_basics.h"

namespace agg
{

    //------------------------------------------------------------------------
    pixel_map::~pixel_map()
    {
        destroy();
    }


    //------------------------------------------------------------------------
    pixel_map::pixel_map() :
        m_pmap(0),
        m_buf(0),
        m_bpp(0),
        m_img_size(0)

    {
    }


    //------------------------------------------------------------------------
    void pixel_map::destroy()
    {
		delete[] m_buf;
		m_buf = NULL;
		if (m_pmap != nil)
		{
			DisposeGWorld(m_pmap);
			m_pmap = nil;
		}
    }


    //------------------------------------------------------------------------
    void pixel_map::create(unsigned width, 
                           unsigned height, 
                           org_e    org,
                           unsigned clear_val)
    {
        destroy();
        if(width == 0)  width = 1;
        if(height == 0) height = 1;
        m_bpp = org;
        
        Rect	r;
    	int		row_bytes = calc_row_len (width, m_bpp);
    	MacSetRect(&r, 0, 0, width, height);
    	m_buf = new unsigned char[m_img_size = row_bytes * height];
 		// The Quicktime version for creating GWorlds is more flexible than the classical function.
    	QTNewGWorldFromPtr (&m_pmap, m_bpp, &r, nil, nil, 0, m_buf, row_bytes);

        // create_gray_scale_palette(m_pmap);  I didn't care about gray scale palettes so far.
        if(clear_val <= 255)
        {
            std::memset(m_buf, clear_val, m_img_size);
        }
    }



    //------------------------------------------------------------------------
    void pixel_map::clear(unsigned clear_val)
    {
        if(m_buf) std::memset(m_buf, clear_val, m_img_size);
    }


    //static
    //This function is just copied from the Win32 plattform support.
    //Is also seems to be appropriate for MacOS as well, but it is not
    //thouroughly tested so far.
    //------------------------------------------------------------------------

    unsigned pixel_map::calc_row_len(unsigned width, unsigned bits_per_pixel)
    {
        unsigned n = width;
        unsigned k;

        switch(bits_per_pixel)
        {
            case  1: k = n;
                     n = n >> 3;
                     if(k & 7) n++; 
                     break;

            case  4: k = n;
                     n = n >> 1;
                     if(k & 3) n++; 
                     break;

            case  8:
                     break;

            case 16: n = n << 1;
                     break;

            case 24: n = (n << 1) + n; 
                     break;

            case 32: n = n << 2;
                     break;

            default: n = 0;
                     break;
        }
        return ((n + 3) >> 2) << 2;
    }




    //------------------------------------------------------------------------
    void pixel_map::draw(WindowRef window, const Rect *device_rect, const Rect *pmap_rect) const
    {
        if(m_pmap == nil || m_buf == NULL) return;

		PixMapHandle	pm = GetGWorldPixMap (m_pmap);
		CGrafPtr		port = GetWindowPort (window);
		Rect			dest_rect;

		// Again, I used the Quicktime version.
		// Good old 'CopyBits' does better interpolation when scaling
		// but does not support all pixel depths.
		MacSetRect (&dest_rect, 0, 0, this->width(), this->height());
		ImageDescriptionHandle		image_description;
		MakeImageDescriptionForPixMap (pm, &image_description);	   
		if (image_description != nil)
		{
			DecompressImage (GetPixBaseAddr (pm), image_description, GetPortPixMap (port), nil, &dest_rect, ditherCopy, nil);	   
			DisposeHandle ((Handle) image_description);
		}
	}


    //------------------------------------------------------------------------
    void pixel_map::draw(WindowRef window, int x, int y, double scale) const
    {
        if(m_pmap == nil || m_buf == NULL) return;
        unsigned width  = (unsigned)(this->width() * scale);
        unsigned height = (unsigned)(this->height() * scale);
        Rect rect;
        SetRect (&rect, x, y, x + width, y + height);
        draw(window, &rect);
    }



    //------------------------------------------------------------------------
    void pixel_map::blend(WindowRef window, const Rect *device_rect, const Rect *bmp_rect) const
    {
		draw (window, device_rect, bmp_rect);	// currently just mapped to drawing method
    }
    

    //------------------------------------------------------------------------
    void pixel_map::blend(WindowRef window, int x, int y, double scale) const
    {
        draw(window, x, y, scale);	// currently just mapped to drawing method
    }


    // I let Quicktime handle image import since it supports most popular
    // image formats such as:
    // *.psd, *.bmp, *.tif, *.png, *.jpg, *.gif, *.pct, *.pcx
    //------------------------------------------------------------------------
    bool pixel_map::load_from_qt(const char *filename)
    {
		FSSpec						fss;
		OSErr						err;
		
		// get file specification to application directory
		err = HGetVol(nil, &fss.vRefNum, &fss.parID);
		if (err == noErr)
		{
			CopyCStringToPascal(filename, fss.name);
			GraphicsImportComponent		gi;
			err = GetGraphicsImporterForFile (&fss, &gi);
			if (err == noErr)
			{
				ImageDescriptionHandle	desc;
				GraphicsImportGetImageDescription(gi, &desc);
// For simplicity, all images are currently converted to 32 bit.
				// create an empty pixelmap
				short depth = 32;
				create ((**desc).width, (**desc).height, (org_e)depth, 0xff);
				DisposeHandle ((Handle)desc);
				// let Quicktime draw to pixelmap
				GraphicsImportSetGWorld(gi, m_pmap, nil);
				GraphicsImportDraw(gi);
// Well, this is a hack. The graphics importer sets the alpha channel of the pixelmap to 0x00
// for imported images without alpha channel but this would cause agg to draw an invisible image.
				// set alpha channel to 0xff
				unsigned char * buf = m_buf;
				for (unsigned int size = 0; size < m_img_size; size += 4)
				{
					*buf = 0xff;
					buf += 4;
				}
			}
		}
        return err == noErr;
    }



    //------------------------------------------------------------------------
    bool pixel_map::save_as_qt(const char *filename) const
    {
		FSSpec						fss;
 		OSErr						err;
		
		// get file specification to application directory
		err = HGetVol(nil, &fss.vRefNum, &fss.parID);
		if (err == noErr)
		{
     		GraphicsExportComponent		ge;
  			CopyCStringToPascal(filename, fss.name);
  			// I decided to use PNG as output image file type.
  			// There are a number of other available formats.
  			// Should I check the file suffix to choose the image file format?
			err = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG, &ge);
			if (err == noErr)
			{
    			err = GraphicsExportSetInputGWorld(ge, m_pmap);
	    		if (err == noErr)
    			{
    				err = GraphicsExportSetOutputFile (ge, &fss);
    				if (err == noErr)
    				{
    					GraphicsExportDoExport(ge, nil);
    				}
    			}
    			CloseComponent(ge);
    		}
    	}
    	
        return err == noErr;
    }

    //------------------------------------------------------------------------
    unsigned char* pixel_map::buf()
    {
        return m_buf;
    }

    //------------------------------------------------------------------------
    unsigned pixel_map::width() const
    {
        if(m_pmap == nil) return 0;
		PixMapHandle	pm = GetGWorldPixMap (m_pmap);
		Rect			bounds;
		GetPixBounds (pm, &bounds);
		return bounds.right - bounds.left;
    }

    //------------------------------------------------------------------------
    unsigned pixel_map::height() const
    {
        if(m_pmap == nil) return 0;
		PixMapHandle	pm = GetGWorldPixMap (m_pmap);
		Rect			bounds;
		GetPixBounds (pm, &bounds);
		return bounds.bottom - bounds.top;
    }

    //------------------------------------------------------------------------
    int pixel_map::row_bytes() const
    {
        if(m_pmap == nil) return 0;
 		PixMapHandle	pm = GetGWorldPixMap (m_pmap);
        return calc_row_len(width(), GetPixDepth(pm));
    }



}



