//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.1
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------

#include <stdio.h>
#include "agg_font_win32_tt.h"
#include "agg_bitset_iterator.h"


namespace agg
{


    //------------------------------------------------------------------------
    template<class Scanline, class ScanlineStorage>
    void decompose_win32_glyph_bitmap_mono(const char* gbuf, 
                                           int w, int h,
                                           int x, int y,
                                           bool flip_y,
                                           Scanline& sl,
                                           ScanlineStorage& storage)
    {
        int i;
        int pitch = ((w + 31) >> 5) << 2;
        const int8u* buf = (const int8u*)gbuf;
        sl.reset(x, x + w);
        storage.prepare(w + 2);
        if(flip_y)
        {
            buf += pitch * (h - 1);
            y += h;
            pitch = -pitch;
        }
        for(i = 0; i < h; i++)
        {
            sl.reset_spans();
            bitset_iterator bits(buf, 0);
            int j;
            for(j = 0; j < w; j++)
            {
                if(bits.bit()) sl.add_cell(x + j, cover_full);
                ++bits;
            }
            buf += pitch;
            if(sl.num_spans())
            {
                sl.finalize(y - i - 1);
                storage.render(sl);
            }
        }
    }



    //------------------------------------------------------------------------
    template<class Scanline, class ScanlineStorage>
    void decompose_win32_glyph_bitmap_gray8(const char* gbuf, 
                                            int w, int h,
                                            int x, int y,
                                            bool flip_y,
                                            Scanline& sl,
                                            ScanlineStorage& storage)
    {
        int i, j;
        int pitch = ((w + 3) >> 2) << 2;
        const int8u* buf = (const int8u*)gbuf;
        sl.reset(x, x + w);
        storage.prepare(w + 2);
        if(flip_y)
        {
            buf += pitch * (h - 1);
            y += h;
            pitch = -pitch;
        }
        for(i = 0; i < h; i++)
        {
            sl.reset_spans();
            const int8u* p = buf;
            for(j = 0; j < w; j++)
            {
                if(*p) 
                {
                    unsigned v = *p;
                    if(v == 64) v = 255;
                    else v <<= 2;
                    sl.add_cell(x + j, v);
                }
                ++p;
            }
            buf += pitch;
            if(sl.num_spans())
            {
                sl.finalize(y - i - 1);
                storage.render(sl);
            }
        }
    }



