/////////////////////////////////////////////////////////////////////////////
// Name:        src/osx/carbon/icon.cpp
// Purpose:     wxIcon class
// Author:      Stefan Csomor
// Modified by:
// Created:     1998-01-01
// Copyright:   (c) Stefan Csomor
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#include "wx/wxprec.h"

#if wxOSX_USE_COCOA_OR_CARBON

#include "wx/icon.h"

#ifndef WX_PRECOMP
    #include "wx/image.h"
#endif

#include "wx/osx/private.h"

wxIMPLEMENT_DYNAMIC_CLASS(wxIcon, wxGDIObject);

#define M_ICONDATA ((wxIconRefData *)m_refData)

#define wxOSX_ICON_USE_NSIMAGE wxOSX_BITMAP_NATIVE_ACCESS

#if wxOSX_ICON_USE_NSIMAGE

// implementation based on NSImage

class WXDLLEXPORT wxIconRefData : public wxGDIRefData
{
public:
    wxIconRefData() { Init(); }
    wxIconRefData( WX_NSImage image, int desiredWidth, int desiredHeight );
    wxIconRefData( WXHICON iconref, int desiredWidth, int desiredHeight );

    virtual ~wxIconRefData() { Free(); }

    virtual bool IsOk() const wxOVERRIDE { return m_nsImage != NULL; }
    virtual void Free();

    int GetWidth() const { return (int) wxOSXGetImageSize(m_nsImage).width; }
    int GetHeight() const { return (int) wxOSXGetImageSize(m_nsImage).height; }

    WX_NSImage GetImage() const;

private:
    void Init();
    void Create( NSImage* icon, int desiredWidth, int desiredHeight );
    NSImage* m_nsImage;

    // We can (easily) copy m_iconRef so we don't implement the copy ctor.
    wxDECLARE_NO_COPY_CLASS(wxIconRefData);
};


wxIconRefData::wxIconRefData( NSImage* icon, int desiredWidth, int desiredHeight )
{
    Init();

    Create(icon, desiredWidth, desiredHeight);
}

wxIconRefData::wxIconRefData( WXHICON iconref, int desiredWidth, int desiredHeight )
{
    Init();

    Create(wxOSXGetNSImageFromIconRef(iconref), desiredWidth, desiredHeight);
    ReleaseIconRef(iconref);
}

void wxIconRefData::Create( NSImage* icon, int WXUNUSED(desiredWidth), int WXUNUSED(desiredHeight) )
{
    if ( icon )
    {
        m_nsImage = icon;
        wxMacCocoaRetain(icon);
    }
}

void wxIconRefData::Init()
{
    m_nsImage = NULL;
}

void wxIconRefData::Free()
{
    if ( m_nsImage )
    {
        wxMacCocoaRelease(m_nsImage);
    }
}

WX_NSImage wxIconRefData::GetImage() const
{
    wxASSERT( IsOk() );

    return m_nsImage;
}

#else // !wxOSX_ICON_USE_NSIMAGE

// implementation based on IconRef

class WXDLLEXPORT wxIconRefData : public wxGDIRefData
{
public:
    wxIconRefData() { Init(); }
    wxIconRefData( WXHICON iconref, int desiredWidth, int desiredHeight );
    virtual ~wxIconRefData() { Free(); }

    virtual bool IsOk() const wxOVERRIDE { return m_iconRef != NULL; }
    virtual void Free();

    void SetWidth( int width ) { m_width = width; }
    void SetHeight( int height ) { m_height = height; }

    int GetWidth() const { return m_width; }
    int GetHeight() const { return m_height; }

    WXHICON GetHICON() const { return (WXHICON) m_iconRef; }

    WX_NSImage GetImage() const;

private:
    void Init();

    IconRef m_iconRef;

    mutable NSImage* m_nsImage;

    int m_width;
    int m_height;

    // We can (easily) copy m_iconRef so we don't implement the copy ctor.
    wxDECLARE_NO_COPY_CLASS(wxIconRefData);
};

wxIconRefData::wxIconRefData( WXHICON icon, int desiredWidth, int desiredHeight )
{
    Init();
    m_iconRef = (IconRef)( icon ) ;

    // Standard sizes
    SetWidth( desiredWidth == -1 ? 32 : desiredWidth ) ;
    SetHeight( desiredHeight == -1 ? 32 : desiredHeight ) ;
}

