/*
 * SSA_VideoClient.java
 *
 * Created on 22 Март 2006 г., 17:08
 */

/**
 *
 * @author voit
 *///----------------------------------------------------------------------------//

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.awt.image.*;
import java.util.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;

//----------------------------------------------------------------------------//

public class SSA_VideoClient extends Applet implements ActionListener
{
    private final static String versionText = "2.0.0.0";

    private int widthScreen, heightScreen, newwidthScreen=0, newheightScreen=0;
    private SSA_MainCanvas canvas;

    protected SSA_DebugFrame deb = null;

    private SSA_ReadThread reader = null;
    private DataInputStream inStream = null;

    private boolean connect;

    private Scrollbar scrollHorzMove, scrollVertMove, scrollZoom;

    private int rcvPacket[] = new int[0x4000];

    private int image[];
	private int isColor = 1; ///Флаг по которому определяем цветная картинка или нет
    private Socket serverSock;
    private int dataSize;
    private SSA_Compressor compress;
    private MemoryImageSource memSource = null;

    // Контролы.

    private Choice choiceSpeed = new Choice();

    private final static int QUEUE_LEN = 20;

    private final static String infoText = "Coded by Ycuken, SSA, 2004";

    private int totalCounter = 0, queuePointer, queueCounter;
    private int totalSize = 0, queueImageSize[] = new int[QUEUE_LEN];
    private long timeConnect, totalBeginTime, queueTimeUnpack[] = new long[QUEUE_LEN];
    private long timeToReconnect = 200000;

    private int color = 0x80;
    private String statusString = "";

    private int topCanvas, topScrollHorz, DependOnLablelsCountTopScrollHorz = 15, heightScroll = 20, topScrollZoom;
	
	///////////Labels values
	private Label LabelZoom;
    private static final int FRAME_TEXT_LABELS_COUNT = 4;
    private Label[] FrameTextLabel = new Label[FRAME_TEXT_LABELS_COUNT];
    private static final int FRAME_STATISTIC_LABELS_COUNT = 4;
    private SSA_Label[] FrameStatisticLabel = new SSA_Label[FRAME_STATISTIC_LABELS_COUNT]; 
 //private Label News = new Label();
    private static final int HTML_STRINGS_NAMES_COUNT = 4;//Кол - во строк для считывания с HTML
    private String HTMLStringsNames[] = new String[HTML_STRINGS_NAMES_COUNT];
	
	private static final int STR_VERSION = 0;
	private static final int STR_AVERAGE_SPEED = 1;
	private static final int STR_AVERAGE_FRAME_SIZE = 2;
	private static final int STR_AVERAGE_FRAME_SIZE_PIECE = 3;
 
    private int widthApplet, heightApplet;
    private Button  btnSetCameraPosition;
    private boolean showStatistic = false, rotationDevice;
    /////////////////////////////////////
	
    private String  senderName;
    private String  script_prefix;

