import java.util.Arrays;
import java.util.*;
import java.io.*;
import java.nio.file.*;

class theKey implements java.io.Serializable {
    protected int[]  p; //The permutation
    protected byte[] m; //The mask
}

public class build_store16 {
    protected int[]  p;
    protected byte[] m;

    public static void main(String[] args){
        build_store16 akstore=new build_store16();
        akstore.test();
    }
    public build_store16(){
        p=Shuffle();
        m=AMask();
        try {
            theKey tK=new theKey();
            tK.p=p;
            tK.m=m;
            Files.deleteIfExists(Paths.get("./mykey.dat"));
            ObjectOutputStream out=new ObjectOutputStream( new FileOutputStream("mykey.dat"));
            out.writeObject(tK);
            out.close();
        } catch (Exception e) {
        }
    }

    private int[] Shuffle(){
        int N = 16;
        int r;
        int[][] a = new int[N][2];
        // insert integers 0..N-1
        for (int i = 0; i < N; i++) {
            a[i][0] = i;a[i][1]=0;
        }
        for (int i = 0; i < N; i++) {
            if (a[i][1]==0) {
                a[i][1]=1;
                while (true) {
                    r = (int) (Math.random() * (N));
                    if (a[r][1]==0)break;
                }
                int swap = a[r][0];
                a[r][0] = a[i][0];
                a[r][1] =1;
                a[i][0] = swap;
            }
        }
        int [] p=new int[N];
        for (int i=0;i<N;i++)p[i]=a[i][0];
        System.out.println("The Shuffel\n"+Arrays.toString(p));
        return p;
    }

    private byte[] AMask(){
        byte[] theMask=new byte[8];
        for (int i=0;i<8;i=i+1) {
            theMask[i] = (byte) (Math.random() * (256));
        }
        return theMask;
    }
    ////////////Testing//////////////////////
    public void test(){
        String s="Code it.";  //8 bytes
        System.out.println(s);
        System.out.println(Decode(Encode(s)));
    }

    private  byte[] permute(byte[] s, int[] p ){
        byte top=(byte)240;
        byte bottom=(byte)15;
        byte[] start=new byte[16];
        for (int i=0;i<8;i++) {
            start[2*i]=getTop(s[i]);
            start[2*i+1]=(byte)(s[i]&bottom);
        }
        System.out.println("The start\n"+Arrays.toString(start));
        byte[] encode16=new byte[16];
        for (int i=0;i<16;i=i+1) {
            encode16[i]=start[p[i]];
        }
        byte[] encoded=new byte[8];
        for (int i=0;i<8;i++) {
            encoded[i]=(byte)((encode16[2*i]<<4)+encode16[2*i+1]);
        }
        return encoded;
    }
    private byte[] mask(byte[] theBytes,byte[] theMask){
        byte[] theEncodedBytes=new byte[8];
        for (int i=0;i<8;i=i+1) {
            theEncodedBytes[i]=(byte)(theBytes[i]^theMask[i]);
        }
        return theEncodedBytes;
    }

    public String Encode(String s){
        byte[] theBytes=new byte[s.length()];;
        try{theBytes = s.getBytes( "ISO-8859-1");}
        catch(Exception e){System.out.println("can't do that");}
        System.out.println("Unmasked\n"+Arrays.toString(theBytes));
        byte[] theMaskedBytes=mask(theBytes,m);
        System.out.println("Masked\n"+Arrays.toString(theMaskedBytes));
        byte[] ThePermutedBytes=permute(theMaskedBytes,p);
        System.out.println("Permuted\n"+Arrays.toString(ThePermutedBytes));
        byte[] theNewBytes=mask(ThePermutedBytes,m);
        System.out.println("Masked Again\n"+Arrays.toString(theNewBytes));
       try{ return new String(theNewBytes, "ISO-8859-1"); }
         catch(Exception e){System.out.println("can't do that either");}
         return "bah";
    }
    public String Decode(String s){
        System.out.println("______________\nNow Decode it\n______________");
        return Encode(s);
    }
    public byte getTop(byte tester){
       // byte tester=(byte)240;
        byte shifted=0;
        int the_part=0;
        int all=0;
        for (int i=4;i<8;i++) {
            the_part=(GetBit(tester,i))?(int)Math.pow(2,(i-4)) :0;
            // Is the bit in position i 1 or 0
            // If a 1 add 2 to the i-4.
            all=all+the_part;
        }
        return(byte)all ;
    }

    static boolean GetBit(byte thebyte, int position){
         return(1 == ((thebyte >> position) & 1));
    }
}