void wxIconRefData::Init()
{
    m_iconRef = NULL ;
    m_nsImage = NULL;

    m_width =
    m_height = 0;
}

void wxIconRefData::Free()
{
    if ( m_iconRef )
    {
        ReleaseIconRef( m_iconRef ) ;
        m_iconRef = NULL ;
    }

    if ( m_nsImage )
    {
        wxMacCocoaRelease(m_nsImage);
    }
}

WX_NSImage wxIconRefData::GetImage() const
{
    wxASSERT( IsOk() );

    if ( m_nsImage == 0 )
    {
        m_nsImage = wxOSXGetNSImageFromIconRef(m_iconRef);
        CFRetain(m_nsImage);
    }

    return m_nsImage;
}

#endif

//
//
//

wxIcon::wxIcon()
{
}

wxIcon::wxIcon( const char bits[], int width, int height )
{
    wxBitmap bmp( bits, width, height ) ;
    CopyFromBitmap( bmp ) ;
}

wxIcon::wxIcon(const char* const* bits)
{
    wxBitmap bmp( bits ) ;
    CopyFromBitmap( bmp ) ;
}

wxIcon::wxIcon(
    const wxString& icon_file, wxBitmapType flags,
    int desiredWidth, int desiredHeight )
{
    LoadFile( icon_file, flags, desiredWidth, desiredHeight );
}

#if wxOSX_USE_ICONREF
wxIcon::wxIcon(WXHICON icon, const wxSize& size)
      : wxGDIObject()
{
    // as the icon owns that ref, we have to acquire it as well
    if (icon)
        AcquireIconRef( (IconRef) icon ) ;

    m_refData = new wxIconRefData( icon, size.x, size.y ) ;
}

WXHICON wxIcon::GetHICON() const
{
    wxASSERT( IsOk() ) ;

    return (WXHICON) ((wxIconRefData*)m_refData)->GetHICON() ;
}
#endif

wxIcon::~wxIcon()
{
}

wxGDIRefData *wxIcon::CreateGDIRefData() const
{
    return new wxIconRefData;
}

wxGDIRefData *
wxIcon::CloneGDIRefData(const wxGDIRefData * WXUNUSED(data)) const
{
    wxFAIL_MSG( wxS("Cloning icons is not implemented in wxCarbon.") );

    return new wxIconRefData;
}

int wxIcon::GetWidth() const
{
   wxCHECK_MSG( IsOk(), -1, wxT("invalid icon") );

   return M_ICONDATA->GetWidth();
}

int wxIcon::GetHeight() const
{
   wxCHECK_MSG( IsOk(), -1, wxT("invalid icon") );

   return M_ICONDATA->GetHeight();
}

int wxIcon::GetDepth() const
{
    return 32;
}

WX_NSImage wxIcon::GetImage() const
{
    wxCHECK_MSG( IsOk(), NULL, wxT("invalid icon") );
    
    return M_ICONDATA->GetImage() ;
}

#if WXWIN_COMPATIBILITY_3_0
void wxIcon::SetDepth( int WXUNUSED(depth) )
{
}

void wxIcon::SetWidth( int WXUNUSED(width) )
{
}

void wxIcon::SetHeight( int WXUNUSED(height) )
{
}
#endif

// Load an icon based on resource name or filel name
// Return true on success, false otherwise
bool wxIcon::LoadFile(
    const wxString& filename, wxBitmapType type,
    int desiredWidth, int desiredHeight )
{
    if( type == wxBITMAP_TYPE_ICON_RESOURCE )
    {
        if( LoadIconFromSystemResource( filename, desiredWidth, desiredHeight ) )
            return true;
        else
            return LoadIconFromBundleResource( filename, desiredWidth, desiredHeight );
    }
    else  if( type == wxBITMAP_TYPE_ICON )
    {
        return LoadIconFromFile( filename, desiredWidth, desiredHeight );
    }
    else
    {
        return LoadIconAsBitmap( filename, type, desiredWidth, desiredHeight );
    }
}

