/*
 Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <Carbon/Carbon.h>

#include <IO/Output/TeleTextOutput.hpp>
#include <IO/Manager/IOManager.hpp>
#include <IO/Manager/IOBuffer.hpp>
//#include <toolkit/Window.hpp>

#import <lib/Tuner.h>
#import <lib/aletv_vt.h>
#import <aletv_export.h>

#define VTX_COUNT 256

struct TEXTELEM {
    char  str[42];
    int   fg;
    int   bg;
    int   len;
    int   line;
    int   x,y;
};

static int vtx_colors[8] = { blackColor, redColor, greenColor, yellowColor,
                             blueColor, magentaColor, cyanColor, whiteColor };

extern "C" void fmt_page(struct Export *e, struct fmt_page *pg, struct vt_page *vtp);

static struct Export * export_fmt;

static struct TEXTELEM * vtx_to_tt(struct vt_page *vtp)
{
    export_fmt = export_open("ascii");

    static struct TEXTELEM tt[VTX_COUNT];
    int t,x,y,color,lcolor;
    struct fmt_page pg[1];
    struct fmt_char l[W+2];
#define L (l+1)
    
    t = 0;
    fmt_page( export_fmt,pg,vtp);
    memset(tt,0,sizeof(tt));
    for (y = 0; y < H; y++) {
        for (x = 0; x < W; x++) {
            struct fmt_char c = pg->data[y][x];
            switch (c.ch) {
            case 0x00:
            case 0xa0:
                c.ch = ' ';
                break;
            case 0x7f:
                c.ch = '*';
                break;
            case BAD_CHAR:
                c.ch = '?';
                break;
            default:
                if (c.attr & EA_GRAPHIC)
                    c.ch = '#';
                break;
            }
            L[x] = c;
        }
        /* delay fg and attr changes as far as possible */
        for (x = 0; x < W; ++x)
            if (L[x].ch == ' ') {
                L[x].fg = L[x-1].fg;
                L[x].attr = L[x-1].attr;
            }
        
        /* move fg and attr changes to prev bg change point */
        for (x = W-1; x >= 0; x--)
            if (L[x].ch == ' ' && L[x].bg == L[x+1].bg) {
                L[x].fg = L[x+1].fg;
                L[x].attr = L[x+1].attr;
            }
        
        /* now render the line */
        lcolor = -1;
        tt[t].line = y;
        tt[t].len  = 0;
        for (x = 0; x < W; x++) {
            color = (L[x].fg&0x0f) * 10 + (L[x].bg&0x0f);
            if (color != lcolor) {
                if (-1 != lcolor)
                    if (tt[t].len) {
                        t++;
                        tt[t].line = y;
                    }
                lcolor = color;
            }
            tt[t].str[tt[t].len++] = L[x].ch;
            tt[t].fg = vtx_colors[L[x].fg&0x0f];
            tt[t].bg = vtx_colors[L[x].bg&0x0f];
        }
        if (tt[t].len)
            t++;
    }
    return tt;
}

static void DrawAt( int x, int y , const char * str, int fg, int bg )
{
    Rect r;
    int s,i;
    int len = strlen( str );
    
    FontInfo info;
    
    GetFontInfo( &info);
    
    s =  info.ascent+info.descent;
    

    TextFont( fixedFont );
    TextFace( bold );
    
    ForeColor( bg  );

    SetRect( &r, x * s, y * s,  (x+len) * s, (y+1) * s );
    
    PaintRect( & r );
    
    ForeColor( fg );
    
    for( i = 0; i < len; i++ ) { 
    
        MoveTo( (x+i) * s, (y+1) * s - info.descent );
    
        DrawChar( str[i] ); //, 0, 1 );
    }
}


#if 0
class VTWindow : public CWindow {

    public:

        typedef CWindow super;

        TeleTextOutput * mOwner;
        int              mPage, mSubPage;
 
        VTWindow( TeleTextOutput * owner, int width, int height ) 
            : CWindow( "Teletext", "Teletext" ), mOwner( owner ) {

            mPage    = 0x100;
            mSubPage = 0;
            
            create( 0, 50, 50, width + 50, height + 50 );

            IOManager::instance().sendCommand( "Tuner", "setChannel", "23" );

        }
        
        
        virtual ~VTWindow() {
        }

