/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   Frame in webkit side
*
*/


#include "config.h"
#include "../../bidi.h"
#include "WebFrameView.h"
#include "WebFrame.h"
#include "WebView.h"
#include "WebCursor.h"
#include "WebFrameBridge.h"
#include "WebCoreGraphicsContext.h"
#include "GraphicsContext.h"
#include "PlatformScrollbar.h"
#include "PageScaler.h"
#include "webkitlogger.h"
#include "BrCtl.h"
#include "SettingsContainer.h"
#include "StaticObjectsContainer.h"
#include "WebTabbedNavigation.h"
#include "WebPagePinchZoomHandler.h"

using namespace WebCore;

WebFrameView::WebFrameView() : 
      m_parent(NULL),
      m_hasBorder(true),
      m_refCount(1),
	  m_isVisible(ETrue)
{
    m_hScrollbar = new WebCore::PlatformScrollbar(this, WebCore::HorizontalScrollbar, WebCore::RegularScrollbar);
    m_vScrollbar = new WebCore::PlatformScrollbar(this, WebCore::VerticalScrollbar, WebCore::RegularScrollbar); 
    m_horizontalScrollBarMode = ScrollbarAuto;
    m_verticalScrollBarMode = ScrollbarAuto;
}

WebFrameView::~WebFrameView() 
{
    delete m_hScrollbar;
    delete m_vScrollbar;
}

void WebFrameView::setTopView(WebView* v)
{
    m_topView = v;
}

void WebFrameView::setWebFrame(WebFrame* f)
{
    m_frame = f;
}

WebFrameView* WebFrameView::initWithFrame(TRect frame)
{
    setRect(frame);
    return this;
}

void WebFrameView::draw(WebCoreGraphicsContext& gc, const TRect& r)
{
    if (m_topView->isPinchZoom())  {
        return;
    }    
    
    TRect vr(visibleRect());
    TRect rect(r);
    if (isScaled() || m_frame->isFrameSet())
        rect.Grow(1,1);             // eliminate rounding errors
    TRect frameRect(m_frameRect);

    rect.Move(-frameRect.iTl);
    rect.Move(m_contentPos);
    if (vr.Intersects(rect)) {
        // save the gc state
        TWebCoreSavedContext saved(gc.save());
        // set the current frame
        gc.setView(*this);

        TPoint cpos(toViewCoords(m_contentPos));

        gc.setOrigin(gc.origin()+toViewCoords(m_frameRect.iTl)-cpos);
        rect.Intersection(vr);
        // calculate clip for zoom level
        TRect clip(toViewCoords(rect));
        
        // set clipping
        if (gc.hasClipping()) {
            TRect t(gc.clippingRect());
            t.Move(-toViewCoords(m_frameRect.iTl)+cpos);
            clip.Intersection(t);
        }
        
        gc.setClippingRect( clip );

        // draw frame content
        m_frame->paintRect(gc, rect);
        gc.cancelClipping();
        gc.setOrigin( gc.origin() + cpos);
        if (m_parent) {
            WebFrameView* parent = m_parent;
            TRect frameClip(0,0,KMaxTInt,KMaxTInt);
            do {
                TRect pr = parent->visibleRect();
                TRect fc(TRect(parent->frameCoordsInViewCoords(pr.iTl), parent->frameCoordsInViewCoords(pr.iBr))); 
                frameClip.Intersection(fc);
                parent = parent->m_parent;
            }
            while(parent);
            frameClip = TRect(viewCoordsInFrameCoords(frameClip.iTl), viewCoordsInFrameCoords(frameClip.iBr)); 
            frameClip = TRect(toViewCoords(frameClip));
            frameClip.Move(-cpos);
            gc.setClippingRect( frameClip );
        }
  
        // draw frame border
        CFbsBitGc& realgc = gc.gc();
        if (m_hasBorder && !m_frame->isFrameSet()) {
            // already moved the origin
            TRect borderRect(TPoint(-1,-1),toViewCoords(m_frameRect).Size());
            borderRect.iBr += TPoint(2,2);
            realgc.SetPenColor(TRgb(0x55,0x55,0x55));
            realgc.SetPenStyle(CGraphicsContext::ESolidPen);
            realgc.SetBrushStyle(CGraphicsContext::ENullBrush);
            realgc.SetPenSize(TSize(1,1));
            realgc.DrawRect(borderRect);
            // double border in bottom/right
            borderRect.iBr += TPoint(1,1);
            realgc.DrawRect(borderRect);
        }

        // draw scrollbars
        rect.Move( -m_contentPos );
        WebCore::GraphicsContext ctx(&gc);
        if (m_vScrollbar->isEnabled())
            m_vScrollbar->paint(&ctx, rect);
        if (m_hScrollbar->isEnabled())
            m_hScrollbar->paint(&ctx, rect);

        gc.cancelClipping();
        gc.restore(saved);
    }
}

