/////////////////////////////////////////////////////////////////////////////
// Name:        m_image.cpp
// Purpose:     wxHtml module for displaying images
// Author:      Vaclav Slavik
// RCS-ID:      $Id: m_image.cpp,v 1.24 2001/02/06 14:23:44 juliansmart Exp $
// Copyright:   (c) 1999 Vaclav Slavik, Joel Lucsy
// Licence:     wxWindows Licence
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
#pragma implementation
#endif

#include "wx/wxprec.h"

#include "wx/defs.h"
#if wxUSE_HTML && wxUSE_STREAMS

#ifdef __BORDLANDC__
#pragma hdrstop
#endif

#ifndef WXPRECOMP
#include "wx/wx.h"
#endif

#include "wx/html/forcelnk.h"
#include "wx/html/m_templ.h"


#include "wx/image.h"
#include "wx/dynarray.h"

#include <math.h>
#include <float.h>

FORCE_LINK_ME(m_image)




WX_DECLARE_OBJARRAY(int, CoordArray);
#include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
WX_DEFINE_OBJARRAY(CoordArray);


//--------------------------------------------------------------------------------
// wxHtmlImageMapAreaCell
//                  0-width, 0-height cell that represents single area in imagemap
//                  (it's GetLink is called from wxHtmlImageCell's)
//--------------------------------------------------------------------------------

class wxHtmlImageMapAreaCell : public wxHtmlCell
{
    public:
        enum celltype { CIRCLE, RECT, POLY };
    protected:
        CoordArray coords;
        celltype type;
        int radius;
    public:
        wxHtmlImageMapAreaCell( celltype t, wxString &coords, double pixel_scale = 1.0);
        virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
};





wxHtmlImageMapAreaCell::wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::celltype t, wxString &incoords, double pixel_scale )
{
    int i;
    wxString x = incoords, y;

    type = t;
    while ((i = x.Find( ',' )) != -1) 
    {
        coords.Add( (int)(pixel_scale * (double)wxAtoi( x.Left( i ).c_str())) );
        x = x.Mid( i + 1 );
    }
    coords.Add( (int)(pixel_scale * (double)wxAtoi( x.c_str())) );
}

