//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#import <Cocoa/Cocoa.h>
#import "platform/platform.h"
#import "core/util/tVector.h"
#import "math/mMathFn.h"
#import "platformMac/macFont.h"
#import "core/stringTable.h"
#import "core/strings/unicode.h"
#import "console/console.h"


//------------------------------------------------------------------------------

PlatformFont* createPlatformFont( const char* name, dsize_t size, U32 charset )
{
   PlatformFont* pFont = new OSXFont();
   
   if ( pFont->create(name, size, charset) )
      return pFont;
   
   delete pFont;
   
   return NULL;
}

//------------------------------------------------------------------------------

void PlatformFont::enumeratePlatformFonts( Vector<StringTableEntry>& fonts, UTF16 *fontFamily )
{
   // Fetch available fonts.
   NSArray* availableFonts = [[NSFontManager sharedFontManager] availableFontNamesWithTraits:0];
   
   // Enumerate font names.
   for (id fontName in availableFonts)
   {
      fonts.push_back( StringTable->insert( [fontName UTF8String] ) );
   }
   
   // Release font name.
   [availableFonts release];
}

//------------------------------------------------------------------------------

OSXFont::OSXFont()
{
   // Reset the rendering color-space.
   mColorSpace = NULL;
}

//------------------------------------------------------------------------------

OSXFont::~OSXFont()
{
   // Destroy the rendering color-space.
   CGColorSpaceRelease( mColorSpace );
}

//------------------------------------------------------------------------------

bool OSXFont::create( const char* name, dsize_t size, U32 charset )
{
   // Sanity!
   AssertFatal( name != NULL, "Cannot create a NULL font name." );
    
   bool doBold = false;
   bool doItalic = false;

   String nameStr = name;
   nameStr = nameStr.trim();

   bool haveModifier;
   do
   {
      haveModifier = false;
      if( nameStr.compare( "Bold", 4, String::NoCase | String::Right ) == 0 )
      {
         doBold = true;
         nameStr = nameStr.substr( 0, nameStr.length() - 4 ).trim();
         haveModifier = true;
      }
      if( nameStr.compare( "Italic", 6, String::NoCase | String::Right ) == 0 )
      {
         doItalic = true;
         nameStr = nameStr.substr( 0, nameStr.length() - 6 ).trim();
         haveModifier = true;
      }
   }
   while( haveModifier );

   // Generate compatible font name.
   NSString* fontName = [NSString stringWithUTF8String: nameStr.utf8()];
   
   // Sanity!
   if ( !fontName )
   {
      Con::errorf("Could not handle font name of '%s'.", name );
      return false;
   }
   
   NSMutableDictionary* fontAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:
      fontName, (NSString*)kCTFontFamilyNameAttribute,
      [NSNumber numberWithFloat: (float)size], (NSString*)kCTFontSizeAttribute,
      nil];
   
   CTFontSymbolicTraits traits = 0x0;
   if (doBold)
      traits |= kCTFontBoldTrait;
   if (doItalic)
      traits |= kCTFontItalicTrait;
   
   CTFontDescriptorRef descriptor =
             CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes);
   
   mFontRef = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL);
   CFRelease(descriptor);
   
   // Sanity!
   if ( !mFontRef )
   {
      Con::errorf( "Could not generate a font reference to font name '%s' of size '%d'", name, size );
      return false;
   }
   
   // Apply font traits if we have any by creating a copy of the font
   if (traits != 0x0)
      mFontRef = CTFontCreateCopyWithSymbolicTraits(mFontRef, (float)size, NULL, traits, traits);
   
   // Fetch font metrics.
   CGFloat ascent = CTFontGetAscent( mFontRef );
   CGFloat descent = CTFontGetDescent( mFontRef );
   
   // Set baseline.
   mBaseline = (U32)mRound(ascent);
   
   // Set height.
   mHeight = (U32)mRound( ascent + descent );
   
   // Create a gray-scale color-space.
   mColorSpace = CGColorSpaceCreateDeviceGray();
   
   // Return status.
   return true;
}

//------------------------------------------------------------------------------

bool OSXFont::isValidChar( const UTF8* str ) const
{
   // since only low order characters are invalid, and since those characters
   // are single codeunits in UTF8, we can safely cast here.
   return isValidChar((UTF16)*str);
}

//------------------------------------------------------------------------------

bool OSXFont::isValidChar( const UTF16 character) const
{
   // We cut out the ASCII control chars here. Only printable characters are valid.
   // 0x20 == 32 == space
   if( character < 0x20 )
      return false;
   
   return true;
}