void WebFrameView::invalidateRect(const TRect& rect, TBool drawNow)
{
    TRect r(rect);
    if (m_parent) {
        TRect frameRect(m_frameRect);
        r.Move(frameRect.iTl - m_contentPos);
        r.Intersection(frameRect);
        m_parent->invalidateRect(r, drawNow);
    }
    else {
        // fix rounding errors from zooming
        if (isScaled() || m_frame->isFrameSet())
            r.Grow(1,1);
    }

    if (!m_topView->isClosing()) {
        if(drawNow)
            m_topView->syncRepaint(r);
        else
            m_topView->scheduleRepaint(r);
    }
}

void WebFrameView::activate()
{
}

void WebFrameView::setRect(const TRect& rect)
{
    TRect frameRect(rect);

    if (m_frameRect!=frameRect) {

        m_frameRect = frameRect;
        checkScrollbarVisibility();
    }
}

int WebFrameView::getBidiWidth()
{
    TInt bidiWidth = KMaxTInt;
    // don't limit bidi width for low subframes
    if (!(toViewCoords(m_frameRect).Height()<150 && m_parent))
        bidiWidth = m_topView->maxBidiSize().iWidth;

    return bidiWidth;
}

void WebFrameView::checkScrollbarVisibility()
{
    if (!m_parent || m_parent->frame()->isFrameSet()) {
        m_vScrollbar->setEnabled(false);
        m_hScrollbar->setEnabled(false);
        return;
    }
    bool vScrollbar = m_vScrollbar->isEnabled();
    bool hScrollbar = m_hScrollbar->isEnabled();
    
    if (!m_allowsScrolling) {
        m_vScrollbar->setEnabled(false);
        m_hScrollbar->setEnabled(false);
        return;
    }
    TRect frameRect(m_frameRect);
    switch(m_verticalScrollBarMode) {
        default:
        case ScrollbarAlwaysOff:
            vScrollbar = false;
        break;
        case ScrollbarAlwaysOn:
            vScrollbar = true;
        break;
        case ScrollbarAuto:
            vScrollbar = contentSize().iHeight > frameRect.Height();
        break;
    }

    switch(m_horizontalScrollBarMode) {
        default:
        case ScrollbarAlwaysOff:
            hScrollbar = false;
        break;
        case ScrollbarAlwaysOn:
            hScrollbar = false;
        break;
        case ScrollbarAuto:
            hScrollbar = contentSize().iWidth > frameRect.Width();
        break;
    }
    
    m_hScrollbar->setEnabled(hScrollbar);
    m_vScrollbar->setEnabled(vScrollbar);

    TRect r(TPoint(0,0), frameRect.Size() - TSize(vScrollbar? KScrollbarWidth : 0, hScrollbar ? KScrollbarWidth : 0));
    if (hScrollbar) {
        m_hScrollbar->setRect(TRect(TPoint(r.iTl.iX, r.iBr.iY ), TSize(r.Width(), KScrollbarWidth)));
        m_hScrollbar->setProportion(r.Width(), m_contentSize.iWidth);
    }

    if (vScrollbar)  {
        m_vScrollbar->setRect(TRect(TPoint(r.iBr.iX, r.iTl.iY), TSize(KScrollbarWidth, r.Height())));
        m_vScrollbar->setProportion(r.Height(), m_contentSize.iHeight);
    }
}

void WebFrameView::setCursor(CursorTypes cursorType)
{
    if (m_topView && StaticObjectsContainer::instance()->webCursor())
        StaticObjectsContainer::instance()->webCursor()->setCursor(cursorType);
}

void WebFrameView::setParent(MScrollView* parent)
{
    m_parent = static_cast<WebFrameView*>(parent);
}

TRect WebFrameView::rect() const
{
    return m_frameRect;
}

TRect WebFrameView::visibleRect() const
{
    TSize s(m_frameRect.Size());
    return TRect(m_contentPos, s );
}