// Load a well known system icon by its wxWidgets identifier
// Returns true on success, false otherwise
bool wxIcon::LoadIconFromSystemResource(const wxString& resourceName, int desiredWidth, int desiredHeight)
{
    UnRef();

    OSType theId = 0 ;

    if ( resourceName == wxT("wxICON_INFORMATION") )
    {
        theId = kAlertNoteIcon ;
    }
    else if ( resourceName == wxT("wxICON_QUESTION") )
    {
        theId = kAlertCautionIcon ;
    }
    else if ( resourceName == wxT("wxICON_WARNING") )
    {
        theId = kAlertCautionIcon ;
    }
    else if ( resourceName == wxT("wxICON_ERROR") )
    {
        theId = kAlertStopIcon ;
    }
    else if ( resourceName == wxT("wxICON_FOLDER") )
    {
        theId = kGenericFolderIcon ;
    }
    else if ( resourceName == wxT("wxICON_FOLDER_OPEN") )
    {
        theId = kOpenFolderIcon ;
    }
    else if ( resourceName == wxT("wxICON_NORMAL_FILE") )
    {
        theId = kGenericDocumentIcon ;
    }
    else if ( resourceName == wxT("wxICON_EXECUTABLE_FILE") )
    {
        theId = kGenericApplicationIcon ;
    }
    else if ( resourceName == wxT("wxICON_CDROM") )
    {
        theId = kGenericCDROMIcon ;
    }
    else if ( resourceName == wxT("wxICON_FLOPPY") )
    {
        theId = kGenericFloppyIcon ;
    }
    else if ( resourceName == wxT("wxICON_HARDDISK") )
    {
        theId = kGenericHardDiskIcon ;
    }
    else if ( resourceName == wxT("wxICON_REMOVABLE") )
    {
        theId = kGenericRemovableMediaIcon ;
    }
    else if ( resourceName == wxT("wxICON_DELETE") )
    {
        theId = kToolbarDeleteIcon ;
    }
    else if ( resourceName == wxT("wxICON_GO_BACK") )
    {
        theId = kBackwardArrowIcon ;
    }
    else if ( resourceName == wxT("wxICON_GO_FORWARD") )
    {
        theId = kForwardArrowIcon ;
    }
    else if ( resourceName == wxT("wxICON_GO_HOME") )
    {
        theId = kToolbarHomeIcon ;
    }
    else if ( resourceName == wxT("wxICON_HELP_SETTINGS") )
    {
        theId = kGenericFontIcon ;
    }
    else if ( resourceName == wxT("wxICON_HELP_PAGE") )
    {
        theId = kGenericDocumentIcon ;
    }
    else if ( resourceName == wxT( "wxICON_PRINT" ) )
    {
        theId = kPrintMonitorFolderIcon;
    }
    else if ( resourceName == wxT( "wxICON_HELP_FOLDER" ) )
    {
        theId = kHelpFolderIcon;
    }

    if ( theId != 0 )
    {
        IconRef iconRef = NULL ;
        __Verify_noErr(GetIconRef( kOnSystemDisk, kSystemIconsCreator, theId, &iconRef )) ;
        if ( iconRef )
            m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ) ;
    }

    return IsOk();
}

// Load an icon of type 'icns' by resource by name
// The resource must exist in one of the currently accessible bundles
// (usually this means the application bundle for the current application)
// Return true on success, false otherwise
bool wxIcon::LoadIconFromBundleResource(const wxString& resourceName, int desiredWidth, int desiredHeight)
{
    UnRef();

#if wxOSX_USE_ICONREF
    IconRef iconRef = NULL ;

    // first look in the resource fork
    if ( iconRef == NULL )
    {
        Str255 theName ;

        wxMacStringToPascal( resourceName , theName ) ;
        Handle resHandle = GetNamedResource( 'icns' , theName ) ;
        if ( resHandle != 0L )
        {
            IconFamilyHandle iconFamily = (IconFamilyHandle) resHandle ;
            OSStatus err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &iconRef );

            if ( err != noErr )
            {
                wxFAIL_MSG("Error when constructing icon ref");
            }

            ReleaseResource( resHandle ) ;
        }
    }
    if ( iconRef == NULL )
    {
        wxCFStringRef name(resourceName);
        FSRef iconFSRef;
        
        wxCFRef<CFURLRef> iconURL(CFBundleCopyResourceURL(CFBundleGetMainBundle(), name, CFSTR("icns"), NULL));

        if (CFURLGetFSRef(iconURL, &iconFSRef))
        {
            // Get a handle on the icon family
            IconFamilyHandle iconFamily;
            OSStatus err = ReadIconFromFSRef( &iconFSRef, &iconFamily );
            
            if ( err == noErr )
            {
                err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &iconRef );
            }
            ReleaseResource( (Handle) iconFamily );
        }
    }

    if ( iconRef )
    {
        m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight );
        return true;
    }
