function CUI( target )
{

    system.registerEvent("onwrite"); 
    system.registerEvent("ontitle");
    system.registerEvent("onclear");
    system.registerEvent("onresize");
    system.registerEvent("onresizeConsole"); 
    system.registerEvent("onScrollUp");    
    system.registerEvent("onScrollDown");        
    system.registerEvent("ondeleteChar");
    system.registerEvent("onreadChar");
    system.registerEvent("ongetConsoleSize");        
    system.registerEvent("ongetLastInput");   
    system.registerEvent("onpointerToLeft");
    system.registerEvent("onpointerToRight");
    system.registerEvent("onpointerToUp");
    system.registerEvent("onpointerToDown");
    
    system.addEventListener("onresize", resize);
    system.addEventListener("onwrite", append);
    system.addEventListener("onclear", clear);
    system.addEventListener("onresizeConsole", resizeTo);
    system.addEventListener("onreadChar", insert);
    system.addEventListener("ondeleteChar", remove);
    system.addEventListener("onScrollUp", scrollUp);
    system.addEventListener("onScrollDown", scrollDown);
    system.addEventListener("ongetConsoleSize", screenLines);
    system.addEventListener("ongetLastInput", getLastInput);
    system.addEventListener("ongetConsoleSize", offsetLines);
    system.addEventListener("ontitle", setTitle);
    system.addEventListener("onpointerToLeft", left);
    system.addEventListener("onpointerToRight", right);
    system.addEventListener("onpointerToUp", up);
    system.addEventListener("onpointerToDown", down);
    
    var storage = new Array();
    var mask;
    var cursor;
    var cell, size;    
    var text;
    var currentPercents = new Size( 100, 100 );
    var stream = new Array( "" );
    var breakLines = {};

    var current, last, lastFixed, beginScreen;
    var debag, debagText; 
    
    if ( target.innerText == undefined )
    {
        text = document.createTextNode("");
        target.appendChild( text );
    }
    var debag = document.createElement("SPAN");
    var debagText = document.createTextNode("_");

    debag.style.color = "red";
    debag.style.backgroundColor = "white";
    target.style.top = 0;
    target.style.left = 0;
    try
    {
        target.parentNode.style.color = target.currentStyle.color;    
        target.parentNode.style.fontSize = target.currentStyle.fontSize;
    }   catch ( e )
        {
            target.parentNode.style.color = window.getComputedStyle(target, "").color; 
            target.parentNode.style.fontSize = window.getComputedStyle(target, "").fontSize;
        }
    mask = document.createElement("PRE");
    mask.appendChild( document.createTextNode("_") );
    document.body.appendChild( mask );
    try 
    { 
        mask.style.color = "red";
        mask.style.fontSize = target.currentStyle.fontSize;
        cell = new Size( mask.clientWidth, mask.clientHeight );       
    }   catch( e ) 
        { 
            mask.style.fontSize = window.getComputedStyle(target, "").fontSize;
            cell = new Size( mask.clientWidth, mask.clientHeight);   
        }
    document.body.removeChild( mask );  
    cursor = new Cursor( target.parentNode , cell );
    cursor.show();
    current = new Position( 0, 0 );
    lastFixed = new Position( 0, 0 );
    beginScreen = 0;
    last = new Position( 0, 0 );
    resize();
    cursor.show();


    function resizeTo ( percents )
    {
        try
        {
            currentPercents = percents;
            resize();
        }
        catch( e )
        {
        }
    }
                   
    function up( )
                    {
                        if ( current.Y() == 0 )
                            return false;
                        var next = new Position( current.X(), current.Y() - 1 );
                        if ( next.less( lastFixed ) || next.equals( lastFixed ) )
                            return false;
                        cursor.visible();
                        current = next;
                        cursorToCurrent();
                        return true;
                    }
    
    function down( )
                    {
                        var next;
                        next = new Position( current.X(), current.Y() + 1 );
                        if ( last.less( next ) )
                            return false;
                        cursor.visible();
                        current = next;
                        cursorToCurrent();                        
                        return true;
                    }
                    
    function left( )
                    {
                        var next;             
                        if (  (current.X() == 0)&&(current.Y() == 0) )
                            return false;
                        if ( current.X() == 0 )
                        {
                            next = new Position( size.Width() - 1, current.Y() - 1 );
                        }
                        else
                        {
                            next = new Position( current.X() - 1, current.Y() );
                        }
                        if ( next.less( lastFixed ) || next.equals( lastFixed ) )
                            return false;
                        cursor.visible();
                        current = next;
                        cursorToCurrent();
                        return true;
                    }

    function right( )
                    {
                        var next;         
                        if ( current.X() == size.Width()-1 )
                        {
                            next = new Position( 0, current.Y() + 1 );
                        }
                        else
                        {
                            next = new Position( current.X() + 1, current.Y() );
                        }
                        if ( last.less( next ) )
                            return false;
                        cursor.visible();
                        current = next;
                        cursorToCurrent();
                        return true;
                    }
                    
    function append ( str )
                    {
                        var i;
                        if (( stream[ last.Y() ] == "" )&&( str.charAt(0) == "\n" ))
                            stream[ last.Y() ] += " ";
                        str = str.replace( /\n(?=\n)/g, "\n ");
                        var lines = str.split("\n");
                        var continuation = lines[0].substr( 0, size.Width() - last.X() );
                        stream[ last.Y() ] += continuation;
                        var tail = lines[0].substr( size.Width() - last.X() );
                        if ( tail )
                        {
                            stream = stream.concat( formatting( tail ) );
                        }
                        for ( i = 1; i < lines.length; ++i )
                        {
                            breakLines[ stream.length - 1 ] = true;
                            stream = stream.concat( formatting( lines[ i ] ) );
                        }
                        if ( (stream[ stream.length - 1 ].length == 0) )
                        {    
                            lastFixed = new Position( size.Width() - 1, stream.length - 2 );
                        }
                        else
                        {
                            lastFixed = new Position(
                                                    stream[ stream.length - 1 ].length - 1, 
                                                    stream.length-1
                                                    );
                        }
                        if ( stream[ stream.length -1 ].length == size.Width() )
                        {
                            last = new Position( 0 , stream.length ); 
                        }
                        else
                        {
                            last = new Position( stream[ stream.length -1 ].length, stream.length -1 );
                        }
                        current = new Position( last.X(), last.Y() );
                        cursorToCurrent();
                        repaint();
                    }
    
    function remove( mode )
    {
        var length, y, temp;
        if ( mode == "back" )
        {
            temp = previous( current );
            if ( temp.less( lastFixed ) || temp.equals( lastFixed ) )
                return;
            current = temp;
        }
        length = stream[ current.Y() ].length;
        if ( current.equals( last ) )
            return;
        stream[ current.Y() ] = stream[ current.Y() ].substr( 0, current.X() ) +
                                stream[ current.Y() ].substr( current.X() + 1 );
        if ( length >= size.Width() )
        {
            for ( y = current.Y(); (( y < stream.length-1 )&&( length == size.Width() )); ++y )
            {
                length = stream[ y + 1 ].length;
                temp = stream[ y + 1 ].charAt( 0 );
                stream[ y ] += temp;
                stream[ y + 1 ] = stream[ y + 1 ].substr( 1 );
            }
        }
        last = previous( last );
        cursorToCurrent();
        repaint();
    }
    
    function insert( symbol )
    {
        if ( symbol == "\n" )
            return;
        if ( current.equals( last ) )
            addChar( symbol );
        else
            insertChar( symbol );
        last = next( last );
        current = next( current );
        cursor.visible();
        cursorToCurrent();
        repaint();
    }
    
    function addChar( c )
    {
        if ( stream[ last.Y() ] != undefined )
            stream[ last.Y() ] += c;
        else
            stream.push( c );
    }
    
    function insertChar( c )
    {
        var y, temp;
        stream[ current.Y() ] = stream[ current.Y() ].substr( 0, current.X() ) + c +
                                       stream[ current.Y() ].substr( current.X() );
        if ( stream[ current.Y() ].length > size.Width() )
        {
            temp = stream[ current.Y() ].charAt( stream[ current.Y() ].length - 1 );
            stream[ current.Y() ] = stream[ current.Y() ].slice( 0, -1);
            for ( y = current.Y() + 1; ( y < stream.length )&&( stream[ y ].length >= size.Width() ); ++y )
            {
                stream[ y ] = temp + stream[ y ];
                temp = stream[ y ].charAt( stream[ y ].length - 1 );
                stream[ y ] = stream[ y ].slice( 0, -1);
            }
            if ( y == stream.length )
            {
                stream.push( temp );
            }
            else
            {
                stream[ y ] = temp + stream[ y ];
            }
        }
    }
    
    function screenLines( event )
    {
        var height = size.Height();
        if ( typeof( event ) == "object" )
        {
            event.screenLines = height;
        }
        return height;
    }
    
    function offsetLines( event )
    {
        var height = last.Y() + size.Height() + 1;
        if ( typeof( event ) == "object" )
            event.offsetLines = height;
        return height;
    }
                        
    
    function scrollDown( shift )
    {
        var overflow;
        overflow = beginScreen + size.Height() + shift + 1 - offsetLines();
        if ( overflow > 0 )
            shift -= overflow;
        if ( shift <= 0 )  return false;
        beginScreen = beginScreen + shift;
        if ( current.Y() < beginScreen ) 
            cursor.hide();
        else
            if ( current.Y() < beginScreen + size.Height()) 
            {
                cursor.show();
                cursor.moveTo( current.X(), current.Y() - beginScreen );
            }
        repaint();
        return true;
    }
                        
    function scrollUp( shift )
    {
        var overflow;
        overflow = shift - beginScreen;
        if ( overflow > 0 )
            shift -= overflow;
        if ( shift <= 0 ) return false;
        beginScreen = beginScreen - shift;
        if ( current.Y() >= beginScreen + size.Height() )
            cursor.hide();
        else
            if ( current.Y() >= beginScreen )
            {
                cursor.show();
                cursor.moveTo( current.X(), current.Y() - beginScreen );
            }
        repaint();
        return true;
    }

    function cursorToCurrent()
    {
        if ( current.Y()  > beginScreen + size.Height() - 1 ) 
        {
            scrollDown( current.Y() - ( beginScreen + size.Height() - 1 ) );     
        }
        if ( current.Y() < beginScreen ) 
        {
            scrollUp( beginScreen - current.Y() );        
        }
        cursor.moveTo( current.X() , current.Y() -  beginScreen );        
    }
    
    function reformat( oldSize )
    {
        var i = 0, j = 0;
        var temp;
        var format = new Array();
        var newCurrent, newLastFixed, newbreakLines = {}, lastBreakLine = -1;
        while ( i < stream.length )
        {
            temp = "";
            do
            {
                if ( current.Y() == j )
                {
                    newCurrent = new Position( ( temp.length + current.X() + 1 ) % size.Width() - 1 ,
                               format.length + Math.floor(( temp.length + current.X() + 1 )/size.Width() ));
                    if ( newCurrent.X() == -1 ) newCurrent = new Position( size.Width() - 1, newCurrent.Y() - 1 );
                }
                if ( lastFixed.Y() == j )
                {
                    newLastFixed = new Position( ( temp.length + lastFixed.X() + 1 ) % size.Width() - 1,
                                format.length + Math.floor(( temp.length +  lastFixed.X() + 1 )/size.Width()) );
                    if ( newLastFixed.X() == -1 ) newLastFixed = new Position( size.Width() - 1, 
                                                                              newLastFixed.Y() - 1 );
                }                
                temp += stream[ j ];
                ++j;
            }
            while( ( !breakLines[ j-1 ] )&&( j < stream.length ) );
            format = format.concat( formatting( temp ) );
            if ( breakLines[ j-1 ] )
            {
                newbreakLines[ format.length - 1 ] = true;
                lastBreakLine = format.length - 1;
            }
            i = j;
        }
        if ( (breakLines[ lastFixed.Y() ])&&(lastBreakLine != -1) )
        {
            newLastFixed = new Position( size.Width() - 1, lastBreakLine );
        }
        if ( newLastFixed )
            lastFixed = newLastFixed;
        if ( newCurrent )
            current = newCurrent;            
        breakLines = newbreakLines;
        cursorToCurrent();
        stream = format;
        if ( stream[ stream.length - 1 ].length == size.Width() )
            last = new Position( 0, stream.length );
        else
            last = new Position( stream[ stream.length - 1 ].length, stream.length - 1);     
    }
    
    function formatting( str )
    {
        var parts;
        var length = size.Width();
        if ( str.length <= length )
            return new Array( str );
        var regexp = new RegExp(".{1," + length + "}", "g" );
        parts = str.match( regexp );
        return parts;
    }

    function resize()
    {
        try
        {
            saveSize = size;
            target.style.width  = ( document.body.clientWidth - 50 ) / 100 * currentPercents.Width();
            target.style.height = ( document.body.clientHeight - 45 ) / 100 * currentPercents.Height();
            size = new Size(
                            Math.round( target.clientWidth / cell.Width() ),
                            Math.round( target.clientHeight / cell.Height() )
                           );
            target.style.height = size.Height() * cell.Height() + 6;
            target.style.width = size.Width() * cell.Width();        
            reformat( saveSize );  
            repaint();
        }
        catch( e )
        {
        }
    }
    
    function repaint(  )
    {
        var buffer = "";
        var i;
        for ( i = beginScreen; ( ( i < size.Height() + beginScreen )&&( i < stream.length ) ); ++i )
        {
            buffer += stream[ i ] + "\n";
        }
        if ( ! (text == undefined) )
        {
            text.data = buffer;
        }
        else
        {
            target.innerText = buffer;
        }    
    }
    
    function clear( )
    {
        stream = new Array( "" );
        storage = new Array();
        breakLines = {};
        current = new Position( 0, 0 );
        lastFixed = new Position( 0, 0 );
        beginScreen = 0;
        last = new Position( 0, 0 );       
    }
    
    function setTitle( text )
    {
        append("\n" + text + " >");
    }
    
    function getLastInput( event )
    {
        var end   = previous( last );
        var begin = next( lastFixed );
        var i;
        if ( end.less( begin ) )
        {
            event.input = "";
            append("\n");
            return;
        }
        debagText.data = begin.Y() + "|" + begin.X();
        event.input = stream[ begin.Y() ].substring( begin.X() );
        for ( i = begin.Y() + 1; i < end.Y(); ++i )
        {
            event.input += stream[ i ];
        }
        if ( begin.Y() != end.Y() )
            event.input += stream[ end.Y() ].substring( 0, end.X() );
        if ( (storage.length != 0)&&(storage[length - 1] != event.input) )
            storage.push( event.input );
        append("\n");
        lastFixed = new Position( size.Width() - 1 , end.Y() );
        return;
    }
    
    function next( pos )
    {
        if ( pos.X() >= size.Width() - 1 )
            return new Position( 0, pos.Y() + 1 );
        else
            return new Position( pos.X() + 1, pos.Y() );
    }
    
    function previous( pos )
    {
        if (( pos.X() == 0 )&&( pos.Y() == 0))
            return pos;
        if ( pos.X() ==  0 )
            return new Position( size.Width() - 1, pos.Y() - 1 );
        else
            return new Position( pos.X() - 1, pos.Y() );        
    }
    
}