    ////////////////////////////////////////////////////////////////////////////
    //
    public void stop()
    {
        reader.interrupt();

        if( reader.getRunning() )
        {
            reader.setRunning( false );

            while( !reader.getStopped() );
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    public void destroy()
    {
        if( connect && (serverSock != null) )
        {
            try
            {
                serverSock.close();
            }
            catch( IOException e )
            {
                e.printStackTrace();
            }
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
	//
    ////////////////////////////////////////////////////////////////////////////
    public void AddingLabels()
	{
	  
	  int i;
      int OffsetCounter = -13;
      int XPosition = 265; 
  
      HTMLStringsNames[0] = "strVersion";
      HTMLStringsNames[1] = "strAverageSpeed";
      HTMLStringsNames[2] = "strAverageFrameSize";
      HTMLStringsNames[3] = "strAverageFrameSizePiece";
  
      for(i=0;i<=FRAME_TEXT_LABELS_COUNT-1;i++)
      FrameTextLabel[i] = new Label();
  
      for(i=0;i<=FRAME_STATISTIC_LABELS_COUNT-1;i++)
      FrameStatisticLabel[i] = new SSA_Label();  
    
      for(i=0;i<=HTML_STRINGS_NAMES_COUNT-1;i++)
       if(getParameter(HTMLStringsNames[i]) != null) 
       {
        OffsetCounter += 13;
        FrameTextLabel[i].setText(getParameter(HTMLStringsNames[i])); 
	    FrameStatisticLabel[i].ItsLabelName = HTMLStringsNames[i];
	
        FrameTextLabel[i].setBounds(5,5 + OffsetCounter, 250, 13); 
	    FrameStatisticLabel[i].setBounds(XPosition,5 + OffsetCounter, 70, 13);	
	
        FrameTextLabel[i].setAlignment(Label.LEFT); 
	    FrameStatisticLabel[i].setAlignment(Label.LEFT);		
	
	    add(FrameTextLabel[i]);
	    add(FrameStatisticLabel[i]);
		DependOnLablelsCountTopScrollHorz += 13;
		showStatistic = true;
       }


	}
    ////////////////////////////////////////////////////////////////////////////
    //
    public void init()
    {
        ////////////////////////////////////////////////////////////////////////
        int i;
        Dimension dim;
        byte[] streamID = new byte[7];
        senderName = getParameter( "sender" );
        
       
        script_prefix = "";
        script_prefix = getParameter( "prefix" );           
        if (script_prefix == null) script_prefix = "";
         

        if( getParameter( "debug" ).equalsIgnoreCase( "TRUE" ) )
            deb = new SSA_DebugFrame( "Debug frame", this, true );
        else
            deb = new SSA_DebugFrame( "Debug frame", this, false );

        rotationDevice = getParameter( "control" ).equalsIgnoreCase( "TRUE" );

        connectToServer();
        widthScreen = getSize().width-5;
        heightScreen = getSize().height-69;

        if( rotationDevice )
        {
            widthApplet = widthScreen + heightScroll + 4;
            heightApplet = 4 + 65 + heightScroll + heightScreen + 5 + heightScroll + 5;
        }
        else
        {
            widthApplet = widthScreen + 4;
            heightApplet = 4 + 65 + heightScreen;
        }

        if( widthApplet < 320 )
            widthApplet = 320;

        setSize(387, 356);

        image = new int[768 * 576];

        // Отключение менеджера компоновки.
        setLayout( null );

        setBackground( Color.lightGray );
        setFont( new Font( "Monospaced", Font.PLAIN, 12 ) );


        // Вычисление координат компонентов.



        if( connect )
        {
			////get parameters from HTML and adding labels
            AddingLabels();
			
        if( showStatistic )
            topScrollHorz = DependOnLablelsCountTopScrollHorz;
        else
            topScrollHorz = 2; //45

        if( !rotationDevice )
            topScrollHorz -= heightScroll;

        topCanvas = topScrollHorz + heightScroll;
        topScrollZoom = topCanvas + heightScreen + 5;			
		
            FrameStatisticLabel[STR_VERSION].SetText(versionText, HTMLStringsNames[STR_VERSION] );
		    ///////////////////	

            canvas = new SSA_MainCanvas( this );
            canvas.setSize( widthScreen, heightScreen );
            canvas.setLocation( 2, topCanvas );

            add( canvas );

            if( rotationDevice )
            {
                scrollHorzMove = new Scrollbar( Scrollbar.HORIZONTAL, 50, 40, 0, 140 );
                scrollHorzMove.setBounds( 2, topScrollHorz, widthScreen, heightScroll );

                add( scrollHorzMove );

                scrollVertMove = new Scrollbar( Scrollbar.VERTICAL, 50, 40, 0, 140 );
                scrollVertMove.setBounds( widthScreen + 2, topCanvas, heightScroll, heightScreen );

                add( scrollVertMove );

                LabelZoom = new Label( "Zoom:", Label.LEFT );
                LabelZoom.setBounds( 5, topScrollZoom, 40, 20 );

                add( LabelZoom );

                scrollZoom = new Scrollbar( Scrollbar.HORIZONTAL, 50, 40, 0, 140 );
                scrollZoom.setBounds( 45, topScrollZoom, widthScreen - 150, heightScroll );

                add( scrollZoom );

                btnSetCameraPosition = new Button( "Set" );
                btnSetCameraPosition.setBounds( widthScreen - 100, topScrollZoom, 100, 20 );

                add( btnSetCameraPosition );

                // Регистрация кнопки в интерфейсе ActionListener.
                btnSetCameraPosition.addActionListener( this );
            }

            compress = new SSA_Compressor(this);

            memSource = new MemoryImageSource( widthScreen, heightScreen, image, 0, widthScreen );
            memSource.setAnimated( true );

            canvas.setImage( createImage( memSource ) );
        }

    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    public void start()
    {
        ////////////////////////////////////////////////////////////////////////
        long tm;
        int i;
        ////////////////////////////////////////////////////////////////////////

        if( connect )
        {
            tm = System.currentTimeMillis();

            for( i = 0; i < QUEUE_LEN; i++ )
            {
                queueImageSize[i] = 0;
                queueTimeUnpack[i] = tm;
            }

            queuePointer = QUEUE_LEN - 1;
            queueCounter = 0;

            if( reader != null )
                reader.start();

            totalBeginTime = System.currentTimeMillis();
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    public void update( Graphics g )
    {
        paint( g );
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    public void paint( Graphics g )
    {
        ////////////////////////////////////////////////////////////////////////
        int index1, index2, index3;
        int width, height;
        int size = 0, i;
        float avg1, avg2, speed;
        long totalTime = 0;
        String str1, str2, str3;
        ////////////////////////////////////////////////////////////////////////

        showStatus( statusString );

        for( i = 0; i < QUEUE_LEN; i++ )
            size += queueImageSize[i];

        // Время начала распаковки считается, что QUEUE_LEN кадров назад.

        if( queuePointer >= QUEUE_LEN - 1 )
            totalTime = System.currentTimeMillis() - queueTimeUnpack[0];
        else
            totalTime = System.currentTimeMillis() - queueTimeUnpack[queuePointer + 1];

        if( totalCounter != 0 )
            avg1 = (float) totalSize / (float) totalCounter / 1000;
        else
            avg1 = 0;

        if( queueCounter != 0 )
            avg2 = (float) size / (float) queueCounter / 1000;
        else
            avg2 = 0;

        if( totalTime != 0 )
            speed = (((float) queueCounter * 1000) / (float) totalTime);
        else
            speed = 0;

        str1 = Float.toString( avg1 );
        index1 = str1.indexOf( "." );
        str2 = Float.toString( avg2 );
        index2 = str2.indexOf( "." );
        str3 = Float.toString( speed );
        index3 = str3.indexOf( "." );
        
    /*    
        //Прорисовка рамки компонента.
        width = widthApplet;
        height = heightApplet;

        

        g.setColor( Color.gray );
        g.drawLine( 0, 0, width - 2, 0 );
        g.drawLine( 0, 0, 0, height - 2 );

        g.setColor( Color.black );
        g.drawLine( 1, 1, width - 3, 1 );
        g.drawLine( 1, 1, 1, height - 3 );

        g.setColor( Color.lightGray );
        g.drawLine( 1, height - 2, width - 2, height - 2 );
        g.drawLine( width - 2, 1, width - 2, height - 2 );

        g.setColor( Color.white );
        g.drawLine( 0, height - 1, width - 1, height - 1 );
        g.drawLine( width - 1, 0, width - 1, height - 1 );

        g.setColor( Color.black );
        //g.drawString( versionText, 5, 20 );

     */

        if( connect )
        {
            if( showStatistic )
            {
            if( index3 + 3 < str3.length() )
                str3 = str3.substring( 0, index3 + 3 ) + "fps";
            else
                str3 = str3.substring( 0, index3 ) + ".00fps";

            FrameStatisticLabel[STR_AVERAGE_SPEED].SetText( str3, HTMLStringsNames[STR_AVERAGE_SPEED]);
			

                if( index1 + 3 < str1.length() )
                    str1 = str1.substring( 0, index1 + 3 ) + "Kb";
                else
                    str1 = str1.substring( 0, index1 ) + ".00Kb";

                if( index2 + 3 < str2.length() )
                    str2 = str2.substring( 0, index2 + 3 ) + "Kb";
                else
                    str2 = str2.substring( 0, index2 ) + ".00Kb";

                FrameStatisticLabel[STR_AVERAGE_FRAME_SIZE].SetText( str1, HTMLStringsNames[STR_AVERAGE_FRAME_SIZE]);
                FrameStatisticLabel[STR_AVERAGE_FRAME_SIZE_PIECE].SetText( str2, HTMLStringsNames[STR_AVERAGE_FRAME_SIZE_PIECE]);
            }
        }
        else
        {
            g.setColor( Color.black );
            g.drawString( statusString, 5, 37 );
            
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    protected int convertByteToInt( byte b )
    {
        if (b >= 0)
            return b;
        else
            return (int) (b & 0x7F) + 0x80;
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    protected int recvBytes( int number )
    {
        ////////////////////////////////////////////////////////////////////////
        int numDwords, i, p, val;
        ////////////////////////////////////////////////////////////////////////

        numDwords = number >> 2;
        p = 0;

        do
        {
            try
            {
                val = 0;

                for( i = 0; i < 4; i++ )
                    val |= (inStream.read()) << (8 * i);
            }
            catch( Exception e )
            {
                return -1;
            }

            rcvPacket[p] = val;

            p++;
        } while( p < numDwords );

        val = 0;

        for( i = 0; i < (number & 3); i++ )
        {
            try
            {
                val |= (convertByteToInt(inStream.readByte())) << (8 * i);
            }
            catch( Exception e )
            {
                return -1;
            }
        }

        rcvPacket[p] = val;

        return 0;
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    protected boolean readyPacket()
    {
        ////////////////////////////////////////////////////////////////////////
        int ret;
        long timeLive;
        Image resizedImage;
        Dimension d;

        int i;
        ////////////////////////////////////////////////////////////////////////

        timeLive = System.currentTimeMillis() - timeConnect;

        //deb.msg( "Ready Packet. totalCounter = " + totalCounter + "; totalSize = " + totalSize + "; timeLive = " + timeLive );

        // Установка в кольцо начального времени распаковки.

        queuePointer++;

        if( queuePointer >= QUEUE_LEN )
            queuePointer = 0;

        queueTimeUnpack[queuePointer] = System.currentTimeMillis();

        if( timeLive > timeToReconnect )
            connectToServer();

        if( recvPacketHeader() == 0 )
        {
            totalCounter++;
            totalSize += dataSize;

            // Установка в кольцо размера текущего кадра.

            queueCounter++;

            if( queueCounter > QUEUE_LEN )
                queueCounter = QUEUE_LEN;

            queueImageSize[queuePointer] = dataSize;

            ret = compress.decompressImageColor( rcvPacket, image, inStream, dataSize, isColor );

            if( ret != SSA_General.DECOMPRESS_OK )
                return false;

            if( widthScreen != compress.widthImage || heightScreen != compress.heightImage )
            {
                widthScreen = compress.widthImage;
                heightScreen = compress.heightImage;

                if( rotationDevice )
                {
                    widthApplet = widthScreen + heightScroll + 4;
                    heightApplet = 4 + 65 + heightScroll + heightScreen + 5 + heightScroll + 5;
                }
                else
                {
                    widthApplet = widthScreen + 4;
                    heightApplet = 4 + 65 + heightScreen;
                }

                //if( widthApplet < 320 )
                    //widthApplet = 320;
                if(newwidthScreen!=0 && newheightScreen!=0)
                    setSize( newwidthScreen, newheightScreen );

                // Вычисление координат компонентов.

                if( showStatistic )
                    topScrollHorz = 67;
                else
                    topScrollHorz = 0;

                if( !rotationDevice )
                    topScrollHorz -= heightScroll;

                topCanvas = topScrollHorz + heightScroll;
                topScrollZoom = topCanvas + heightScreen + 5;

                if( rotationDevice )
                {
                    scrollHorzMove.setBounds( 2, topScrollHorz, widthScreen, heightScroll );
                    scrollVertMove.setBounds( widthScreen + 2, topCanvas, heightScroll, heightScreen );
                    LabelZoom.setBounds( 5, topScrollZoom, 40, 20 );
                    scrollZoom.setBounds( 45, topScrollZoom, widthScreen - 150, heightScroll );
                    btnSetCameraPosition.setBounds( widthScreen - 100, topScrollZoom, 100, 20 );
                }
                memSource = new MemoryImageSource( widthScreen, heightScreen, image, 0, widthScreen );
                
                d = canvas.getSize();
                memSource.setAnimated( true );
                resizedImage = createImage( memSource );
                
                canvas.setImage( resizedImage.getScaledInstance(d.width, d.height, Image.SCALE_FAST) );
                
            }
            else
            {
                memSource = new MemoryImageSource( widthScreen, heightScreen, image, 0, widthScreen );
                
                memSource.setAnimated( true );
                resizedImage = createImage( memSource );
                d = canvas.getSize();
                canvas.setImage( resizedImage.getScaledInstance(d.width, d.height, Image.SCALE_FAST) );
                memSource.newPixels();
            }
                

            
            canvas.repaint();

            // Перерисовка статистики.
            repaint();
            
            return true;
        }
        else
        {
            if( connectToServer() )
                return true;
            else
            {
                disconnect();

                return false;
            }
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    protected int recvPacketHeader()
    {
        /*Header представляет собой структуру:
        struct HEADER
        {
            DWORD size;     //Размер заголовка (без data1, data2)
            DWORD dataSize; //Размер блока данных
            DWORD data1;    //
            DWORD data2;    //
        };
        */

        if( recvBytes( 16 ) == 0 )
        {
            //deb.msg( "DWORD0 = " + rcvPacket[0] );
            //deb.msg( "DWORD1 = " + rcvPacket[1] );
            //deb.msg( "DWORD2 = " + rcvPacket[2] );
            //deb.msg( "DWORD3 = " + rcvPacket[3] );

            if( rcvPacket[0] != 8 )
                return - 1;

            // Третий бит в data2 показывает отсутствие BMPHEADER'а в пакете.
            if( (rcvPacket[3] & 8) == 0 )
            {
                dataSize = rcvPacket[1] - 48;

                // Удаление из пакета BMPHEADERа(40 байт).
                return recvBytes(40);
            }
            else
                dataSize = rcvPacket[1] - 8;

            return 0;
        }
        else
            return -1;
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    protected void disconnect()
    {
        ////////////////////////////////////////////////////////////////////////
        int i;
        ////////////////////////////////////////////////////////////////////////

        connect = false;

        statusString = "Dis: Соединение разорвано с сервером";

        deb.msg( "Dis: Соединение разорвано с сервером" );

        for( i = 0; i < widthScreen * heightScreen; i++ )
            image[i] = 0xFF808080;

        memSource.newPixels();

        canvas.repaint();
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    public void actionPerformed( ActionEvent e )
    {
        Object src = e.getSource();

        if( src == btnSetCameraPosition )
            setCameraPosition();
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    public boolean connectToServer()
    {
        ////////////////////////////////////////////////////////////////////////
        URL url;

        boolean ret = false;
        ////////////////////////////////////////////////////////////////////////

        //deb.msg( "connectToServer()" );

        try
        {    
            if( inStream != null )
            {
                inStream.close();
                inStream = null;

                connect = false;
            }
			deb.msg("sender"+senderName);
            url = new URL( getCodeBase() + script_prefix + "stream.exe?" +senderName );
			
            deb.msg( url.toString() );
			
            inStream = new DataInputStream( url.openStream() );

            String str = inStream.readLine();

            deb.msg( str );
			
            if( str.equalsIgnoreCase( "OK" ) )
            {
				
                if( reader == null )
                    reader = new SSA_ReadThread( this );
				
                reader.setNewStream( inStream );
				
                connect = true;
            }

            ret = true;
        }
        catch( IOException e )
        {
            ret = false;

            deb.msg( "Ошибка передачи команды в stream.exe" );
        }

        timeConnect = System.currentTimeMillis();

        return ret;
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    public boolean setCameraPosition()
    {
        ////////////////////////////////////////////////////////////////////////
        URL url;

        int x, y, z, code;

        BufferedReader bufferedReader;

        boolean ret = false;
        ////////////////////////////////////////////////////////////////////////

        x = scrollHorzMove.getValue();
        y = 100 - scrollVertMove.getValue();
        z = 100 - scrollZoom.getValue();

        code = x * 1000000 + y * 1000 + z;

        try
        {
            url = new URL( getCodeBase() + "web701.exe?" + code );

            deb.msg( url.toString() );

            bufferedReader = new BufferedReader( new InputStreamReader( url.openStream() ) );

            String str = bufferedReader.readLine();

            deb.msg( str );

            ret = true;
        }
        catch( IOException e )
        {
            ret = false;

            deb.msg( "Ошибка передачи команды в web701.exe" );
        }

        scrollHorzMove.setValue( 50 );
        scrollVertMove.setValue( 50 );
        scrollZoom.setValue( 50 );

        return ret;
    }

    //
    ////////////////////////////////////////////////////////////////////////////

}