    //------------------------------------------------------------------------
    template<class PathStorage, class ConvCoord>
    bool decompose_win32_glyph_outline(const char* gbuf,
                                       unsigned total_size,
                                       bool flip_y, 
                                       PathStorage& path, 
                                       ConvCoord conv)
    {
        const char* cur_glyph = gbuf;
        const char* end_glyph = gbuf + total_size;
        
        while(cur_glyph < end_glyph)
        {
            const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
            
            const char* end_poly = cur_glyph + th->cb;
            const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);

            path.move_to(conv(th->pfxStart.x), 
                         flip_y ? -conv(th->pfxStart.y) :
                                   conv(th->pfxStart.y));
           
            while(cur_poly < end_poly)
            {
                const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
                
                if (pc->wType == TT_PRIM_LINE)
                {
                    int i;
                    for (i = 0; i < pc->cpfx; i++)
                    {
                        path.line_to(conv(pc->apfx[i].x),
                                     flip_y ? -conv(pc->apfx[i].y) :
                                               conv(pc->apfx[i].y));
                    }
                }
                
                if (pc->wType == TT_PRIM_QSPLINE)
                {
                    int u;
                    for (u = 0; u < pc->cpfx - 1; u++)  // Walk through points in spline
                    {
                        POINTFX pnt_b = pc->apfx[u];    // B is always the current point
                        POINTFX pnt_c = pc->apfx[u+1];
                        
                        if (u < pc->cpfx - 2)           // If not on last spline, compute C
                        {
                            // midpoint (x,y)
                            *(int*)&pnt_c.x = (*(int*)&pnt_b.x + *(int*)&pnt_c.x) / 2;
                            *(int*)&pnt_c.y = (*(int*)&pnt_b.y + *(int*)&pnt_c.y) / 2;
                        }
                        
                        path.curve3(conv(pnt_b.x),
                                    flip_y ? -conv(pnt_b.y) : 
                                              conv(pnt_b.y),
                                    conv(pnt_c.x),
                                    flip_y ? -conv(pnt_c.y) : 
                                              conv(pnt_c.y));
                    }
                }
                cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
            }
            cur_glyph += th->cb;
        }
        return true;
    }




    //------------------------------------------------------------------------
    font_engine_win32_tt::~font_engine_win32_tt()
    {
        delete [] m_kerning_pairs;
        delete [] m_gbuf;
        delete [] m_signature;
        delete [] m_typeface;
        if(m_dc && m_old_font) ::SelectObject(m_dc, m_old_font);
        unsigned i;
        for(i = 0; i < m_num_fonts; ++i)
        {
            delete [] m_font_names[i];
            ::DeleteObject(m_fonts[i]);
        }
        delete [] m_font_names;
        delete [] m_fonts;
    }



    //------------------------------------------------------------------------
    font_engine_win32_tt::font_engine_win32_tt(HDC dc, unsigned max_fonts) :
        m_dc(dc),
        m_old_font(m_dc ? (HFONT)::GetCurrentObject(m_dc, OBJ_FONT) : 0),
        m_fonts(new HFONT [max_fonts]),
        m_num_fonts(0),
        m_max_fonts(max_fonts),
        m_font_names(new char* [max_fonts]),
        m_cur_font(0),

        m_change_stamp(0),
        m_typeface(new char [256-16]),
        m_typeface_len(256-16-1),
        m_signature(new char [256+256-16]),
        m_height(0),
        m_width(0),
        m_weight(FW_REGULAR),
        m_italic(false),
        m_underline(false),
        m_strikeout(false),
        m_char_set(ANSI_CHARSET),
        m_pitch_and_family(FF_DONTCARE),
        m_hinting(true),
        m_flip_y(false),
        m_font_created(false),
        m_resolution(0),
        m_glyph_rendering(glyph_ren_native_gray8),
        m_glyph_index(0),
        m_data_size(0),
        m_data_type(glyph_data_invalid),
        m_bounds(1,1,0,0),
        m_advance_x(0.0),
        m_advance_y(0.0),
        m_gbuf(new char [buf_size]),
        m_kerning_pairs(0),
        m_num_kerning_pairs(0),
        m_max_kerning_pairs(0),

        m_path(),
        m_curves(m_path),
        m_scanline_aa(),
        m_scanline_bin(),
        m_scanlines_aa(),
        m_scanlines_bin(),
        m_rasterizer()
    {
        m_curves.approximation_scale(2.0);
        memset(&m_mat2, 0, sizeof(m_mat2));
        m_mat2.eM11.value = 1;
        m_mat2.eM22.value = 1;
    }



    //------------------------------------------------------------------------
    int font_engine_win32_tt::find_font(const char* name) const
    {
        unsigned i;
        for(i = 0; i < m_num_fonts; ++i)
        {
            if(strcmp(name, m_font_names[i]) == 0) return i;
        }
        return -1;
    }


    //------------------------------------------------------------------------
    bool font_engine_win32_tt::create_font(const char* typeface_, 
                                           glyph_rendering ren_type)
    {
        if(m_dc)
        {
            unsigned len = strlen(typeface_);
            if(len > m_typeface_len)
            {
                delete [] m_signature;
                delete [] m_typeface;
                m_typeface  = new char [len + 32];
                m_signature = new char [len + 32 + 256];
                m_typeface_len = len + 32 - 1;
            }

            strcpy(m_typeface, typeface_);

            int h = m_height;
            int w = m_width;

            if(m_resolution)
            {
                h = ::MulDiv(m_height, m_resolution, 72);
                w = ::MulDiv(m_width,  m_resolution, 72);
            }


            sprintf(m_signature, 
                    "%s,%u,%dx%d,%d,%d,%d,%d,%d", 
                    m_typeface,
                    m_char_set,
                    h,
                    w,
                    m_weight,
                    int(m_italic),
                    int(m_underline),
                    int(m_strikeout),
                    int(m_pitch_and_family));

            int idx = find_font(m_signature);
            if(idx >= 0)
            {
                m_cur_font = m_fonts[idx];
                ::SelectObject(m_dc, m_cur_font);
                m_glyph_rendering = ren_type;
                m_num_kerning_pairs = 0;
                update_signature();
                return true;
            }
            else
            {
                m_cur_font = ::CreateFont(-h,                     // height of font
                                          w,                      // average character width
                                          0,                      // angle of escapement
                                          0,                      // base-line orientation angle
                                          m_weight,               // font weight
                                          m_italic,               // italic attribute option
                                          m_underline,            // underline attribute option
                                          m_strikeout,            // strikeout attribute option
                                          m_char_set,             // character set identifier
                                          OUT_DEFAULT_PRECIS,     // output precision
                                          CLIP_DEFAULT_PRECIS,    // clipping precision
                                          ANTIALIASED_QUALITY,    // output quality
                                          m_pitch_and_family,     // pitch and family
                                          m_typeface);            // typeface name
                if(m_cur_font)
                {
                    if(m_num_fonts >= m_max_fonts)
                    {
                        delete [] m_font_names[0];
                        if(m_old_font) ::SelectObject(m_dc, m_old_font);
                        ::DeleteObject(m_fonts[0]);
                        memcpy(m_fonts, 
                               m_fonts + 1, 
                               (m_max_fonts - 1) * sizeof(HFONT));
                        memcpy(m_font_names, 
                               m_font_names + 1, 
                               (m_max_fonts - 1) * sizeof(char*));
                        m_num_fonts = m_max_fonts - 1;
                    }

                    m_font_names[m_num_fonts] = new char[strlen(m_signature) + 1];
                    strcpy(m_font_names[m_num_fonts], m_signature);
                    m_fonts[m_num_fonts] = m_cur_font;
                    ++m_num_fonts;
                    ::SelectObject(m_dc, m_cur_font);
                    m_glyph_rendering = ren_type;
                    m_num_kerning_pairs = 0;
                    update_signature();
                    return true;
                }
            }
        }
        return false;
    }





    //------------------------------------------------------------------------
    bool font_engine_win32_tt::create_font(const char* typeface_, 
                                           glyph_rendering ren_type,
                                           double height_,
                                           double width_,
                                           int weight_,
                                           bool underline_,
                                           bool strikeout_,
                                           DWORD char_set_,
                                           DWORD pitch_and_family_)
    {
        height(height_);
        width(width_);
        weight(weight_);
        underline(underline_);
        strikeout(strikeout_);
        char_set(char_set_);
        pitch_and_family(pitch_and_family_);
        return create_font(typeface_, ren_type);
    }




    //------------------------------------------------------------------------
    void font_engine_win32_tt::update_signature()
    {
        if(m_dc && m_cur_font)
        {
            sprintf(m_signature, 
                    "%s,%u,%d,%d:%dx%d,%d,%d,%d,%d,%d,%d,%d", 
                    m_typeface,
                    m_char_set,
                    int(m_glyph_rendering),
                    m_resolution,
                    m_height,
                    m_width,
                    m_weight,
                    int(m_italic),
                    int(m_underline),
                    int(m_strikeout),
                    int(m_hinting),
                    int(m_flip_y),
                    int(m_pitch_and_family));
            ++m_change_stamp;
        }
    }



    //------------------------------------------------------------------------
    static inline int fx_to_int(const FIXED& p)
    {
        return (int(p.value) << 6) + (int(p.fract) >> 10);
    }


    //------------------------------------------------------------------------
    bool font_engine_win32_tt::prepare_glyph(unsigned glyph_code)
    {
        if(m_dc && m_cur_font)
        {
            int format = GGO_BITMAP;

            switch(m_glyph_rendering)
            {
            case glyph_ren_native_gray8: 
                format = GGO_GRAY8_BITMAP;
                break;

            case glyph_ren_outline:
            case glyph_ren_agg_mono:
            case glyph_ren_agg_gray8:
                format = GGO_NATIVE;
                break;
            }

#ifndef GGO_UNHINTED         // For compatibility with old SDKs.
#define GGO_UNHINTED 0x0100
#endif
            if(!m_hinting) format |= GGO_UNHINTED;
        
            GLYPHMETRICS gm;
            int total_size = GetGlyphOutline(m_dc,
                                             glyph_code,
                                             format,
                                             &gm,
                                             buf_size,
                                             (void*)m_gbuf,
                                             &m_mat2);

            if(total_size < 0) 
            {
                // GetGlyphOutline() fails when being called for
                // GGO_GRAY8_BITMAP and white space (stupid Microsoft).
                // It doesn't even initialize the glyph metrics
                // structure. So, we have to query the metrics
                // separately (basically we need gmCellIncX).
                int total_size = GetGlyphOutline(m_dc,
                                                 glyph_code,
                                                 GGO_METRICS,
                                                 &gm,
                                                 buf_size,
                                                 (void*)m_gbuf,
                                                 &m_mat2);

                if(total_size < 0) return false;
                gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
                total_size = 0;
            }

            m_glyph_index = glyph_code;
            m_advance_x = gm.gmCellIncX;
            m_advance_y = gm.gmCellIncY;

            switch(m_glyph_rendering)
            {
            case glyph_ren_native_mono: 
                decompose_win32_glyph_bitmap_mono(m_gbuf, 
                                                  gm.gmBlackBoxX,
                                                  gm.gmBlackBoxY,
                                                  gm.gmptGlyphOrigin.x,
                                                  m_flip_y ? -gm.gmptGlyphOrigin.y : 
                                                              gm.gmptGlyphOrigin.y,
                                                  m_flip_y,
                                                  m_scanline_bin,
                                                  m_scanlines_bin);
                m_bounds.x1 = m_scanlines_bin.min_x();
                m_bounds.y1 = m_scanlines_bin.min_y();
                m_bounds.x2 = m_scanlines_bin.max_x();
                m_bounds.y2 = m_scanlines_bin.max_y();
                m_data_size = m_scanlines_bin.byte_size(); 
                m_data_type = glyph_data_mono;
                return true;

            case glyph_ren_native_gray8:
                decompose_win32_glyph_bitmap_gray8(m_gbuf, 
                                                   gm.gmBlackBoxX,
                                                   gm.gmBlackBoxY,
                                                   gm.gmptGlyphOrigin.x,
                                                   m_flip_y ? -gm.gmptGlyphOrigin.y : 
                                                               gm.gmptGlyphOrigin.y,
                                                   m_flip_y,
                                                   m_scanline_aa,
                                                   m_scanlines_aa);
                m_bounds.x1 = m_scanlines_aa.min_x();
                m_bounds.y1 = m_scanlines_aa.min_y();
                m_bounds.x2 = m_scanlines_aa.max_x();
                m_bounds.y2 = m_scanlines_aa.max_y();
                m_data_size = m_scanlines_aa.byte_size(); 
                m_data_type = glyph_data_gray8;
                return true;

            case glyph_ren_outline:
                m_path.remove_all();
                if(decompose_win32_glyph_outline(m_gbuf,
                                                 total_size,
                                                 m_flip_y, 
                                                 m_path,
                                                 fx_to_int))
                {
                    rect_d bnd  = m_path.bounding_rect();
                    m_data_size = m_path.byte_size();
                    m_data_type = glyph_data_outline;
                    m_bounds.x1 = int(floor(bnd.x1));
                    m_bounds.y1 = int(floor(bnd.y1));
                    m_bounds.x2 = int(ceil(bnd.x2));
                    m_bounds.y2 = int(ceil(bnd.y2));
                    return true;
                }
                break;

            case glyph_ren_agg_mono:
                m_path.remove_all();
                if(decompose_win32_glyph_outline(m_gbuf,
                                                 total_size,
                                                 m_flip_y, 
                                                 m_path,
                                                 fx_to_int))
                {
                    m_scanlines_bin.prepare(1); // Remove all 
                    m_rasterizer.reset();
                    m_rasterizer.add_path(m_curves);
                    m_rasterizer.render(m_scanline_bin, m_scanlines_bin);
                    m_bounds.x1 = m_scanlines_bin.min_x();
                    m_bounds.y1 = m_scanlines_bin.min_y();
                    m_bounds.x2 = m_scanlines_bin.max_x();
                    m_bounds.y2 = m_scanlines_bin.max_y();
                    m_data_size = m_scanlines_bin.byte_size(); 
                    m_data_type = glyph_data_mono;
                    return true;
                }
                break;

            case glyph_ren_agg_gray8:
                m_path.remove_all();
                if(decompose_win32_glyph_outline(m_gbuf,
                                                 total_size,
                                                 m_flip_y, 
                                                 m_path,
                                                 fx_to_int))
                {
                    m_scanlines_aa.prepare(1); // Remove all 
                    m_rasterizer.reset();
                    m_rasterizer.add_path(m_curves);
                    m_rasterizer.render(m_scanline_aa, m_scanlines_aa);
                    m_bounds.x1 = m_scanlines_aa.min_x();
                    m_bounds.y1 = m_scanlines_aa.min_y();
                    m_bounds.x2 = m_scanlines_aa.max_x();
                    m_bounds.y2 = m_scanlines_aa.max_y();
                    m_data_size = m_scanlines_aa.byte_size(); 
                    m_data_type = glyph_data_gray8;
                    return true;
                }
                break;
            }
        }
        return false;
    }



    //------------------------------------------------------------------------
    void font_engine_win32_tt::write_glyph_to(int8u* data) const
    {
        if(data && m_data_size)
        {
            switch(m_data_type)
            {
            case glyph_data_mono:    m_scanlines_bin.serialize(data); break;
            case glyph_data_gray8:   m_scanlines_aa.serialize(data);  break;
            case glyph_data_outline: m_path.serialize(data);          break;
            }
        }
    }



    //------------------------------------------------------------------------
    bool font_engine_win32_tt::pair_less(const KERNINGPAIR v1, 
                                         const KERNINGPAIR v2)
    {
        if(v1.wFirst != v2.wFirst) return v1.wFirst < v2.wFirst;
        return v1.wSecond < v2.wSecond;
    }


    //------------------------------------------------------------------------
    void font_engine_win32_tt::sort_kerning_pairs()
    {
        pod_array_adaptor<KERNINGPAIR> pairs(m_kerning_pairs, m_num_kerning_pairs);
        quick_sort(pairs, pair_less);
    }



    //------------------------------------------------------------------------
    void font_engine_win32_tt::load_kerning_pairs()
    {
        if(m_dc && m_cur_font)
        {
            if(m_kerning_pairs == 0)
            {
                m_kerning_pairs = new KERNINGPAIR [16384-16];
                m_max_kerning_pairs = 16384-16;
            }
            m_num_kerning_pairs = ::GetKerningPairs(m_dc, 
                                                    m_max_kerning_pairs,
                                                    m_kerning_pairs);

            if(m_num_kerning_pairs)
            {
                // Check to see if the kerning pairs are sorted and
                // sort them if they are not.
                //----------------
                unsigned i;
                for(i = 1; i < m_num_kerning_pairs; ++i)
                {
                    if(!pair_less(m_kerning_pairs[i - 1], m_kerning_pairs[i]))
                    {
                        sort_kerning_pairs();
                        break;
                    }
                }
            }
        }
    }


    //------------------------------------------------------------------------
    bool font_engine_win32_tt::add_kerning(unsigned first, unsigned second,
                                           double* x, double* y)
    {
        if(m_dc && m_cur_font)
        {
            if(m_num_kerning_pairs == 0)
            {
                load_kerning_pairs();
            }

            int end = m_num_kerning_pairs - 1;
            int beg = 0;
            KERNINGPAIR t;
            t.wFirst = (WORD)first;
            t.wSecond = (WORD)second;
            while(beg <= end)
            {
                int mid = (end + beg) / 2;
                if(m_kerning_pairs[mid].wFirst  == t.wFirst &&
                   m_kerning_pairs[mid].wSecond == t.wSecond)
                {
                    *x += m_kerning_pairs[mid].iKernAmount;
                    return true;
                }
                else
                if(pair_less(t, m_kerning_pairs[mid]))
                {
                    end = mid - 1;
                }
                else
                {
                    beg = mid + 1;
                }
            }
            return false;
        }
        return false;
    }



}