#endif
   return false;
}

// Load an icon from an icon file using the underlying OS X API
// The icon file must be in a format understood by the OS
// Return true for success, false otherwise
bool wxIcon::LoadIconFromFile(const wxString& filename, int desiredWidth, int desiredHeight)
{
    UnRef();

    bool result = false;

#if wxOSX_USE_ICONREF
    OSStatus err;
    // Get a file system reference
    FSRef fsRef;
    err = FSPathMakeRef( (const wxUint8*)filename.utf8_str().data(), &fsRef, NULL );

    if( err != noErr )
        return false;

    // Get a handle on the icon family
    IconFamilyHandle iconFamily;
    err = ReadIconFromFSRef( &fsRef, &iconFamily );

    if( err != noErr )
        return false;

    // Get the icon reference itself
    IconRef iconRef;
    err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &iconRef );

    if( err == noErr )
    {
        // If everything is OK, assign m_refData
        m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight );
        result = true;
    }

    // Release the iconFamily before returning
    ReleaseResource( (Handle) iconFamily );
#endif
    return result;
}

// Load an icon from a file using functionality from wxWidgets
// A suitable bitmap handler (or image handler) must be available
// Return true on success, false otherwise
bool wxIcon::LoadIconAsBitmap(const wxString& filename, wxBitmapType type, int desiredWidth, int desiredHeight)
{
    UnRef();

    wxBitmapHandler *handler = wxBitmap::FindHandler( type );

    if ( handler )
    {
        wxBitmap bmp ;
        if ( handler->LoadFile( &bmp , filename, type, desiredWidth, desiredHeight ))
        {
            CopyFromBitmap( bmp ) ;
            return true ;
        }
    }

#if wxUSE_IMAGE
    else
    {
        wxImage loadimage( filename, type );
        if (loadimage.IsOk())
        {
            if ( desiredWidth == -1 )
                desiredWidth = loadimage.GetWidth() ;
            if ( desiredHeight == -1 )
                desiredHeight = loadimage.GetHeight() ;
            if ( desiredWidth != loadimage.GetWidth() || desiredHeight != loadimage.GetHeight() )
                loadimage.Rescale( desiredWidth , desiredHeight ) ;

            wxBitmap bmp( loadimage );
            CopyFromBitmap( bmp ) ;

            return true;
        }
    }
#endif

    return false;
}


void wxIcon::CopyFromBitmap( const wxBitmap& bmp )
{
    UnRef() ;

#if wxOSX_ICON_USE_NSIMAGE
    m_refData = new wxIconRefData( bmp.GetImage() , bmp.GetWidth(), bmp.GetHeight()  ) ;
#else
    // as the bitmap owns that ref, we have to acquire it as well
    
    int w = bmp.GetWidth() ;
    int h = bmp.GetHeight() ;
    int sz = wxMax( w , h ) ;

    if ( sz == 24 || sz == 64 )
    {
        wxBitmap scaleBmp( bmp.ConvertToImage().Scale( w * 2 , h * 2 ) ) ;
        m_refData = new wxIconRefData( (WXHICON) scaleBmp.CreateIconRef(), bmp.GetWidth(), bmp.GetHeight()  ) ;
    }
    else 
    {
        m_refData = new wxIconRefData( (WXHICON) bmp.CreateIconRef() , bmp.GetWidth(), bmp.GetHeight()  ) ;
    }
#endif
}

wxIMPLEMENT_DYNAMIC_CLASS(wxICONResourceHandler, wxBitmapHandler);

bool  wxICONResourceHandler::LoadFile(
    wxBitmap *bitmap, const wxString& name, wxBitmapType WXUNUSED(flags),
    int desiredWidth, int desiredHeight )
{
    wxIcon icon ;
    if ( icon.LoadFile( name , wxBITMAP_TYPE_ICON_RESOURCE , desiredWidth , desiredHeight ) )
    {
        bitmap->CopyFromIcon( icon ) ;
        return bitmap->IsOk() ;
    }
    return false;
}

#endif