void WebFrameView::scrollTo(const TPoint& aPoint)
{
    if (m_parent) {
        // tot:fixme frame scrolling when frame-flat is off
        if (m_frame->isIframe()) {
            // iframe, support scrolling
            if (aPoint != m_contentPos) {
                TPoint p(nearestPointInFrame(aPoint));
                m_contentPos = p;
                m_frame->notifyPluginsOfScrolling();
                m_topView->syncRepaint( TRect(0,0,KMaxTInt/2,KMaxTInt/2) );
            }
        }
        else {
            if (aPoint.iX==0 && aPoint.iY==0) {
                // this gets called for all frames during loading, needs to be ignored (breaks some cases though)
                return;
            }

            TPoint p(0, aPoint.iY); // don't to x shifts, they are confusing here
            // subframes don't scroll in the current implementation, scroll the main frame
            p += toDocCoords(rectInGlobalCoords().iTl);
            m_topView->mainFrame()->frameView()->scrollTo(p);
        }
    }
    else {
        // main frame, scroll
        if (aPoint != m_contentPos) {
            TPoint p( nearestPointInFrame(aPoint) );
            TBool copyScroll = m_mayUseCopyScroll;

            if( p != aPoint && m_topView->inFindState() ) {
                p = aPoint;
                m_topView->clearOffScreenBitmap();
                copyScroll = EFalse;
            }

            TPoint from( toViewCoords(m_contentPos) );
            TPoint to( toViewCoords(p) );

            m_topView->scrollBuffer(to, from, copyScroll);
            m_contentPos = p;

            
            m_frame->notifyPluginsOfScrolling();
            
            
            if( m_topView->pageScaler() && m_topView->pageScaler()->Visible())
                m_topView->pageScaler()->DocumentViewportMoved();

            //Update scroll bar , thumb position
            m_topView->updateScrollbars(m_contentSize.iHeight, m_contentPos.iY, m_contentSize.iWidth, m_contentPos.iX);

            if (m_topView->brCtl()->settings()->getNavigationType() == SettingsContainer::NavigationTypeTabbed) {
                int h = (to.iX - from.iX);
                int v = (to.iY - from.iY);
                h = (h != 0) ? ((h > 0) ? 1 : -1) : h;
                v = (v != 0) ? ((v > 0) ? 1 : -1) : v;
                m_topView->tabbedNavigation()->updateCursorPosAfterScroll(core(m_frame.get()), h, v);
            }
            // trigger painting
            m_topView->syncRepaint();
        }
    }
}


bool WebFrameView::needScroll(const TPoint& aPoint)
{
    bool rv = false;
    if (m_parent) {
        if (m_frame->isIframe()) {
            if (aPoint != m_contentPos) {
                rv = true;
            }
        }
        else {
            if (!(aPoint.iX==0 && aPoint.iY==0)){
                 rv = true;
            }
        }
    }
    else {
        // main frame
        if (aPoint != m_contentPos) {
            TPoint p( nearestPointInFrame(aPoint) );
            TBool copyScroll = m_mayUseCopyScroll;

            if( m_contentPos != p ){
                rv = true;
            }
        }
    }
    return rv;
}

TPoint WebFrameView::nearestPointInFrame(const TPoint &aPoint)
{
    if( m_parent && !m_frame->isIframe() ) return aPoint;
    TSize frameSize( m_frameRect.Size() );
    TPoint p(aPoint);
    p.iX = Min(contentSize().iWidth - frameSize.iWidth, p.iX);
    p.iY = Min(contentSize().iHeight - frameSize.iHeight, p.iY);
    p.iX = Max(0, p.iX);
    p.iY = Max(0, p.iY);

    return p;
}

void WebFrameView::resizeContent(const TSize &size)
{

    if (!m_parent) {
        if (m_topView->brCtl()->capabilities()&TBrCtlDefs::ECapabilityFitToScreen) {
            if( size.iHeight != 0 && size.iWidth != 0 ){
                m_topView->updateMinZoomLevel(size);
            }
        }
    }

    if (size != m_contentSize) {

        m_resizeByEngine = ETrue;
        m_contentSize = size;
        checkScrollbarVisibility();
        
        //maybe the content got smaller and we need to scroll back to view?
        TPoint p( nearestPointInFrame(m_contentPos) );
        if (p!=m_contentPos)
            // this will also update scrollbars is necessary
            scrollTo(p);
        else if (!m_parent) {
            // top level
            m_topView->updateScrollbars(m_contentSize.iHeight, m_contentPos.iY, m_contentSize.iWidth, m_contentPos.iX);
        }
    }
}

void WebFrameView::setMayUseCopyScroll(TBool aCopy)
{
    m_mayUseCopyScroll = aCopy;
}

TPoint WebFrameView::convertContentToView(const TPoint& aConvert)
{
    return frameCoordsInViewCoords(aConvert);
}