//------------------------------------------------------------------------------

PlatformFont::CharInfo& OSXFont::getCharInfo(const UTF8 *str) const
{
   return getCharInfo( oneUTF32toUTF16(oneUTF8toUTF32(str,NULL)) );
}

//------------------------------------------------------------------------------

PlatformFont::CharInfo& OSXFont::getCharInfo(const UTF16 character) const
{
   // Declare and clear out the CharInfo that will be returned.
   static PlatformFont::CharInfo characterInfo;
   dMemset(&characterInfo, 0, sizeof(characterInfo));
   
   // prep values for GFont::addBitmap()
   characterInfo.bitmapIndex = 0;
   characterInfo.xOffset = 0;
   characterInfo.yOffset = 0;
   
   CGGlyph characterGlyph;
   CGRect characterBounds;
   CGSize characterAdvances;
   UniChar unicodeCharacter = character;
   
   // Fetch font glyphs.
   if ( !CTFontGetGlyphsForCharacters( mFontRef, &unicodeCharacter, &characterGlyph, (CFIndex)1) )
   {
      // Sanity!
      //AssertFatal( false, "Cannot create font glyph." );
      Con::warnf("Font glyph is messed up. Some characters may render incorrectly.");
   }
   
   // Fetch glyph bounding box.
   CTFontGetBoundingRectsForGlyphs( mFontRef, kCTFontHorizontalOrientation, &characterGlyph, &characterBounds, (CFIndex)1 );
   
   // Fetch glyph advances.
   CTFontGetAdvancesForGlyphs( mFontRef, kCTFontHorizontalOrientation, &characterGlyph, &characterAdvances, (CFIndex)1 );
   
   // Set character metrics,
   characterInfo.xOrigin = (S32)mRound( characterBounds.origin.x );
   characterInfo.yOrigin = (S32)mRound( characterBounds.origin.y );
   characterInfo.width = (U32)mCeil( characterBounds.size.width ) + 2;
   characterInfo.height = (U32)mCeil( characterBounds.size.height ) + 2;
   characterInfo.xIncrement = (S32)mRound( characterAdvances.width );
   
   // Finish if character is undrawable.
   if ( characterInfo.width == 0 && characterInfo.height == 0 )
      return characterInfo;
   
   // Clamp character minimum width.
   if ( characterInfo.width == 0 )
      characterInfo.width = 2;
   
   if ( characterInfo.height == 0 )
      characterInfo.height = 1;
   
   
   // Allocate a bitmap surface.
   const U32 bitmapSize = characterInfo.width * characterInfo.height;
   characterInfo.bitmapData = new U8[bitmapSize];
   dMemset(characterInfo.bitmapData, 0x00, bitmapSize);
   
   // Create a bitmap context.
   CGContextRef bitmapContext = CGBitmapContextCreate( characterInfo.bitmapData, characterInfo.width, characterInfo.height, 8, characterInfo.width, mColorSpace, kCGImageAlphaNone );
   
   // Sanity!
   AssertFatal( bitmapContext != NULL, "Cannot create font context." );
   
   // Render font anti-aliased if font is arbitrarily small.
   CGContextSetShouldAntialias( bitmapContext, true);
   CGContextSetShouldSmoothFonts( bitmapContext, true);
   CGContextSetRenderingIntent( bitmapContext, kCGRenderingIntentAbsoluteColorimetric);
   CGContextSetInterpolationQuality( bitmapContext, kCGInterpolationNone);
   CGContextSetGrayFillColor( bitmapContext, 1.0, 1.0);
   CGContextSetTextDrawingMode( bitmapContext,  kCGTextFill);
   
   // Draw glyph.
   CGPoint renderOrigin;
   renderOrigin.x = -characterInfo.xOrigin;
   renderOrigin.y = -characterInfo.yOrigin;

   CTFontDrawGlyphs( mFontRef, &characterGlyph, &renderOrigin, 1, bitmapContext );
   
#if 0
   Con::printf("Width:%f, Height:%f, OriginX:%f, OriginY:%f",
               characterBounds.size.width,
               characterBounds.size.height,
               characterBounds.origin.x,
               characterBounds.origin.y );
#endif
   
   // Adjust the y origin for the glyph size.
   characterInfo.yOrigin += characterInfo.height;// + mHeight;
   
   // Release the bitmap context.
   CGContextRelease( bitmapContext );
   
   // Return character information.
   return characterInfo;
}
