// steganography class - Williams
import java.io.*;

public class Steg extends FilterWriter {
    protected Reader plainmessage;  // template
    protected String plainmsgfile;  // file name for template
    protected boolean newline;      // need a newline
    protected boolean encoding;     // true if hiding
    protected boolean decphase=false;  // decoding phase
    protected int decodech=0;       // decoding character
    protected int decodectr=1;     // current bit decoding
    // when phase is false, 1 space is a 0
    // and 2 spaces is a 1
    // otherwise, invert
    protected boolean phase=false;


    // This is the encoding constructor
    public Steg(String plainmsg,Writer out) throws IOException {
    super(out);
    plainmsgfile=plainmsg;
    plainmessage=new BufferedReader(new FileReader(plainmsgfile));
    encoding=true;
    }

    // This is the decoding constructor
    public Steg(Writer out) {
    super(out);
    encoding=false;
    }

    // Read a character
    // if EOF, return a space and rewind
    protected char readChar() throws IOException {
    int c;
    c=plainmessage.read();
    if (c==-1) {
        plainmessage.close();
        plainmessage=new FileReader(plainmsgfile);
        c=' ';
    }
    return (char)c;
    }

    // Read a word. Skip leading blanks and set newline
    // if you encounter a newline
    protected String readWord() throws IOException {
    StringBuffer wordBuf = new StringBuffer();
    char c;
    newline=false;
    do {
        c=readChar();
        if (c=='\n') newline=true;
    } while (Character.isWhitespace(c));  // skip lead blanks
    do {
        wordBuf.append(c);
        c=readChar();
        if (c=='\n') newline=true;
    } while (!Character.isWhitespace(c)); // read to blank
    return wordBuf.toString();
    }

    // Encrypt or decrypt according to flag
    public void write(int c) throws IOException {
    // do the "encryption"
    if (encoding) {
        String word,sep;
        boolean bit;
        for (int i=0;i<16;i++) { // unicode is 16 bits
        word=readWord();
        out.write(word);
        if ((c&(1<<i))==0) bit=false; else bit=true;
        if (phase)
            if (bit) sep=" "; else sep="  ";
        else
            if (bit) sep="  "; else sep=" ";
        phase=!phase;
        out.write(sep);
        if (newline) out.write('\n');
        }
    }
    else {
        // decoding
        if (!decphase) {
        if (c==' ') decphase=true;
        }
        else {
        if (c=='\n'||c=='\r') return; // ignore eol
        decphase=false;
        if (c==' '&&!phase || c!=' ' && phase) {
            decodech|=decodectr;
        }
        decodectr<<=1;
        phase=!phase;
        if (decodectr==0x10000) {
            out.write(decodech);
            decodectr=1;
            decodech=0;
        }

        }
    }
    }

    // This write simply delegates to the first write
    public void write(char[] cbuf,int off, int len) throws IOException {
    while (len-!=0) {
        write((int)cbuf[off++]);
    }
    }

    // This write simply delegates to the first write
    public void write(String str,int off, int len) throws IOException {
        while (len-!=0) {
        write((int)str.charAt(off++));
        }
    }

    // Support for the test main
    static void encodetest(String encfile,String inpf) throws Exception {
    FileReader inp=new FileReader(inpf);
    OutputStreamWriter writer = new OutputStreamWriter(System.out);
    Steg stegout=new Steg(encfile,writer);
    do {
        int c=inp.read();
        if (c==-1) break;
        stegout.write(c);
    } while (true);
    stegout.flush();
    }

    // Support for the test main
    static void decodetest(String fn) throws Exception {
    OutputStreamWriter writer = new OutputStreamWriter(System.out);
    Steg stegin=new Steg(writer);
    FileReader fread=new FileReader(fn);
    do {
        int c=fread.read();
        if (c==-1) break;
        stegin.write(c);
    } while (true);
    stegin.flush();
    }

    // Test main - if two arguments, encode
    // if 1 argument, decode
    // anything else prints help message
    public static void main(String args[]) throws Exception {
    if (args.length==2){
        encodetest(args[0],args[1]);
        System.exit(0);
    }
    if (args.length==1) {
        decodetest(args[0]);
        System.exit(0);
    }
    System.out.println("Usage: Steg template file " +
           "(to encode)\nSteg file (to decode");
    }

}