TPoint WebFrameView::convertViewToContent(const TPoint& aConvert)
{
    return viewCoordsInFrameCoords( aConvert );
}

TBool WebFrameView::hasHorizontalScrollbar() const 
{
    return m_vScrollbar != NULL;
}

TBool WebFrameView::hasVerticalScrollbar() const
{
    return m_hScrollbar != NULL;
}

void WebFrameView::setHorizontalScrollingMode(ScrollbarMode aMode)
{
    m_horizontalScrollBarMode = aMode;
    checkScrollbarVisibility();
}

WebCore::ScrollbarMode WebFrameView::verticalScrollingMode() 
{
    return m_verticalScrollBarMode;
}

void WebFrameView::setVerticalScrollingMode(ScrollbarMode aMode)
{
    m_verticalScrollBarMode = aMode;
    checkScrollbarVisibility();
}

WebCore::ScrollbarMode WebFrameView::horizontalScrollingMode()
{
    return m_horizontalScrollBarMode;
}

void WebFrameView::setScrollingMode(ScrollbarMode aMode)
{
    setHorizontalScrollingMode(aMode);
    setVerticalScrollingMode(aMode);
}

void WebFrameView::setScrollbarsSuppressed(TBool /*aSuppressed*/,TBool /*aRepaintOnUnsuppress*/ )
{
    // ### NOT IMPLEMENTED
}

void WebFrameView::addChild(MWebCoreWidget* aChild)
{
    if(aChild)
        aChild->setParent(this);
}

TInt WebFrameView::scalingFactor() const
{
    if (m_topView)
        return m_topView->scalingFactor();
    return 100;
}

TPoint WebFrameView::viewCoordsInFrameCoords(const TPoint &aPoint)
{
    TPoint pt = aPoint;

    pt += toViewCoords(m_contentPos);
    WebFrameView* pv = m_parent;
    while(pv)
        {
        pt += pv->toViewCoords(pv->m_contentPos);
        pv = pv->m_parent;
        }

    pt -= rectInGlobalCoords().iTl;
    pt = m_topView->mainFrame()->frameView()->toDocCoords(pt);
    return pt;
}

TPoint WebFrameView::frameCoordsInViewCoords(const TPoint &aPoint)
{
    TPoint pt(aPoint);

    pt = m_topView->mainFrame()->frameView()->toViewCoords(aPoint);
    pt += rectInGlobalCoords().iTl;
    pt -= toViewCoords(m_contentPos);
    WebFrameView* pv= m_parent;
    while(pv)
        {
        pt -= pv->toViewCoords(pv->m_contentPos);
        pv = pv->m_parent;
        }
    return pt;
}

TRect WebFrameView::frameCoordsInViewCoords(const TRect &aRect)
{
    TSize s = aRect.Size(); 
    return TRect(frameCoordsInViewCoords(aRect.iTl), toViewCoords(s));
}


TRect WebFrameView::rectInGlobalCoords() const
{
    TRect rect(toViewCoords(m_frameRect));

    for (WebFrameView* p = m_parent; p; p=p->m_parent)
        rect.Move(toViewCoords(p->m_frameRect.iTl));

    return rect;
}

void WebFrameView::setNeedsDisplay(TBool n) 
{
}

void WebFrameView::setFocus(TBool aFocus) 
{
}

void WebFrameView::makeVisible(TBool aVisible) 
{
    m_isVisible = aVisible;
}

TBool WebFrameView::isFocused() const
{
    return ETrue;
}

TBool WebFrameView::isVisible() const
{
    return m_isVisible;
}

TSize WebFrameView::contentSize() const
{
    return m_contentSize;
}

void WebFrameView::setAllowsScrolling(bool allowsScrolling)
{
    m_allowsScrolling = allowsScrolling;
}

void WebFrameView::setMarginWidth(int marginWidth)
{
}

void WebFrameView::setMarginHeight(int marginHeight)
{
}

int WebFrameView::marginWidth() const
{
    return 0;
}

int WebFrameView::marginHeight() const 
{
    return 0;
}

WebFrameView* WebFrameView::viewClassForMIMEType(const WebCore::String& MIMEType) 
{
    return NULL;
}

void WebFrameView::valueChanged(WebCore::Scrollbar*) 
{
}

WebCore::IntRect WebFrameView::windowClipRect() const
{
    return WebCore::IntRect();
}

void WebFrameView::setContentPos(TPoint pt)
{
    m_contentPos = pt;
}

// END OF FILE