wxHtmlLinkInfo *wxHtmlImageMapAreaCell::GetLink( int x, int y ) const
{
    switch (type) 
    {
        case RECT:
            {
                int l, t, r, b;

                l = coords[ 0 ];
                t = coords[ 1 ];
                r = coords[ 2 ];
                b = coords[ 3 ];
                if (x >= l && x <= r && y >= t && y <= b) 
		        {
                    return m_Link;
                }
                break;
            }
        case CIRCLE:
            {
                int l, t, r;
                double  d;

                l = coords[ 0 ];
                t = coords[ 1 ];
                r = coords[ 2 ];
                d = sqrt( (double) (((x - l) * (x - l)) + ((y - t) * (y - t))) );
                if (d < (double)r) 
		        {
                    return m_Link;
                }
            }
            break;
        case POLY:
            {
                if (coords.GetCount() >= 6) 
		        {
                    int intersects = 0;
                    int wherex = x;
                    int wherey = y;
                    int totalv = coords.GetCount() / 2;
                    int totalc = totalv * 2;
                    int xval = coords[totalc - 2];
                    int yval = coords[totalc - 1];
                    int end = totalc;
                    int pointer = 1;

                    if ((yval >= wherey) != (coords[pointer] >= wherey)) 
		            {
                        if ((xval >= wherex) == (coords[0] >= wherex)) 
			            {
                            intersects += (xval >= wherex) ? 1 : 0;
                        } 
			            else 
				        {
                            intersects += ((xval - (yval - wherey) *
                                            (coords[0] - xval) /
                                            (coords[pointer] - yval)) >= wherex) ? 1 : 0;
                        }
                    }

                    while (pointer < end) 
		            {
                        yval = coords[pointer];
                        pointer += 2;
                        if (yval >= wherey) 
			            {
                            while ((pointer < end) && (coords[pointer] >= wherey)) 
			                {
                                pointer += 2;
                            }
                            if (pointer >= end) 
			                {
                                break;
                            }
                            if ((coords[pointer - 3] >= wherex) ==
                                    (coords[pointer - 1] >= wherex)) {
                                intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
                            } 
			                else 
					        {
                                intersects +=
                                    ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
                                      (coords[pointer - 1] - coords[pointer - 3]) /
                                      (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
                            }
                        } 
			            else 
				        {
                            while ((pointer < end) && (coords[pointer] < wherey)) 
			                {
                                pointer += 2;
                            }
                            if (pointer >= end) 
			                {
                                break;
                            }
                            if ((coords[pointer - 3] >= wherex) ==
                                    (coords[pointer - 1] >= wherex)) 
				            {
                                intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
                            } 
			                else 
					        {
                                intersects +=
                                    ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
                                      (coords[pointer - 1] - coords[pointer - 3]) /
                                      (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
                            }
                        }
                    }
                    if ((intersects & 1) != 0) 
		            {
                        return m_Link;
                    }
                }
            }
            break;
    }

    if (m_Next) 
    {
        wxHtmlImageMapAreaCell  *a = (wxHtmlImageMapAreaCell*)m_Next;
        return a->GetLink( x, y );
    }
    return NULL;
}








//--------------------------------------------------------------------------------
// wxHtmlImageMapCell
//                  0-width, 0-height cell that represents map from imagemaps
//                  it is always placed before wxHtmlImageMapAreaCells
//                  It responds to Find(wxHTML_COND_ISIMAGEMAP)
//--------------------------------------------------------------------------------


class wxHtmlImageMapCell : public wxHtmlCell
{
    public:
        wxHtmlImageMapCell( wxString &name );
    protected:
        wxString m_Name;
    public:
        virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
        virtual const wxHtmlCell *Find( int cond, const void *param ) const;
};


wxHtmlImageMapCell::wxHtmlImageMapCell( wxString &name )
{
    m_Name = name ;
}

wxHtmlLinkInfo *wxHtmlImageMapCell::GetLink( int x, int y ) const
{
    wxHtmlImageMapAreaCell  *a = (wxHtmlImageMapAreaCell*)m_Next;
    if (a)
        return a->GetLink( x, y );
    return wxHtmlCell::GetLink( x, y );
}

const wxHtmlCell *wxHtmlImageMapCell::Find( int cond, const void *param ) const
{
    if (cond == wxHTML_COND_ISIMAGEMAP) 
    {
        if (m_Name == *((wxString*)(param)))
            return this;
    }
    return wxHtmlCell::Find(cond, param);
}





//--------------------------------------------------------------------------------
// wxHtmlImageCell
//                  Image/bitmap
//--------------------------------------------------------------------------------

class wxHtmlImageCell : public wxHtmlCell
{
    public:
        wxBitmap *m_Image;
        double m_Scale;
        wxHtmlImageMapCell *m_ImageMap;
        wxString m_MapName;

        wxHtmlImageCell(wxFSFile *input, int w = -1, int h = -1, double scale = 1.0, int align = wxHTML_ALIGN_BOTTOM, wxString mapname = wxEmptyString);
        ~wxHtmlImageCell() {if (m_Image) delete m_Image; }
        void Draw(wxDC& dc, int x, int y, int view_y1, int view_y2);
        virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
};




//--------------------------------------------------------------------------------
// wxHtmlImageCell
//--------------------------------------------------------------------------------

wxHtmlImageCell::wxHtmlImageCell(wxFSFile *input, int w, int h, double scale, int align, wxString mapname) : wxHtmlCell()
{
    wxImage *img;
    int ww, hh, bw, bh;
    wxInputStream *s = input->GetStream();
    
    m_Scale = scale;
    img = new wxImage(*s, wxBITMAP_TYPE_ANY);
    m_Image = NULL;
    if (img && (img->Ok())) 
    {
        ww = img->GetWidth();
        hh = img->GetHeight();
        if (w != -1) bw = w; else bw = ww;
        if (h != -1) bh = h; else bh = hh;

        m_Width = (int)(scale * (double)bw);
        m_Height = (int)(scale * (double)bh);

        if ((bw != ww) || (bh != hh)) 
	    {
            wxImage img2 = img->Scale(bw, bh);
            m_Image = new wxBitmap(img2.ConvertToBitmap());
        } 
	    else
            m_Image = new wxBitmap(img->ConvertToBitmap());
        delete img;
    }
    switch (align) 
    {
        case wxHTML_ALIGN_TOP :
            m_Descent = m_Height;
            break;
        case wxHTML_ALIGN_CENTER :
            m_Descent = m_Height / 2;
            break;
        case wxHTML_ALIGN_BOTTOM :
        default :
            m_Descent = 0;
            break;
    }

    m_ImageMap = NULL;
    m_MapName = mapname;
    SetCanLiveOnPagebreak(FALSE);
}



void wxHtmlImageCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2)
{
    if (m_Image)
    {
        double us_x, us_y;
        dc.GetUserScale(&us_x, &us_y);
        dc.SetUserScale(us_x * m_Scale, us_y * m_Scale);
    
//      dc.DrawBitmap(*m_Image, x + m_PosX, y + m_PosY, (m_Image->GetMask() != (wxMask*) 0));
        dc.DrawBitmap(*m_Image, (int) ((x + m_PosX) / m_Scale), 
                                (int) ((y + m_PosY) / m_Scale), TRUE);
        dc.SetUserScale(us_x, us_y);
    }
    wxHtmlCell::Draw(dc, x, y, view_y1, view_y2);
}

wxHtmlLinkInfo *wxHtmlImageCell::GetLink( int x, int y ) const
{
    if (m_MapName.IsEmpty())
        return wxHtmlCell::GetLink( x, y );
    if (!m_ImageMap) 
    {
        wxHtmlContainerCell *p, *op;
        op = p = GetParent();
        while (p) 
	    {
            op = p;
            p = p->GetParent();
        }
        p = op;
        wxHtmlCell *cell = (wxHtmlCell*)p->Find( wxHTML_COND_ISIMAGEMAP, (const void*)(&m_MapName));
        if (!cell) 
	    {
            ((wxString&)m_MapName).Clear();
            return wxHtmlCell::GetLink( x, y );
        }
        {   // dirty hack, ask Joel why he fills m_ImageMap in this place
            // THE problem is that we're in const method and we can't modify m_ImageMap
            wxHtmlImageMapCell **cx = (wxHtmlImageMapCell**)(&m_ImageMap);
            *cx = (wxHtmlImageMapCell*)cell;
        }
    }
    return m_ImageMap->GetLink( x, y );
}



//--------------------------------------------------------------------------------
// tag handler
//--------------------------------------------------------------------------------

TAG_HANDLER_BEGIN(IMG, "IMG,MAP,AREA")

    TAG_HANDLER_PROC(tag)
    {
        if (tag.GetName() == wxT("IMG")) 
	    {
            if (tag.HasParam(wxT("SRC"))) 
	        {
                int w = -1, h = -1;
                int al;
                wxFSFile *str;
                wxString tmp = tag.GetParam(wxT("SRC"));
                wxString mn = wxEmptyString;

                str = m_WParser->GetFS()->OpenFile(tmp);
                if (tag.HasParam(wxT("WIDTH"))) tag.ScanParam(wxT("WIDTH"), wxT("%i"), &w);
                if (tag.HasParam(wxT("HEIGHT"))) tag.ScanParam(wxT("HEIGHT"), wxT("%i"), &h);
                al = wxHTML_ALIGN_BOTTOM;
                if (tag.HasParam(wxT("ALIGN"))) 
		        {
                    wxString alstr = tag.GetParam(wxT("ALIGN"));
                    alstr.MakeUpper();  // for the case alignment was in ".."
                    if (alstr == wxT("TEXTTOP")) al = wxHTML_ALIGN_TOP;
                    else if ((alstr == wxT("CENTER")) || (alstr == wxT("ABSCENTER"))) al = wxHTML_ALIGN_CENTER;
                }
                if (tag.HasParam(wxT("USEMAP"))) 
		        {
                    mn = tag.GetParam( wxT("USEMAP") );
                    if (mn[ (unsigned int) 0 ] == wxT('#')) 
		            {
                        mn = mn.Mid( 1 );
                    }
                }
                wxHtmlImageCell *cel = NULL;
                if (str) 
		        {
                    cel = new wxHtmlImageCell(str, w, h, m_WParser->GetPixelScale(), al, mn);
                    cel->SetLink(m_WParser->GetLink());
                    m_WParser->GetContainer()->InsertCell(cel);
                    delete str;
                }
            }
        }
        if (tag.GetName() == wxT("MAP")) 
	    {
            m_WParser->CloseContainer();
            m_WParser->OpenContainer();
            if (tag.HasParam(wxT("NAME"))) 
	        {
                wxString tmp = tag.GetParam(wxT("NAME"));
                wxHtmlImageMapCell *cel = new wxHtmlImageMapCell( tmp );
                m_WParser->GetContainer()->InsertCell( cel );
            }
            ParseInner( tag );
            m_WParser->CloseContainer();
            m_WParser->OpenContainer();
        }
        if (tag.GetName() == wxT("AREA")) 
	    {
            if (tag.HasParam(wxT("SHAPE"))) 
	        {
                wxString tmp = tag.GetParam(wxT("SHAPE"));
                wxString coords = wxEmptyString;
                tmp.MakeUpper();
                wxHtmlImageMapAreaCell *cel = NULL;
                if (tag.HasParam(wxT("COORDS"))) 
		        {
                    coords = tag.GetParam(wxT("COORDS"));
                }
                if (tmp == wxT("POLY")) 
		        {
                    cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::POLY, coords, m_WParser->GetPixelScale() );
                } 
		        else if (tmp == wxT("CIRCLE")) 
		        {
                    cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::CIRCLE, coords, m_WParser->GetPixelScale() );
                } 
		        else if (tmp == wxT("RECT")) 
			    {
                    cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::RECT, coords, m_WParser->GetPixelScale() );
                }
                if (cel != NULL && tag.HasParam(wxT("HREF"))) 
		        {
                    wxString tmp = tag.GetParam(wxT("HREF"));
                    wxString target = wxEmptyString;
                    if (tag.HasParam(wxT("TARGET"))) target = tag.GetParam(wxT("TARGET"));
                    cel->SetLink( wxHtmlLinkInfo(tmp, target));
                }
                if (cel != NULL) m_WParser->GetContainer()->InsertCell( cel );
            }
        }

        return FALSE;
    }

TAG_HANDLER_END(IMG)



TAGS_MODULE_BEGIN(Image)

    TAGS_MODULE_ADD(IMG)

TAGS_MODULE_END(Image)


#endif
