package network;

import java.math.BigInteger;

/**
 * NetPack Klasse
 * Beinhaltet ein byte-Array mit den Daten die gesendet werden sollen,
 * sowie die Möglichkeit eine Sonderzeichen Substitution durchzuführen
 */
public class NetPack
{ 
	// CommandIDs
    public static final int CMD_MAP_SIZE = 35;
    public static final int PACK_SIZE = 32;
    public static final int WAIT = 1;
    public static final int HOME = 2;
    public static final int HALT = 3;
    public static final int RESET = 4;
    public static final int MOVEMOD = 5;
    public static final int MOVEMODSTEP = 6;
    public static final int MOVEPOS = 7;
    public static final int PRG_START= 8;
    public static final int RAMPVEL = 9;
    public static final int RAMPACC = 10;
    public static final int WAITHOME = 11;
    public static final int WAITPOS = 12;
    public static final int SLEEP = 13;
    public static final int GRIP = 14;
    public static final int OFFSET = 15;
    public static final int MOTION_PERC = 16;
    public static final int MOTION_PARAM = 17;
    public static final int OBSTACLE_CIRCLE = 18;
    public static final int OBSTACLE_RECT = 19;
    public static final int SET_CUR_TRANSOBJ = 20;
    public static final int SAVE_DATABASE = 21;
    public static final int REFRESH_DATABASE= 22;
    public static final int LOAD_BITMAP = 23;
    public static final int SAVE_BITMAP= 24;
    public static final int CAMIF = 25;
    public static final int CAMELSE= 26;
    public static final int CAMENDIF= 27;
    public static final int GETPOS= 28;
    public static final int EXITPRG= 29;
    public static final int IMAGEREQUEST=30;
    
    // Parameter-Konstanten
    public static final int PARAM_LIN = 1;
    public static final int PARAM_DIRECT = 2;
    
    //Konstanten
    public static final int PARAM_INT1 = 1;
    public static final int PARAM_INT2 = 2;
    public static final int PARAM_FLOAT1 = 4;
    public static final int PARAM_FLOAT2 = 8;
    public static final int PARAM_POS = 16;
    public static final int PARAM_UNKN1 = 32;
    public static final int PARAM_UNKN2 = 64;
    public static final int PARAM_STRING = 128;
    
    // Wartekonstanten
    public static final int WAIT_2 = 1;
    public static final int WAIT_3 = 2;
    public static final int WAIT_4 = 4;
    
    // Attribute
    private byte nData[];
    private int iPackLen;
    private int iCmdMap[];

    /**
     * Konstruktor der Commando erhält u. ein NetPack erzeugt
     * @param c COmmando zum Zerlegen
     */
    public NetPack(Command c)
    {
        nData = new byte[PACK_SIZE];
        iCmdMap = new int[CMD_MAP_SIZE];
        this.initCmdMap();
        iPackLen = this.substitute(this.createNPack(c));
    }

	/* ##########################################################################
	 * 						ÖFFENTLICHE METHODEN
	 * ##########################################################################*/	  
    
    /**
     * substituiert die Sondereichen 0x02 u. 0x03
     * @param iLen Länge des Paketes
     * @return Länge des endgültigen Pakets
     */
    public int substitute(int iLen){

    	  byte[] cData = new byte[32];
    	  int c=1;
    	  cData[0]=0x02;

    	  for(int i=1;i<iLen-1;i++){
    		if((nData[i]==0x02)||(nData[i]==0x03)||(nData[i]==0x10)){
    			cData[c++]=0x10;
    			cData[c]=(byte) (0x80+nData[i]);
    		} else {
    			cData[c]=nData[i];
    		}
    		++c;
    	  }
    	  cData[c]=0x03;
    	  nData=cData;
    	  return(c+1);
    }    

    /**
	 * Liefert das Byte-Array, das übertragen werden kann
	 * @return  nData Byte-Array zum Übertragen
	 * @uml.property  name="nData"
	 */
    public byte[] getNData()
    {
        return nData;
    }
    
    /**
     * Liefert die Länge des Byte Arrays
     * @return Länge des Pakets
     */
    public int getPackLen()
    {
        return iPackLen;
    }
    
	/* ##########################################################################
	 * 						HILFSMETHODEN
	 * ##########################################################################*/   
    
