import java.io.*;
public class LFSR {
    static FileWriter out = null; //открытый файл
    static BufferedReader in = null; //шифрованный    
    static FileWriter keyIn = null; //запись ключа при шифрации
    static BufferedReader keyOut = null; //чтение ключа при дешифрации   
    
    public static void main(String[] args) throws IOException{ 
        try{
            out = new FileWriter(args[1], false); //файл для записи шифротекста
            in = new BufferedReader(new FileReader(args[0]));  //файл открытого текста
            if(args[3].equals("-cod")) coding(args[2]); //шифрация, передаем путь для ключа
            else if(args[3].equals("-decod")) decoding(args[2]); //дешифрация
                 else {
                     System.out.println("\nЗадан неверный флаг "+ args[3] + " допустимы -cod (шифрация) и -decod (дешифрация)");
                     System.exit(3);
                 }
        } catch(ArrayIndexOutOfBoundsException ex){
            System.out.println("\nШифрование: LFSR In.txt Out.txt. Key.txt -cod, где\n"
            + "In - файл, который требуется зашифровать\n"
            + "Out - файл, куда будет записан шифрованный текст\n"
            + "Key - файл, куда будет записан 16-битный ключ\n"
            + "-cod - команда шифрации\n"
            + "\nДешифрация: LFSR In.txt Out.txt. Key.txt -decod, где\n"
            + "In - файл, который требуется расшифровать\n"
            + "Out - файл, куда будет записан расшифрованный текст\n"
            + "Key - файл с 16-битным ключом\n"
            + "-decod - команда дешифрации\n");
            System.exit(1);
        } catch (FileNotFoundException ex){
            System.out.println("\nФайл " + args[0] + " не найден.");
            System.exit(2);
        } 
        System.out.println("Successfully");
    } 
    public static int [] getKey() throws IOException{
        int key[] = new int[16];
        java.util.Random rand = new java.util.Random();
        for(int r = 0; r < key.length; r++)           
             key[r]=rand.nextInt(2);       
        return key;
    }
    public static long getGamma(int key[]) { //гамма 32 бит
        int n = key.length; //длина регистра   
        String g = "";        
        int S = 0; 
        for (int t = 0; t < 32; t++) { // формирование гаммы с использованием многочлена
            S = (key[n-1]);
            S = ((( (S >> 15) ^ (S >> 4) ^ (S >> 2) ^ (S >> 1) ^ S ) & 0x00000001 ) << 15 ) | (S >> 1);
            for (int i = n-1; i > 0; i--) //сдвиг ячеек регистра
                key[i] = key[i-1];           
            key[0] = S;  //запись сформированного бита
            if(S>0) g += 1; //формирование гаммы
            else g += 0;
        }
        return Long.parseLong(g, 2);    
    }
    public static void overlayGamma(long gamma) throws IOException{
        String line = "";
        while((line = in.readLine()) != null)
           for(int i = 0; i < line.length(); i++)
               out.write((char)(line.charAt(i) ^ gamma));      
    }
    public static void coding(String path) throws IOException{          
        int key[] = getKey(); //формирование ключа, инициализация регистра
        try{
            keyIn = new FileWriter(path, false);
            for(int i = 0; i < key.length; i++) //запись ключа в файл
                keyIn.write(Integer.toString(key[i]));
            overlayGamma(getGamma(key)); //наложение полученной гаммы на открытый текст
        } finally{
            out.close();
            in.close();
            keyIn.close();
        }
    }
    public static void decoding(String path) throws IOException{
        int key[] = new int[16];
        String k = "";
        try{
            keyOut = new BufferedReader(new FileReader(path));
            if((k = keyOut.readLine()) != null);
            for(int i = 0; i < k.length(); i++) //получаем ключ
                key[i] = Character.digit(k.charAt(i),10);                          
            overlayGamma(getGamma(key)); //наложение полученной гаммы на шифротекст 
        } catch (FileNotFoundException ex){            
            System.out.println("\nФайл с ключом " + path + " не найден.");
            System.exit(2);
        }  
        finally{
            keyOut.close();
            out.close();
            in.close();
        }
    }
}