package edu.columbia.cs.cs1007.roster;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Random;

/**
 * A utility class that demonstrates several ways of reading from files
 * and writing to files.
 
 @author Julia Stoyanovich, jds2109@columbia.edu.
 * COMS 1007, Summer 2009.
 *
 */
public class StreamsDemo {

  private static final int READ_AHEAD_LIMIT = 1000;

  public static void copyBinaryFile(File inFile, File outFilethrows 
                      FileNotFoundException, IOException {
    
    FileInputStream in = new FileInputStream(inFile);
    FileOutputStream out = new FileOutputStream(outFile);
    
    try {
            int c;
            // a special byte value of -1 signifies end of stream
            while ((c = in.read()) != -1) {
               out.write(c);
            }

        finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
  }

  public static String[] copyASCIIFile(File inFile, File outFilethrows 
                      FileNotFoundException, IOException {
    
    ArrayList<String> lines = new ArrayList<String>();
    FileReader in = new FileReader(inFile);
    FileWriter out = new FileWriter(outFile);
    
    try {
            int c;
            StringBuffer line=new StringBuffer();
            
            // a special byte value of -1 signifies end of stream
            while ((c = in.read()) != -1) {
              if ((charc == '\n') {
                
                lines.add(line.toString());
                line = new StringBuffer();
              else {
                line.append((char)c);
              }
            }
            
            for (String currentLine : lines) {
              out.write(currentLine + "\n");
            }

        finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    return lines.toArray(new String[0]);
  }

  public static String[] copyASCIIFileLineByLine(File inFile, File outFile)
      throws FileNotFoundException, IOException {

    ArrayList<String> lines = new ArrayList<String>();

    BufferedReader in = new BufferedReader(new FileReader(inFile));
    BufferedWriter out = new BufferedWriter(new FileWriter(outFile));

    try {

      String line;
      while ((line = in.readLine()) != null) {
        lines.add(line);
        out.write(line + "\n");
      }

    finally {
      if (in != null) {
        in.close();
      }
      if (out != null) {
        out.close();
      }
    }
    return lines.toArray(new String[0]);
  }
  
  public static void markAndReset(File inFile, File outFile, int K
        throws FileNotFoundException, IOException {
  
    // this method reads the character stream line by line, 
    // repeating the read of every Kth line.
    
    BufferedReader in = new BufferedReader(new FileReader(inFile));
    BufferedWriter out = new BufferedWriter(new FileWriter(outFile));

    try {
      
      String line;
      int lineNum=1;

      while ((line = in.readLine()) != null) {
        
        if (lineNum == K) {
          lineNum = 0;
          in.reset();
        else {
          lineNum++;
          in.mark(READ_AHEAD_LIMIT);    
        }
        out.write(line + "\n");
        out.flush();
      }

    finally {
      if (in != null) {
        in.close();
      }
      if (out != null) {
        out.close();
      }
    }
  }
  
  public static String randomUpdates(String fileName, String message
                  throws FileNotFoundException,IOException {

    RandomAccessFile file = new RandomAccessFile(fileName, "rw");
    String oldString;
    
    try {
      // compute the file length in bytes
      // assume that files are no longer than Integer.MAX_VALUE bytes, 
      // which is OK for this example
      int fileLength = (int)file.length();
      
      // transform the message into a byte representation, 
      // figure out the length
      byte[] data = message.getBytes();
      int dataLength = data.length;
      
      // randomly determine where to start writing
      Random rand = new Random();
      int start =rand.nextInt(fileLength - dataLength);
      
      // move to the start position
      file.seek(start);
  
      // read what's there right now
      byte[] oldData = new byte[dataLength];
      
      for (int i=0; i<oldData.length; i++) {
        oldData[i= file.readByte();
      }
      
      oldString = new String(oldData);
      // write the data
      file.write(data);
    
    finally {
      // important to close the file!
      file.close();      
    }
    return oldString;
  }

  public static void serializeStudent(File outFile, Student student
                  throws FileNotFoundException, IOException {
    
    ObjectOutputStream out = new ObjectOutputStream(
                  new FileOutputStream(outFile));
    out.writeObject(student);
    out.close();
  }

  public static Student deserializeStudent(File inFile)
                  throws FileNotFoundException, IOException, 
                      ClassNotFoundException {

    ObjectInputStream in = new ObjectInputStream(
        new FileInputStream(inFile));
    Student student = (Studentin.readObject();
    in.close();
    return student;
  }

  public static void serializeStudentArray(File outFile, Student student
                  throws FileNotFoundException, IOException {

    Student[] studentArr = new Student[3];
    for (int i=0; i<studentArr.length; i++) {
      studentArr[inew Student(student.getUni(), student.getName()
                      student.getGradYear(), student.getMajor());
    }
    ObjectOutputStream out = new ObjectOutputStream(
                  new FileOutputStream(outFile));
    out.writeObject(studentArr);
    out.close();
  }

  
  public static Student[] deserializeStudentArray(File inFile
                  throws FileNotFoundException, IOException, ClassNotFoundException {

    ObjectInputStream in = new ObjectInputStream(
                    new FileInputStream(inFile));
    Student[] studentArr = (Student[]) in.readObject();
    in.close();
    return studentArr;
  }
  
  public static void main(String args[]) {
    
    try {
      if (args.length < 3) {
        throw new IllegalArgumentException("Wrong number of arguments");
      }
      
      File inFile = new File(args[0]);
      File outFile = new File(args[1]);
      String mode = args[2].trim();
      
      if (mode.equalsIgnoreCase("Binary")) {
        System.out.println("Copying " + inFile.getAbsolutePath() 
                  " to " + outFile.getAbsolutePath() " in binary mode");
        
        copyBinaryFile(inFile, outFile);
      
      else if (mode.equalsIgnoreCase("ASCII")) {
        System.out.println("Copying " + inFile.getAbsolutePath() 
            " to " + outFile.getAbsolutePath() " in ASCII mode");

        copyASCIIFile(inFile, outFile);
      
      else if (mode.equalsIgnoreCase("Line")) {
        System.out.println("Copying " + inFile.getAbsolutePath() 
            " to " + outFile.getAbsolutePath() " in ASCII mode, line by line");

        copyASCIIFileLineByLine(inFile, outFile);
      
      else if (mode.equalsIgnoreCase("Repeat")) {
        int K = 2;
        System.out.println("Copying " + inFile.getAbsolutePath() 
            " to " + outFile.getAbsolutePath() " in ASCII mode " +
            " repeating every " + K + " lines.");        
        
        markAndReset(inFile, outFile, K);
        
      else if (mode.equalsIgnoreCase("Serialize")) {

        Student student = new Student("jds2109""Julia Stoyanovich"2009"CS");
        System.out.println("Serializing student " + student.toString() 
                   " to file " + outFile.getAbsolutePath());        
      
        serializeStudent(outFile, student);
        
      else if (mode.equalsIgnoreCase("Deserialize")) {
        
        Student student = deserializeStudent(inFile);
      
        System.out.println("De-serialized " + student.toString() 
                  " from file " + inFile.getAbsolutePath());
      else if (mode.equalsIgnoreCase("ArraySerialize")) {
        
        Student student = new Student("jds2109""Julia Stoyanovich"2009"CS");
        System.out.println("Serializing an array of students " + student.toString() 
               " to file " + outFile.getAbsolutePath());        
  
        serializeStudentArray(outFile,student);
      else if (mode.equalsIgnoreCase("ArrayDeserialize")) {
        
        Student[] studentArr = deserializeStudentArray(inFile);
        
        System.out.println("De-serialized an array of " + studentArr.length + 
                  " students from file " + inFile.getAbsolutePath());
        
        for (int i=0; i<studentArr.length; i++) {
          System.out.println(studentArr[i].toString());
        }
      else if (mode.equalsIgnoreCase("Random")) {
        
        String message = "**I was here**";
        String oldString = randomUpdates(outFile.getAbsolutePath(), message);

        System.out.println("Replaced \"" + oldString + "\" by \"" +  message + "\" in " 
                outFile.getAbsolutePath());
      }
    catch (IllegalArgumentException iae) {
      System.out.println(iae.getMessage());
    
    catch (FileNotFoundException fnfe) {
      System.out.println(fnfe.getMessage());
      
    catch (IOException ioe) {
      System.out.println(ioe.getMessage());      
    
    catch (ClassNotFoundException cnfe) {
      System.out.println(cnfe.getMessage());
    }
  }
}