    /**
     * Zerlegt die Parameter des Commandos in ein Byte-Array
     * @param c Commandi das zerlegt werden soll
     * @return Länge des Paketes
     */
    private int createNPack(Command c)
    {
        nData[0] = 2;
        nData[1] = (byte)c.getCommandID();
        BigInteger bi = new BigInteger(Integer.toString(iCmdMap[c.getCommandID()]));
        int iPtr = 2;
        if(bi.testBit(0)) //Int1 zerlegen
        {
            splitiParam(c.getiParam(), iPtr);
            iPtr += 4;
            //System.out.println("Int1");
        }
        if(bi.testBit(2)) //Float1 zerlegen
        {
            splitfParam(c.getfParam(0), iPtr);
            iPtr += 4;
            //System.out.println("Float1");
        }
        if(bi.testBit(3)) //Float2 zerlegen
        {
            splitfParam(c.getfParam(1), iPtr);
            iPtr += 4;
            //System.out.println("Float2");
        }
        if(bi.testBit(4)) //Position zerlegen
        {
            splitfParam(c.getfParam(2), iPtr);
            iPtr += 4;
            splitfParam(c.getfParam(3), iPtr);
            iPtr += 4;
            splitfParam(c.getfParam(4), iPtr);
            iPtr += 4;
            char sParam[] = c.getsParam();
            nData[iPtr++] = (byte)sParam[0];
            nData[iPtr++] = (byte)sParam[1];
            //System.out.println("Pos");
        }
        if(bi.testBit(7)) //String zerlegen
        {
            char sParam[] = c.getsParam();
            System.out.println(iPtr+"-"+(iPtr+c.getsParam().length));
            for(int i = 0; i < c.getsParam().length; i++)
            {
                nData[iPtr++] = (byte)sParam[i];
            }

            //System.out.println("String");
        }
        nData[iPtr++] = 3;
        return iPtr;
    }

    /**
     * Methode zum Zerlegen eines int-Wertes
     * @param iParam int-Wert der zerlegt werden soll
     * @param i Stelle, an der die Bytes eingefügt werden sollen
     */
    private void splitiParam(int iParam, int i)
    {
        nData[i] = (byte)iParam;
        nData[i + 1] = (byte)(iParam >> 8);
        nData[i + 2] = (byte)(iParam >> 16);
        nData[i + 3] = (byte)(iParam >> 24);
    }

    /**
     * Methode zum Zerlegen eines float-Wertes
     * @param fParam float-Wert der zerlegt werden soll
     * @param i Stelle, an der die Bytes eingefügt werden sollen
     */
    private void splitfParam(float fParam, int i)
    {
        int x = Float.floatToRawIntBits(fParam);
        nData[i] = (byte)x;
        nData[i + 1] = (byte)(x >> 8);
        nData[i + 2] = (byte)(x >> 16);
        nData[i + 3] = (byte)(x >> 24);
    }
    
    /**
     * Methode zum Initialisieren der CommandoMap, in der steht
     * welches Commando welche Parameter enthält
     */
    private void initCmdMap()
    {
    	  iCmdMap[WAIT] = 				PARAM_INT1;
    	  iCmdMap[HOME] = 				PARAM_INT1;
    	  iCmdMap[HALT] = 				0;
    	  iCmdMap[RESET] = 				PARAM_INT1;
    	  iCmdMap[MOVEMOD] = 			PARAM_INT1 + PARAM_FLOAT1;
    	  iCmdMap[MOVEMODSTEP] = 		PARAM_INT1 + PARAM_FLOAT1;
    	  iCmdMap[MOVEPOS] = 			PARAM_POS;
    	  iCmdMap[PRG_START] 	= 		PARAM_INT1 + PARAM_STRING;
    	  iCmdMap[RAMPVEL] = 			PARAM_INT1 + PARAM_FLOAT1;
    	  iCmdMap[RAMPACC] = 			PARAM_INT1 + PARAM_FLOAT1;
    	  iCmdMap[WAITHOME] = 			0;
    	  iCmdMap[WAITPOS] = 			PARAM_INT1 + PARAM_FLOAT1;
    	  iCmdMap[SLEEP] = 				PARAM_INT1;
    	  iCmdMap[GRIP] = 				PARAM_INT1;
    	  iCmdMap[OFFSET] = 			PARAM_POS;
    	  iCmdMap[MOTION_PERC] = 		PARAM_FLOAT1;
    	  iCmdMap[MOTION_PARAM] = 		PARAM_INT1;
    	  iCmdMap[OBSTACLE_CIRCLE] = 	PARAM_FLOAT1 + PARAM_POS;
    	  iCmdMap[OBSTACLE_RECT] = 		PARAM_FLOAT1 + PARAM_FLOAT2 + PARAM_POS;
    	  iCmdMap[SET_CUR_TRANSOBJ] = 	PARAM_INT1;
    	  iCmdMap[SAVE_DATABASE] = 		0;
    	  iCmdMap[REFRESH_DATABASE] = 	0;
       	  iCmdMap[LOAD_BITMAP] = 		PARAM_INT1 + PARAM_STRING;
    	  iCmdMap[SAVE_BITMAP] = 		PARAM_STRING;
       	  iCmdMap[CAMIF] = 			PARAM_POS + PARAM_INT1 + PARAM_FLOAT1 + PARAM_FLOAT2;
    	  iCmdMap[CAMELSE] = 			0;
    	  iCmdMap[CAMENDIF] = 			0;
    	  iCmdMap[GETPOS] = 			0;
    	  iCmdMap[EXITPRG] = 			0;
    	  iCmdMap[IMAGEREQUEST] =		0;
    }

}