        virtual void draw( const Rect & r ) {

            TeleTextInput::VTPage * vtPage = mOwner->findPage( 0, mPage, mSubPage );

            if( ! vtPage ) {
                printf("VTWindow::draw vtPage %x.%x is null\n", mPage, mSubPage );
                return;
            }
            
            TEXTELEM * tt = vtx_to_tt( &vtPage->page );
            
            if( ! tt ) {
                printf("VTWindow::draw tt is null\n");
                return;
            }
            
            WindowRef window = (WindowRef) windowRef();
            
            for ( int i = 0; tt[i].len; i++) {

                if( tt[i].line >= 0 ) {
                        
                    int line = tt[i].line;
                    int j = i;
                    int x = 0;

                    while( tt[j].line == line ) {

                        printf("VTWindow::draw line(%d) x(%d) y(%d) fg(%d) bg(%d) str(%s)\n", 
                            tt[j].line, x, line, tt[j].fg, tt[j].bg, tt[j].str  );

                        DrawAt( x, line, tt[j].str, tt[j].fg, tt[j].bg  );
                        
                        x += strlen( tt[j].str );
                        
                        j++;
                    }
                }
            }
        }
        
        virtual void resize( int width, int height ) {
        }
        
        void KeyDown( char key, char keyCode, unsigned modifiers ) {

            printf("VTWindow::keyDown key(%x) code(%x) modifiers(%x)\n", key, keyCode, modifiers );

            super::KeyDown( key, keyCode, modifiers );
            
            switch(key){

            case 27:
                exit(0);
                break;
            case '+':
                IOManager::instance().sendCommand( "Tuner", "changeChannel", "+1");
                break;
            case '-':
                IOManager::instance().sendCommand( "Tuner", "changeChannel", "-1" );
                break;
            case 0x1e:
                mOwner->showPage( mPage + 1 , 0 );
                break;
            case 0x1f:
                mOwner->showPage( mPage - 1 , 0 );
            break;
            }            
        }
        
};
#endif

TeleTextOutput::TeleTextOutput( const string & aName, IODeviceID anID )
 : IOOutput( aName, anID ), mWorkThread( aName )
{
    init();
}

TeleTextOutput::~TeleTextOutput()
{
    finit();
}

bool TeleTextOutput::start() 
{ 
    if( mInited && ! mStarted && isAttached()) {
        
        mStarted = true;

#if 0
        mWindow->show();

        FontInfo info;
    
        GetFontInfo( &info);

        int s =  info.ascent+info.descent;

        SizeWindow( (WindowRef) mWindow->windowRef(), 40*s,25*s, true );
#endif
    }

    return mStarted;
}

bool TeleTextOutput::stop() 
{ 
    mStarted = false;
    
    return mStarted;
}

void TeleTextOutput::showPage( int pageNo, int subNo )
{
#if 0
    mWindow->mPage    = pageNo;
    mWindow->mSubPage = subNo;
    
    mWindow->redraw();
#endif
}

size_t TeleTextOutput::frameSize()
{
    return 40*25;
}

void TeleTextOutput::init()
{
#if 0
    mWindow = new VTWindow( this, 360, 288 );
#endif
    mInited = true;
}

void TeleTextOutput::finit()
{
#if 0
    delete mWindow;
#endif
    mWindow = 0;

    IOManager::instance().unregisterNode ( this );

}

bool TeleTextOutput::updateFrom( IOBuffer * ioBuf )
{
#if 0
    TeleTextInput::VTPage * page = (TeleTextInput::VTPage*)ioBuf->getData();
    
    //printf("TeleTextOutput::updateFrom got page channel(%d) page(%x) sub(%x)\n", page->channel, page->page.pgno, page->page.subno );
    
    addPage( page );
    
    if( page->page.pgno == mWindow->mPage && page->page.subno == mWindow->mSubPage ) {
        showPage( page->page.pgno, page->page.subno );
    }
#endif
    return true;
}
 
TeleTextInput::VTPage * TeleTextOutput::findPage( int channel, int pageNo, int subPageNo )
{
    lock();

    PageList::iterator iter( mPageList.begin() );
    
    while( iter != mPageList.end() ) {
    
        TeleTextInput::VTPage * p = *iter;
        
        if( p->channel == channel && p->page.pgno ==  pageNo && p->page.subno ==  subPageNo) {
            unlock();
            return p;
        }
        iter++;
    }
    
    unlock();
    
    return 0;
}

void TeleTextOutput::addPage( TeleTextInput::VTPage * page )
{
    if( page ) {
            
        printf("adding page %x sub %x\n", page->page.pgno, page->page.subno );
    
        TeleTextInput::VTPage * p = findPage( page->channel, page->page.pgno, page->page.subno );
    
        if( p == 0 ) {
    
            lock();
    
            p = new TeleTextInput::VTPage;
        
            *p = *page;
            
            mPageList.push_back( p );

            unlock();

        }
        else {
            * p = *page;
        }
                
        showPage( page->page.pgno, page->page.subno  );
    }
}

static void initTeleTextOutputPlugin()
{
    TeleTextOutput * ow = new TeleTextOutput( "TeleTextOutput", 0 );
    
    IOManager::instance().registerNode (ow );
}


